From e81b85dd5bdd6d42ee84f24ed4a142f6141f170e Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 1 Jan 2022 19:44:51 -0600 Subject: [PATCH 01/83] add a couple more packets --- azalea-client/src/connect.rs | 6 ++++ azalea-protocol/src/mc_buf.rs | 16 ++++++++-- .../game/clientbound_custom_payload_packet.rs | 30 +++++++++++++++++++ .../packets/game/clientbound_login_packet.rs | 17 ----------- ...clientbound_update_view_distance_packet.rs | 28 +++++++++++++++++ azalea-protocol/src/packets/game/mod.rs | 17 +++++++++-- .../login/clientbound_custom_query_packet.rs | 2 +- 7 files changed, 93 insertions(+), 23 deletions(-) create mode 100644 azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 90cb3ad7..c04126dc 100644 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -64,6 +64,12 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { GamePacket::ClientboundLoginPacket(p) => { println!("Got login packet {:?}", p); } + GamePacket::ClientboundUpdateViewDistancePacket(p) => { + println!("Got view distance packet {:?}", p); + } + GamePacket::ClientboundCustomPayloadPacket(p) => { + println!("Got custom payload packet {:?}", p); + } // GamePacket::ClientboundKeepAlivePacket(p) => { // println!("Got keep alive packet {:?}", p.keep_alive_id); // } diff --git a/azalea-protocol/src/mc_buf.rs b/azalea-protocol/src/mc_buf.rs index 538fc212..860f61f2 100644 --- a/azalea-protocol/src/mc_buf.rs +++ b/azalea-protocol/src/mc_buf.rs @@ -166,7 +166,8 @@ pub trait Readable { fn get_varint_size(&mut self, value: i32) -> u8; fn get_varlong_size(&mut self, value: i32) -> u8; async fn read_byte_array(&mut self) -> Result, String>; - async fn read_bytes(&mut self, n: usize) -> Result, String>; + async fn read_bytes_with_len(&mut self, n: usize) -> Result, String>; + async fn read_bytes(&mut self) -> Result, String>; async fn read_utf(&mut self) -> Result; async fn read_utf_with_len(&mut self, max_length: u32) -> Result; async fn read_byte(&mut self) -> Result; @@ -230,10 +231,10 @@ where async fn read_byte_array(&mut self) -> Result, String> { let length = self.read_varint().await? as usize; - Ok(self.read_bytes(length).await?) + Ok(self.read_bytes_with_len(length).await?) } - async fn read_bytes(&mut self, n: usize) -> Result, String> { + async fn read_bytes_with_len(&mut self, n: usize) -> Result, String> { let mut bytes = vec![0; n]; match AsyncReadExt::read_exact(self, &mut bytes).await { Ok(_) => Ok(bytes), @@ -241,6 +242,15 @@ where } } + async fn read_bytes(&mut self) -> Result, String> { + // read to end of the buffer + let mut bytes = vec![]; + AsyncReadExt::read_to_end(self, &mut bytes) + .await + .map_err(|_| "Error reading bytes".to_string())?; + Ok(bytes) + } + async fn read_utf(&mut self) -> Result { self.read_utf_with_len(MAX_STRING_LENGTH.into()).await } diff --git a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs new file mode 100644 index 00000000..63047801 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs @@ -0,0 +1,30 @@ +use super::GamePacket; +use crate::mc_buf::{Readable, Writable}; +use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; + +#[derive(Clone, Debug)] +pub struct ClientboundCustomPayloadPacket { + pub identifier: ResourceLocation, + pub data: Vec, +} + +impl ClientboundCustomPayloadPacket { + pub fn get(self) -> GamePacket { + GamePacket::ClientboundCustomPayloadPacket(self) + } + + pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_resource_location(&self.identifier)?; + buf.write_bytes(&self.data)?; + Ok(()) + } + + pub async fn read( + buf: &mut T, + ) -> Result { + let identifier = buf.read_resource_location().await?; + let data = buf.read_bytes().await?; + + Ok(ClientboundCustomPayloadPacket { identifier, data }.get()) + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs index 9043fa1a..0286fce4 100644 --- a/azalea-protocol/src/packets/game/clientbound_login_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_login_packet.rs @@ -4,23 +4,6 @@ use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; #[derive(Clone, Debug)] pub struct ClientboundLoginPacket { - // private final int playerId; - // private final boolean hardcore; - // private final GameType gameType; - // @Nullable - // private final GameType previousGameType; - // private final Set> levels; - // private final RegistryAccess.RegistryHolder registryHolder; - // private final DimensionType dimensionType; - // private final ResourceKey dimension; - // private final long seed; - // private final int maxPlayers; - // private final int chunkRadius; - // private final int simulationDistance; - // private final boolean reducedDebugInfo; - // private final boolean showDeathScreen; - // private final boolean isDebug; - // private final boolean isFlat; pub player_id: i32, pub hardcore: bool, pub game_type: GameType, diff --git a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs new file mode 100644 index 00000000..562f8fc2 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs @@ -0,0 +1,28 @@ +// i don't know the actual name of this packet, i couldn't find it in the source code! + +use super::GamePacket; +use crate::mc_buf::{Readable, Writable}; + +#[derive(Clone, Debug)] +pub struct ClientboundUpdateViewDistancePacket { + pub view_distance: i32, +} + +impl ClientboundUpdateViewDistancePacket { + pub fn get(self) -> GamePacket { + GamePacket::ClientboundUpdateViewDistancePacket(self) + } + + pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(self.view_distance)?; + Ok(()) + } + + pub async fn read( + buf: &mut T, + ) -> Result { + let view_distance = buf.read_varint().await?; + + Ok(ClientboundUpdateViewDistancePacket { view_distance }.get()) + } +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 00fa1d75..ab5ca7e8 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -1,4 +1,6 @@ +pub mod clientbound_custom_payload_packet; pub mod clientbound_login_packet; +pub mod clientbound_update_view_distance_packet; use super::ProtocolPacket; use crate::connect::PacketFlow; @@ -10,13 +12,21 @@ where Self: Sized, { ClientboundLoginPacket(clientbound_login_packet::ClientboundLoginPacket), + ClientboundUpdateViewDistancePacket( + clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, + ), + ClientboundCustomPayloadPacket( + clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, + ), } #[async_trait] impl ProtocolPacket for GamePacket { fn id(&self) -> u32 { match self { - GamePacket::ClientboundLoginPacket(_packet) => 0x00, + GamePacket::ClientboundCustomPayloadPacket(_packet) => 0x18, + GamePacket::ClientboundLoginPacket(_packet) => 0x26, + GamePacket::ClientboundUpdateViewDistancePacket(_packet) => 0x4a, } } @@ -33,8 +43,11 @@ impl ProtocolPacket for GamePacket { { Ok(match flow { PacketFlow::ServerToClient => match id { + 0x18 => clientbound_custom_payload_packet::ClientboundCustomPayloadPacket::read(buf).await?, 0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?, - + 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket + ::read(buf) + .await?, _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), }, PacketFlow::ClientToServer => match id { diff --git a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs index 2bc1fc1e..048fa53f 100644 --- a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs @@ -26,7 +26,7 @@ impl ClientboundCustomQueryPacket { ) -> Result { let transaction_id = buf.read_varint().await? as u32; let identifier = ResourceLocation::new(&buf.read_utf().await?)?; - let data = buf.read_bytes(1048576).await?; + let data = buf.read_bytes_with_len(1048576).await?; Ok(ClientboundCustomQueryPacket { transaction_id, identifier, From a1afbb6031527c1db5831fc8e916bc0ecce633b4 Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 1 Jan 2022 23:55:19 -0600 Subject: [PATCH 02/83] start adding packet macros --- Cargo.lock | 26 ++- azalea-protocol/Cargo.toml | 3 + azalea-protocol/packet-macros/Cargo.toml | 14 ++ azalea-protocol/packet-macros/src/lib.rs | 164 ++++++++++++++++++ azalea-protocol/src/mc_buf.rs | 14 +- .../game/clientbound_custom_payload_packet.rs | 28 +-- ...clientbound_update_view_distance_packet.rs | 23 +-- azalea-protocol/src/packets/game/mod.rs | 7 +- .../handshake/client_intention_packet.rs | 60 ++++--- azalea-protocol/src/packets/handshake/mod.rs | 2 +- .../login/clientbound_custom_query_packet.rs | 3 +- .../login/clientbound_game_profile_packet.rs | 3 +- .../packets/login/clientbound_hello_packet.rs | 2 +- .../clientbound_login_compression_packet.rs | 3 +- azalea-protocol/src/packets/login/mod.rs | 2 +- .../packets/login/serverbound_hello_packet.rs | 3 +- azalea-protocol/src/packets/mod.rs | 8 +- .../clientbound_status_response_packet.rs | 4 +- azalea-protocol/src/packets/status/mod.rs | 2 +- .../serverbound_status_request_packet.rs | 2 +- 20 files changed, 285 insertions(+), 88 deletions(-) create mode 100644 azalea-protocol/packet-macros/Cargo.toml create mode 100644 azalea-protocol/packet-macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c037630d..eca4d4fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,9 @@ dependencies = [ "azalea-nbt", "byteorder", "bytes", + "num-derive", + "num-traits", + "packet-macros", "serde", "serde_json", "thiserror", @@ -172,6 +175,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +[[package]] +name = "casey" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe85130dda9cf267715582ce6cf1ab581c8dfe3cb33f7065fee0f14e3fea14" +dependencies = [ + "syn", +] + [[package]] name = "cast" version = "0.2.7" @@ -676,6 +688,16 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "packet-macros" +version = "0.1.0" +dependencies = [ + "casey", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -755,9 +777,9 @@ checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index c9883195..ff3bd9d4 100644 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -15,6 +15,9 @@ azalea-core = {path = "../azalea-core"} azalea-nbt = {path = "../azalea-nbt"} byteorder = "^1.4.3" bytes = "^1.1.0" +num-derive = "^0.3.3" +num-traits = "^0.2.14" +packet-macros = {path = "./packet-macros"} serde = {version = "1.0.130", features = ["serde_derive"]} serde_json = "^1.0.72" thiserror = "^1.0.30" diff --git a/azalea-protocol/packet-macros/Cargo.toml b/azalea-protocol/packet-macros/Cargo.toml new file mode 100644 index 00000000..5a301756 --- /dev/null +++ b/azalea-protocol/packet-macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +edition = "2021" +name = "packet-macros" +version = "0.1.0" + +[lib] +proc-macro = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +casey = "^0.3.3" +proc-macro2 = "^1.0.36" +quote = "^1.0.10" +syn = "^1.0.82" diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs new file mode 100644 index 00000000..470ac4c1 --- /dev/null +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -0,0 +1,164 @@ +use quote::{quote, quote_spanned, ToTokens}; +use syn::{self, parse_macro_input, spanned::Spanned, DeriveInput, FieldsNamed}; + +fn as_packet_derive( + input: proc_macro::TokenStream, + state: proc_macro2::TokenStream, +) -> proc_macro::TokenStream { + let DeriveInput { ident, data, .. } = parse_macro_input!(input); + + let fields = match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => fields, + _ => panic!("#[derive(*Packet)] can only be used on structs"), + }; + let FieldsNamed { named, .. } = match fields { + syn::Fields::Named(f) => f, + _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"), + }; + + let write_fields = named + .iter() + .map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(syn::TypePath { path, .. }) => { + if path.is_ident("String") { + quote! { buf.write_utf(&self.#field_name)?; } + } else if path.is_ident("ResourceLocation") { + quote! { buf.write_resource_location(&self.#field_name)?; } + // i don't know how to do this in a way that isn't terrible + } else if path.to_token_stream().to_string() == "Vec < u8 >" { + quote! { buf.write_bytes(&self.#field_name)?; } + } else if path.is_ident("i32") { + // only treat it as a varint if it has the varint attribute + if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { + quote! { buf.write_varint(self.#field_name)?; } + } else { + quote! { buf.write_i32(self.#field_name)?; } + } + } else if path.is_ident("u32") { + if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { + quote! { buf.write_varint(self.#field_name as i32)?; } + } else { + quote! { buf.write_u32(self.#field_name)?; } + } + } else if path.is_ident("u16") { + quote! { buf.write_short(self.#field_name as i16)?; } + } else if path.is_ident("ConnectionProtocol") { + quote! { buf.write_varint(self.#field_name.clone() as i32)?; } + } else { + panic!( + "#[derive(*Packet)] doesn't know how to write {}", + path.to_token_stream() + ); + } + } + _ => panic!( + "Error writing field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::>(); + + let read_fields = named + .iter() + .map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(syn::TypePath { path, .. }) => { + if path.is_ident("String") { + quote! { let #field_name = buf.read_utf().await?; } + } else if path.is_ident("ResourceLocation") { + quote! { let #field_name = buf.read_resource_location().await?; } + // i don't know how to do this in a way that isn't terrible + } else if path.to_token_stream().to_string() == "Vec < u8 >" { + quote! { let #field_name = buf.read_bytes().await?; } + } else if path.is_ident("i32") { + // only treat it as a varint if it has the varint attribute + if f.attrs.iter().any(|a| a.path.is_ident("varint")) { + quote! { let #field_name = buf.read_varint().await?; } + } else { + quote! { let #field_name = buf.read_i32().await?; } + } + } else if path.is_ident("u32") { + if f.attrs.iter().any(|a| a.path.is_ident("varint")) { + quote! { let #field_name = buf.read_varint().await? as u32; } + } else { + quote! { let #field_name = buf.read_u32().await?; } + } + } else if path.is_ident("u16") { + quote! { let #field_name = buf.read_short().await? as u16; } + } else if path.is_ident("ConnectionProtocol") { + quote! { + let #field_name = ConnectionProtocol::from_i32(buf.read_varint().await?) + .ok_or_else(|| "Invalid intention".to_string())?; + } + } else { + panic!( + "#[derive(*Packet)] doesn't know how to read {}", + path.to_token_stream() + ); + } + } + _ => panic!( + "Error reading field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::>(); + let read_field_names = named.iter().map(|f| &f.ident).collect::>(); + + let gen = quote! { + impl #ident { + pub fn get(self) -> #state { + #state::#ident(self) + } + + pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + #(#write_fields)* + Ok(()) + } + + pub async fn read( + buf: &mut T, + ) -> Result<#state, String> { + #(#read_fields)* + Ok(#ident { + #(#read_field_names: #read_field_names),* + }.get()) + } + } + }; + + gen.into() +} + +#[proc_macro_derive(GamePacket, attributes(varint))] +pub fn derive_game_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + as_packet_derive(input, quote! {crate::packets::game::GamePacket}) +} + +#[proc_macro_derive(HandshakePacket, attributes(varint))] +pub fn derive_handshake_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + as_packet_derive(input, quote! {crate::packets::handshake::HandshakePacket}) +} + +#[proc_macro_derive(LoginPacket, attributes(varint))] +pub fn derive_login_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + as_packet_derive(input, quote! {crate::packets::login::LoginPacket}) +} + +#[proc_macro_derive(StatusPacket, attributes(varint))] +pub fn derive_status_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + as_packet_derive(input, quote! {crate::packets::status::StatusPacket}) +} diff --git a/azalea-protocol/src/mc_buf.rs b/azalea-protocol/src/mc_buf.rs index 860f61f2..72583d5a 100644 --- a/azalea-protocol/src/mc_buf.rs +++ b/azalea-protocol/src/mc_buf.rs @@ -35,7 +35,7 @@ pub trait Writable { fn write_varint(&mut self, value: i32) -> Result<(), std::io::Error>; fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error>; fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error>; - fn write_short(&mut self, n: u16) -> Result<(), std::io::Error>; + fn write_short(&mut self, n: i16) -> Result<(), std::io::Error>; fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; fn write_int(&mut self, n: i32) -> Result<(), std::io::Error>; fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error>; @@ -125,8 +125,8 @@ impl Writable for Vec { self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) } - fn write_short(&mut self, n: u16) -> Result<(), std::io::Error> { - WriteBytesExt::write_u16::(self, n) + fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> { + WriteBytesExt::write_i16::(self, n) } fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { @@ -176,6 +176,7 @@ pub trait Readable { async fn read_nbt(&mut self) -> Result; async fn read_long(&mut self) -> Result; async fn read_resource_location(&mut self) -> Result; + async fn read_short(&mut self) -> Result; } #[async_trait] @@ -334,6 +335,13 @@ where let location = ResourceLocation::new(&location_string)?; Ok(location) } + + async fn read_short(&mut self) -> Result { + match AsyncReadExt::read_i16(self).await { + Ok(r) => Ok(r), + Err(_) => Err("Error reading short".to_string()), + } + } } #[cfg(test)] diff --git a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs index 63047801..24220a83 100644 --- a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs @@ -1,30 +1,10 @@ -use super::GamePacket; use crate::mc_buf::{Readable, Writable}; -use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; +use crate::packets::game::GamePacket; +use azalea_core::resource_location::ResourceLocation; +use packet_macros::GamePacket; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, GamePacket)] pub struct ClientboundCustomPayloadPacket { pub identifier: ResourceLocation, pub data: Vec, } - -impl ClientboundCustomPayloadPacket { - pub fn get(self) -> GamePacket { - GamePacket::ClientboundCustomPayloadPacket(self) - } - - pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_resource_location(&self.identifier)?; - buf.write_bytes(&self.data)?; - Ok(()) - } - - pub async fn read( - buf: &mut T, - ) -> Result { - let identifier = buf.read_resource_location().await?; - let data = buf.read_bytes().await?; - - Ok(ClientboundCustomPayloadPacket { identifier, data }.get()) - } -} diff --git a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs index 562f8fc2..f6028e6c 100644 --- a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs @@ -2,27 +2,10 @@ use super::GamePacket; use crate::mc_buf::{Readable, Writable}; +use packet_macros::GamePacket; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, GamePacket)] pub struct ClientboundUpdateViewDistancePacket { + #[varint] pub view_distance: i32, } - -impl ClientboundUpdateViewDistancePacket { - pub fn get(self) -> GamePacket { - GamePacket::ClientboundUpdateViewDistancePacket(self) - } - - pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_varint(self.view_distance)?; - Ok(()) - } - - pub async fn read( - buf: &mut T, - ) -> Result { - let view_distance = buf.read_varint().await?; - - Ok(ClientboundUpdateViewDistancePacket { view_distance }.get()) - } -} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index ab5ca7e8..43b3ca3d 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -30,7 +30,9 @@ impl ProtocolPacket for GamePacket { } } - fn write(&self, _buf: &mut Vec) {} + fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + Ok(()) + } /// Read a packet by its id, ConnectionProtocol, and flow async fn read( @@ -48,7 +50,8 @@ impl ProtocolPacket for GamePacket { 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket ::read(buf) .await?, - _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), + // _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), + _ => panic!("Unknown ServerToClient game packet id: {}", id), }, PacketFlow::ClientToServer => match id { // 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs index 939a695e..b3eb8301 100644 --- a/azalea-protocol/src/packets/handshake/client_intention_packet.rs +++ b/azalea-protocol/src/packets/handshake/client_intention_packet.rs @@ -1,34 +1,48 @@ +use crate::{ + mc_buf::{Readable, Writable}, + packets::ConnectionProtocol, +}; +use num_traits::FromPrimitive; +use packet_macros::HandshakePacket; use std::hash::Hash; -use crate::{mc_buf::Writable, packets::ConnectionProtocol}; - -use super::HandshakePacket; - -#[derive(Hash, Clone, Debug)] +#[derive(Hash, Clone, Debug, HandshakePacket)] pub struct ClientIntentionPacket { + #[varint] pub protocol_version: u32, pub hostname: String, pub port: u16, - /// 1 for status, 2 for login pub intention: ConnectionProtocol, } -impl ClientIntentionPacket { - pub fn get(self) -> HandshakePacket { - HandshakePacket::ClientIntentionPacket(self) - } +// impl ClientIntentionPacket { +// pub fn get(self) -> HandshakePacket { +// HandshakePacket::ClientIntentionPacket(self) +// } - pub fn write(&self, buf: &mut Vec) { - buf.write_varint(self.protocol_version as i32).unwrap(); - buf.write_utf(&self.hostname).unwrap(); - buf.write_short(self.port).unwrap(); - buf.write_varint(self.intention.clone() as i32).unwrap(); - } +// pub fn write(&self, buf: &mut Vec) { +// buf.write_varint(self.protocol_version as i32).unwrap(); +// buf.write_utf(&self.hostname).unwrap(); +// buf.write_short(self.port).unwrap(); +// buf.write_varint(self.intention.clone() as i32).unwrap(); +// } - pub async fn read( - _buf: &mut T, - ) -> Result { - Err("ClientIntentionPacket::parse not implemented".to_string()) - // Ok(ClientIntentionPacket {}.get()) - } -} +// pub async fn read( +// buf: &mut T, +// ) -> Result { +// let protocol_version = buf.read_varint().await? as u32; +// let hostname = buf.read_utf().await?; +// let port = buf.read_short().await? as u16; +// let intention = buf.read_varint().await?; + +// Ok(HandshakePacket::ClientIntentionPacket( +// ClientIntentionPacket { +// protocol_version, +// hostname, +// port, +// intention: ConnectionProtocol::from_i32(intention) +// .ok_or_else(|| "Invalid intention".to_string())?, +// }, +// )) +// } +// } diff --git a/azalea-protocol/src/packets/handshake/mod.rs b/azalea-protocol/src/packets/handshake/mod.rs index 1d753645..17465fca 100644 --- a/azalea-protocol/src/packets/handshake/mod.rs +++ b/azalea-protocol/src/packets/handshake/mod.rs @@ -22,7 +22,7 @@ impl ProtocolPacket for HandshakePacket { } } - fn write(&self, buf: &mut Vec) { + fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { match self { HandshakePacket::ClientIntentionPacket(packet) => packet.write(buf), } diff --git a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs index 048fa53f..22e58b0d 100644 --- a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs @@ -15,10 +15,11 @@ impl ClientboundCustomQueryPacket { LoginPacket::ClientboundCustomQueryPacket(self) } - pub fn write(&self, buf: &mut Vec) { + pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { buf.write_varint(self.transaction_id as i32).unwrap(); buf.write_utf(self.identifier.to_string().as_str()).unwrap(); buf.write_bytes(&self.data).unwrap(); + Ok(()) } pub async fn read( diff --git a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs index 1a752c1a..c54aa819 100644 --- a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs @@ -14,11 +14,12 @@ impl ClientboundGameProfilePacket { LoginPacket::ClientboundGameProfilePacket(self) } - pub fn write(&self, buf: &mut Vec) { + pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { for n in self.game_profile.uuid.to_int_array() { buf.write_int(n as i32).unwrap(); } buf.write_utf(self.game_profile.name.as_str()).unwrap(); + Ok(()) } pub async fn read( diff --git a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs index e0b865be..9d0cec39 100644 --- a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs @@ -16,7 +16,7 @@ impl ClientboundHelloPacket { LoginPacket::ClientboundHelloPacket(self) } - pub fn write(&self, _buf: &mut Vec) { + pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { panic!("ClientboundHelloPacket::write not implemented") } diff --git a/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs index af355192..a88c6cbf 100644 --- a/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs @@ -14,8 +14,9 @@ impl ClientboundLoginCompressionPacket { LoginPacket::ClientboundLoginCompressionPacket(self) } - pub fn write(&self, buf: &mut Vec) { + pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { buf.write_varint(self.compression_threshold).unwrap(); + Ok(()) } pub async fn read( diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs index 4d490d08..b1f61746 100644 --- a/azalea-protocol/src/packets/login/mod.rs +++ b/azalea-protocol/src/packets/login/mod.rs @@ -34,7 +34,7 @@ impl ProtocolPacket for LoginPacket { } } - fn write(&self, buf: &mut Vec) { + fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { match self { LoginPacket::ClientboundCustomQueryPacket(packet) => packet.write(buf), LoginPacket::ClientboundGameProfilePacket(packet) => packet.write(buf), diff --git a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs index 0039cbce..a72480f2 100644 --- a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs +++ b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs @@ -14,8 +14,9 @@ impl ServerboundHelloPacket { LoginPacket::ServerboundHelloPacket(self) } - pub fn write(&self, buf: &mut Vec) { + pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { buf.write_utf(&self.username).unwrap(); + Ok(()) } pub async fn read( diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs index e065b65c..0f1cd2f0 100644 --- a/azalea-protocol/src/packets/mod.rs +++ b/azalea-protocol/src/packets/mod.rs @@ -3,13 +3,13 @@ pub mod handshake; pub mod login; pub mod status; -use async_trait::async_trait; - use crate::connect::PacketFlow; +use async_trait::async_trait; +use num_derive::FromPrimitive; pub const PROTOCOL_VERSION: u32 = 757; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, FromPrimitive)] pub enum ConnectionProtocol { Handshake = -1, Game = 0, @@ -42,5 +42,5 @@ where where Self: Sized; - fn write(&self, buf: &mut Vec); + fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error>; } diff --git a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs index 38270ad1..58f5b701 100644 --- a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs +++ b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs @@ -39,7 +39,9 @@ impl ClientboundStatusResponsePacket { StatusPacket::ClientboundStatusResponsePacket(Box::new(self)) } - pub fn write(&self, _buf: &mut Vec) {} + pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { + Ok(()) + } pub async fn read( buf: &mut T, diff --git a/azalea-protocol/src/packets/status/mod.rs b/azalea-protocol/src/packets/status/mod.rs index 6383bae8..31fedfb9 100644 --- a/azalea-protocol/src/packets/status/mod.rs +++ b/azalea-protocol/src/packets/status/mod.rs @@ -29,7 +29,7 @@ impl ProtocolPacket for StatusPacket { } } - fn write(&self, buf: &mut Vec) { + fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { match self { StatusPacket::ServerboundStatusRequestPacket(packet) => packet.write(buf), StatusPacket::ClientboundStatusResponsePacket(packet) => packet.write(buf), diff --git a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs index 3a25ac42..af98f7cb 100644 --- a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs +++ b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs @@ -10,7 +10,7 @@ impl ServerboundStatusRequestPacket { StatusPacket::ServerboundStatusRequestPacket(self) } - pub fn write(&self, _buf: &mut Vec) { + pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { panic!("ServerboundStatusRequestPacket::write not implemented") } From 3ec7352da2c8a9870718e6a81913f6aa1b576b95 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 2 Jan 2022 00:03:04 -0600 Subject: [PATCH 03/83] add macro for a couple more packets --- azalea-protocol/packet-macros/src/lib.rs | 4 +-- .../game/clientbound_custom_payload_packet.rs | 1 - ...clientbound_update_view_distance_packet.rs | 1 - .../handshake/client_intention_packet.rs | 32 ------------------- .../login/clientbound_custom_query_packet.rs | 32 ++----------------- 5 files changed, 5 insertions(+), 65 deletions(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 470ac4c1..11878abf 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -1,5 +1,5 @@ -use quote::{quote, quote_spanned, ToTokens}; -use syn::{self, parse_macro_input, spanned::Spanned, DeriveInput, FieldsNamed}; +use quote::{quote, ToTokens}; +use syn::{self, parse_macro_input, DeriveInput, FieldsNamed}; fn as_packet_derive( input: proc_macro::TokenStream, diff --git a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs index 24220a83..4ee0ddf0 100644 --- a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs @@ -1,5 +1,4 @@ use crate::mc_buf::{Readable, Writable}; -use crate::packets::game::GamePacket; use azalea_core::resource_location::ResourceLocation; use packet_macros::GamePacket; diff --git a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs index f6028e6c..cd48b304 100644 --- a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs @@ -1,6 +1,5 @@ // i don't know the actual name of this packet, i couldn't find it in the source code! -use super::GamePacket; use crate::mc_buf::{Readable, Writable}; use packet_macros::GamePacket; diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs index b3eb8301..00d124a4 100644 --- a/azalea-protocol/src/packets/handshake/client_intention_packet.rs +++ b/azalea-protocol/src/packets/handshake/client_intention_packet.rs @@ -14,35 +14,3 @@ pub struct ClientIntentionPacket { pub port: u16, pub intention: ConnectionProtocol, } - -// impl ClientIntentionPacket { -// pub fn get(self) -> HandshakePacket { -// HandshakePacket::ClientIntentionPacket(self) -// } - -// pub fn write(&self, buf: &mut Vec) { -// buf.write_varint(self.protocol_version as i32).unwrap(); -// buf.write_utf(&self.hostname).unwrap(); -// buf.write_short(self.port).unwrap(); -// buf.write_varint(self.intention.clone() as i32).unwrap(); -// } - -// pub async fn read( -// buf: &mut T, -// ) -> Result { -// let protocol_version = buf.read_varint().await? as u32; -// let hostname = buf.read_utf().await?; -// let port = buf.read_short().await? as u16; -// let intention = buf.read_varint().await?; - -// Ok(HandshakePacket::ClientIntentionPacket( -// ClientIntentionPacket { -// protocol_version, -// hostname, -// port, -// intention: ConnectionProtocol::from_i32(intention) -// .ok_or_else(|| "Invalid intention".to_string())?, -// }, -// )) -// } -// } diff --git a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs index 22e58b0d..77262c43 100644 --- a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs @@ -1,38 +1,12 @@ -use super::LoginPacket; use crate::mc_buf::{Readable, Writable}; use azalea_core::resource_location::ResourceLocation; +use packet_macros::LoginPacket; use std::hash::Hash; -#[derive(Hash, Clone, Debug)] +#[derive(Hash, Clone, Debug, LoginPacket)] pub struct ClientboundCustomQueryPacket { + #[varint] pub transaction_id: u32, pub identifier: ResourceLocation, pub data: Vec, } - -impl ClientboundCustomQueryPacket { - pub fn get(self) -> LoginPacket { - LoginPacket::ClientboundCustomQueryPacket(self) - } - - pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_varint(self.transaction_id as i32).unwrap(); - buf.write_utf(self.identifier.to_string().as_str()).unwrap(); - buf.write_bytes(&self.data).unwrap(); - Ok(()) - } - - pub async fn read( - buf: &mut T, - ) -> Result { - let transaction_id = buf.read_varint().await? as u32; - let identifier = ResourceLocation::new(&buf.read_utf().await?)?; - let data = buf.read_bytes_with_len(1048576).await?; - Ok(ClientboundCustomQueryPacket { - transaction_id, - identifier, - data, - } - .get()) - } -} From bb57273f48294355d7ac863c291d80878f711c16 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 2 Jan 2022 17:03:34 -0600 Subject: [PATCH 04/83] start improving packet macros --- azalea-protocol/packet-macros/src/lib.rs | 42 +- azalea-protocol/src/mc_buf.rs | 496 ----------------------- azalea-protocol/src/mc_buf/mod.rs | 163 ++++++++ azalea-protocol/src/mc_buf/read.rs | 230 +++++++++++ azalea-protocol/src/mc_buf/write.rs | 180 ++++++++ 5 files changed, 592 insertions(+), 519 deletions(-) delete mode 100644 azalea-protocol/src/mc_buf.rs create mode 100644 azalea-protocol/src/mc_buf/mod.rs create mode 100644 azalea-protocol/src/mc_buf/read.rs create mode 100644 azalea-protocol/src/mc_buf/write.rs diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 11878abf..22c39ea9 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -32,13 +32,6 @@ fn as_packet_derive( // i don't know how to do this in a way that isn't terrible } else if path.to_token_stream().to_string() == "Vec < u8 >" { quote! { buf.write_bytes(&self.#field_name)?; } - } else if path.is_ident("i32") { - // only treat it as a varint if it has the varint attribute - if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { - quote! { buf.write_varint(self.#field_name)?; } - } else { - quote! { buf.write_i32(self.#field_name)?; } - } } else if path.is_ident("u32") { if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { quote! { buf.write_varint(self.#field_name as i32)?; } @@ -50,10 +43,16 @@ fn as_packet_derive( } else if path.is_ident("ConnectionProtocol") { quote! { buf.write_varint(self.#field_name.clone() as i32)?; } } else { - panic!( - "#[derive(*Packet)] doesn't know how to write {}", - path.to_token_stream() - ); + if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { + quote! { + crate::mc_buf::McBufVarintWritable::varint_write_into(&self.#field_name, buf)?; + } + } else { + quote! { + crate::mc_buf::McBufVarintWritable::write_into(&self.#field_name, buf)?; + } + } + } } _ => panic!( @@ -78,16 +77,8 @@ fn as_packet_derive( quote! { let #field_name = buf.read_utf().await?; } } else if path.is_ident("ResourceLocation") { quote! { let #field_name = buf.read_resource_location().await?; } - // i don't know how to do this in a way that isn't terrible } else if path.to_token_stream().to_string() == "Vec < u8 >" { quote! { let #field_name = buf.read_bytes().await?; } - } else if path.is_ident("i32") { - // only treat it as a varint if it has the varint attribute - if f.attrs.iter().any(|a| a.path.is_ident("varint")) { - quote! { let #field_name = buf.read_varint().await?; } - } else { - quote! { let #field_name = buf.read_i32().await?; } - } } else if path.is_ident("u32") { if f.attrs.iter().any(|a| a.path.is_ident("varint")) { quote! { let #field_name = buf.read_varint().await? as u32; } @@ -102,10 +93,15 @@ fn as_packet_derive( .ok_or_else(|| "Invalid intention".to_string())?; } } else { - panic!( - "#[derive(*Packet)] doesn't know how to read {}", - path.to_token_stream() - ); + if f.attrs.iter().any(|a| a.path.is_ident("varint")) { + quote! { + let #field_name = crate::mc_buf::McBufVarintReadable::varint_read_into(buf).await?; + } + } else { + quote! { + let #field_name = crate::mc_buf::McBufReadable::read_into(buf).await?; + } + } } } _ => panic!( diff --git a/azalea-protocol/src/mc_buf.rs b/azalea-protocol/src/mc_buf.rs deleted file mode 100644 index 72583d5a..00000000 --- a/azalea-protocol/src/mc_buf.rs +++ /dev/null @@ -1,496 +0,0 @@ -//! Utilities for reading and writing for the Minecraft protocol - -use std::io::Write; - -use async_trait::async_trait; -use azalea_core::resource_location::ResourceLocation; -use byteorder::{BigEndian, WriteBytesExt}; -use tokio::io::{AsyncRead, AsyncReadExt}; - -// const DEFAULT_NBT_QUOTA: u32 = 2097152; -const MAX_STRING_LENGTH: u16 = 32767; -// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; - -#[async_trait] -pub trait Writable { - fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> - where - F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, - T: Sized, - Self: Sized; - fn write_int_id_list(&mut self, list: Vec) -> Result<(), std::io::Error>; - fn write_map( - &mut self, - map: Vec<(KT, VT)>, - key_writer: KF, - value_writer: VF, - ) -> Result<(), std::io::Error> - where - KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, - VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, - Self: Sized; - - fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error>; - fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; - fn write_varint(&mut self, value: i32) -> Result<(), std::io::Error>; - fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error>; - fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error>; - fn write_short(&mut self, n: i16) -> Result<(), std::io::Error>; - fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; - fn write_int(&mut self, n: i32) -> Result<(), std::io::Error>; - fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error>; - fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error>; - fn write_long(&mut self, n: i64) -> Result<(), std::io::Error>; - fn write_resource_location( - &mut self, - location: &ResourceLocation, - ) -> Result<(), std::io::Error>; -} - -#[async_trait] -impl Writable for Vec { - fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> - where - F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, - Self: Sized, - { - self.write_varint(list.len() as i32)?; - for item in list { - writer(self, item)?; - } - Ok(()) - } - - fn write_int_id_list(&mut self, list: Vec) -> Result<(), std::io::Error> { - self.write_list(&list, |buf, n| buf.write_varint(*n)) - } - - fn write_map( - &mut self, - map: Vec<(KT, VT)>, - key_writer: KF, - value_writer: VF, - ) -> Result<(), std::io::Error> - where - KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, - VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, - Self: Sized, - { - self.write_varint(map.len() as i32)?; - for (key, value) in map { - key_writer(self, key)?; - value_writer(self, value)?; - } - Ok(()) - } - - fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> { - WriteBytesExt::write_u8(self, n) - } - - fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - self.extend_from_slice(bytes); - Ok(()) - } - - fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { - let mut buffer = [0]; - if value == 0 { - self.write_all(&buffer).unwrap(); - } - while value != 0 { - buffer[0] = (value & 0b0111_1111) as u8; - value = (value >> 7) & (i32::max_value() >> 6); - if value != 0 { - buffer[0] |= 0b1000_0000; - } - self.write_all(&buffer)?; - } - Ok(()) - } - - fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> { - if string.len() > len { - panic!( - "String too big (was {} bytes encoded, max {})", - string.len(), - len - ); - } - self.write_varint(string.len() as i32)?; - self.write_bytes(string.as_bytes()) - } - - fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> { - self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) - } - - fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> { - WriteBytesExt::write_i16::(self, n) - } - - fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - self.write_varint(bytes.len() as i32)?; - self.write_bytes(bytes) - } - - fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> { - WriteBytesExt::write_i32::(self, n) - } - - fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> { - self.write_byte(if b { 1 } else { 0 }) - } - - fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error> { - nbt.write(self) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) - } - - fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> { - WriteBytesExt::write_i64::(self, n) - } - - fn write_resource_location( - &mut self, - location: &ResourceLocation, - ) -> Result<(), std::io::Error> { - self.write_utf(&location.to_string()) - } -} - -#[async_trait] -pub trait Readable { - async fn read_int_id_list(&mut self) -> Result, String>; - async fn read_varint(&mut self) -> Result; - fn get_varint_size(&mut self, value: i32) -> u8; - fn get_varlong_size(&mut self, value: i32) -> u8; - async fn read_byte_array(&mut self) -> Result, String>; - async fn read_bytes_with_len(&mut self, n: usize) -> Result, String>; - async fn read_bytes(&mut self) -> Result, String>; - async fn read_utf(&mut self) -> Result; - async fn read_utf_with_len(&mut self, max_length: u32) -> Result; - async fn read_byte(&mut self) -> Result; - async fn read_int(&mut self) -> Result; - async fn read_boolean(&mut self) -> Result; - async fn read_nbt(&mut self) -> Result; - async fn read_long(&mut self) -> Result; - async fn read_resource_location(&mut self) -> Result; - async fn read_short(&mut self) -> Result; -} - -#[async_trait] -impl Readable for R -where - R: AsyncRead + std::marker::Unpin + std::marker::Send, -{ - async fn read_int_id_list(&mut self) -> Result, String> { - let len = self.read_varint().await?; - let mut list = Vec::with_capacity(len as usize); - for _ in 0..len { - list.push(self.read_varint().await?); - } - Ok(list) - } - - // fast varints stolen from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 - /// Read a single varint from the reader and return the value, along with the number of bytes read - async fn read_varint(&mut self) -> Result { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..4 { - self.read_exact(&mut buffer) - .await - .map_err(|_| "Invalid VarInt".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); - if buffer[0] & 0b1000_0000 == 0 { - return Ok(ans); - } - } - Ok(ans) - } - - fn get_varint_size(&mut self, value: i32) -> u8 { - for i in 1..5 { - if (value & -1 << (i * 7)) != 0 { - continue; - } - return i; - } - 5 - } - - fn get_varlong_size(&mut self, value: i32) -> u8 { - for i in 1..10 { - if (value & -1 << (i * 7)) != 0 { - continue; - } - return i; - } - 10 - } - - async fn read_byte_array(&mut self) -> Result, String> { - let length = self.read_varint().await? as usize; - Ok(self.read_bytes_with_len(length).await?) - } - - async fn read_bytes_with_len(&mut self, n: usize) -> Result, String> { - let mut bytes = vec![0; n]; - match AsyncReadExt::read_exact(self, &mut bytes).await { - Ok(_) => Ok(bytes), - Err(_) => Err("Error reading bytes".to_string()), - } - } - - async fn read_bytes(&mut self) -> Result, String> { - // read to end of the buffer - let mut bytes = vec![]; - AsyncReadExt::read_to_end(self, &mut bytes) - .await - .map_err(|_| "Error reading bytes".to_string())?; - Ok(bytes) - } - - async fn read_utf(&mut self) -> Result { - self.read_utf_with_len(MAX_STRING_LENGTH.into()).await - } - - async fn read_utf_with_len(&mut self, max_length: u32) -> Result { - let length = self.read_varint().await?; - // i don't know why it's multiplied by 4 but it's like that in mojang's code so - if length < 0 { - return Err( - "The received encoded string buffer length is less than zero! Weird string!" - .to_string(), - ); - } - if length as u32 > max_length * 4 { - return Err(format!( - "The received encoded string buffer length is longer than maximum allowed ({} > {})", - length, - max_length * 4 - )); - } - - // this is probably quite inefficient, idk how to do it better - let mut string = String::new(); - let mut buffer = vec![0; length as usize]; - 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!( - "The received string length is longer than maximum allowed ({} > {})", - length, max_length - )); - } - - Ok(string) - } - - /// Read a single byte from the reader - async fn read_byte(&mut self) -> Result { - match AsyncReadExt::read_u8(self).await { - Ok(r) => Ok(r), - Err(_) => Err("Error reading byte".to_string()), - } - } - - async fn read_int(&mut self) -> Result { - match AsyncReadExt::read_i32(self).await { - Ok(r) => Ok(r), - Err(_) => Err("Error reading int".to_string()), - } - } - - async fn read_boolean(&mut self) -> Result { - match self.read_byte().await { - Ok(0) => Ok(false), - Ok(1) => Ok(true), - _ => Err("Error reading boolean".to_string()), - } - } - - async fn read_nbt(&mut self) -> Result { - match azalea_nbt::Tag::read(self).await { - Ok(r) => Ok(r), - // Err(e) => Err(e.to_string()), - Err(e) => Err(e.to_string()).unwrap(), - } - } - - async fn read_long(&mut self) -> Result { - match AsyncReadExt::read_i64(self).await { - Ok(r) => Ok(r), - Err(_) => Err("Error reading long".to_string()), - } - } - - async fn read_resource_location(&mut self) -> Result { - // get the resource location from the string - let location_string = self.read_utf().await?; - let location = ResourceLocation::new(&location_string)?; - Ok(location) - } - - async fn read_short(&mut self) -> Result { - match AsyncReadExt::read_i16(self).await { - Ok(r) => Ok(r), - Err(_) => Err("Error reading short".to_string()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::{collections::HashMap, io::Cursor}; - use tokio::io::BufReader; - - #[test] - fn test_write_varint() { - let mut buf = Vec::new(); - buf.write_varint(123456).unwrap(); - assert_eq!(buf, vec![192, 196, 7]); - - let mut buf = Vec::new(); - buf.write_varint(0).unwrap(); - assert_eq!(buf, vec![0]); - } - - #[tokio::test] - async fn test_read_varint() { - let mut buf = BufReader::new(Cursor::new(vec![192, 196, 7])); - assert_eq!(buf.read_varint().await.unwrap(), 123456); - assert_eq!(buf.get_varint_size(123456), 3); - - let mut buf = BufReader::new(Cursor::new(vec![0])); - assert_eq!(buf.read_varint().await.unwrap(), 0); - assert_eq!(buf.get_varint_size(0), 1); - - let mut buf = BufReader::new(Cursor::new(vec![1])); - assert_eq!(buf.read_varint().await.unwrap(), 1); - assert_eq!(buf.get_varint_size(1), 1); - } - - #[tokio::test] - async fn test_read_varint_longer() { - let mut buf = BufReader::new(Cursor::new(vec![138, 56, 0, 135, 56, 123])); - assert_eq!(buf.read_varint().await.unwrap(), 7178); - } - - #[tokio::test] - async fn test_list() { - let mut buf = Vec::new(); - buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s)) - .unwrap(); - - // there's no read_list because idk how to do it in rust - let mut buf = BufReader::new(Cursor::new(buf)); - - let mut result = Vec::new(); - let length = buf.read_varint().await.unwrap(); - for _ in 0..length { - result.push(buf.read_utf().await.unwrap()); - } - - assert_eq!(result, vec!["a", "bc", "def"]); - } - - #[tokio::test] - async fn test_int_id_list() { - let mut buf = Vec::new(); - buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i)) - .unwrap(); - - let mut buf = BufReader::new(Cursor::new(buf)); - - let result = buf.read_int_id_list().await.unwrap(); - assert_eq!(result, vec![1, 2, 3]); - } - - #[tokio::test] - async fn test_map() { - let mut buf = Vec::new(); - buf.write_map( - vec![("a", 1), ("bc", 23), ("def", 456)], - Vec::write_utf, - Vec::write_varint, - ) - .unwrap(); - - let mut buf = BufReader::new(Cursor::new(buf)); - - let mut result = Vec::new(); - let length = buf.read_varint().await.unwrap(); - for _ in 0..length { - result.push(( - buf.read_utf().await.unwrap(), - buf.read_varint().await.unwrap(), - )); - } - - assert_eq!( - result, - vec![ - ("a".to_string(), 1), - ("bc".to_string(), 23), - ("def".to_string(), 456) - ] - ); - } - - #[tokio::test] - async fn test_nbt() { - let mut buf = Vec::new(); - buf.write_nbt(&azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "hello world".to_string(), - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "name".to_string(), - azalea_nbt::Tag::String("Bananrama".to_string()), - )])), - )]))) - .unwrap(); - - let mut buf = BufReader::new(Cursor::new(buf)); - - let result = buf.read_nbt().await.unwrap(); - assert_eq!( - result, - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "hello world".to_string(), - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "name".to_string(), - azalea_nbt::Tag::String("Bananrama".to_string()), - )])), - )])) - ); - } - - #[tokio::test] - async fn test_long() { - let mut buf = Vec::new(); - buf.write_long(123456).unwrap(); - - let mut buf = BufReader::new(Cursor::new(buf)); - - assert_eq!(buf.read_long().await.unwrap(), 123456); - } - - #[tokio::test] - async fn test_resource_location() { - let mut buf = Vec::new(); - buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap()) - .unwrap(); - - let mut buf = BufReader::new(Cursor::new(buf)); - - assert_eq!( - buf.read_resource_location().await.unwrap(), - ResourceLocation::new("minecraft:dirt").unwrap() - ); - } -} diff --git a/azalea-protocol/src/mc_buf/mod.rs b/azalea-protocol/src/mc_buf/mod.rs new file mode 100644 index 00000000..a924431e --- /dev/null +++ b/azalea-protocol/src/mc_buf/mod.rs @@ -0,0 +1,163 @@ +//! Utilities for reading and writing for the Minecraft protocol + +mod read; +mod write; + +pub use read::{McBufReadable, McBufVarintReadable, Readable}; +pub use write::{McBufVarintWritable, McBufWritable, Writable}; + +// const DEFAULT_NBT_QUOTA: u32 = 2097152; +const MAX_STRING_LENGTH: u16 = 32767; +// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; + +#[cfg(test)] +mod tests { + use super::*; + use azalea_core::resource_location::ResourceLocation; + use std::{collections::HashMap, io::Cursor}; + use tokio::io::BufReader; + + #[test] + fn test_write_varint() { + let mut buf = Vec::new(); + buf.write_varint(123456).unwrap(); + assert_eq!(buf, vec![192, 196, 7]); + + let mut buf = Vec::new(); + buf.write_varint(0).unwrap(); + assert_eq!(buf, vec![0]); + } + + #[tokio::test] + async fn test_read_varint() { + let mut buf = BufReader::new(Cursor::new(vec![192, 196, 7])); + assert_eq!(buf.read_varint().await.unwrap(), 123456); + assert_eq!(buf.get_varint_size(123456), 3); + + let mut buf = BufReader::new(Cursor::new(vec![0])); + assert_eq!(buf.read_varint().await.unwrap(), 0); + assert_eq!(buf.get_varint_size(0), 1); + + let mut buf = BufReader::new(Cursor::new(vec![1])); + assert_eq!(buf.read_varint().await.unwrap(), 1); + assert_eq!(buf.get_varint_size(1), 1); + } + + #[tokio::test] + async fn test_read_varint_longer() { + let mut buf = BufReader::new(Cursor::new(vec![138, 56, 0, 135, 56, 123])); + assert_eq!(buf.read_varint().await.unwrap(), 7178); + } + + #[tokio::test] + async fn test_list() { + let mut buf = Vec::new(); + buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s)) + .unwrap(); + + // there's no read_list because idk how to do it in rust + let mut buf = BufReader::new(Cursor::new(buf)); + + let mut result = Vec::new(); + let length = buf.read_varint().await.unwrap(); + for _ in 0..length { + result.push(buf.read_utf().await.unwrap()); + } + + assert_eq!(result, vec!["a", "bc", "def"]); + } + + #[tokio::test] + async fn test_int_id_list() { + let mut buf = Vec::new(); + buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i)) + .unwrap(); + + let mut buf = BufReader::new(Cursor::new(buf)); + + let result = buf.read_int_id_list().await.unwrap(); + assert_eq!(result, vec![1, 2, 3]); + } + + #[tokio::test] + async fn test_map() { + let mut buf = Vec::new(); + buf.write_map( + vec![("a", 1), ("bc", 23), ("def", 456)], + Vec::write_utf, + Vec::write_varint, + ) + .unwrap(); + + let mut buf = BufReader::new(Cursor::new(buf)); + + let mut result = Vec::new(); + let length = buf.read_varint().await.unwrap(); + for _ in 0..length { + result.push(( + buf.read_utf().await.unwrap(), + buf.read_varint().await.unwrap(), + )); + } + + assert_eq!( + result, + vec![ + ("a".to_string(), 1), + ("bc".to_string(), 23), + ("def".to_string(), 456) + ] + ); + } + + #[tokio::test] + async fn test_nbt() { + let mut buf = Vec::new(); + buf.write_nbt(&azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( + "hello world".to_string(), + azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( + "name".to_string(), + azalea_nbt::Tag::String("Bananrama".to_string()), + )])), + )]))) + .unwrap(); + + let mut buf = BufReader::new(Cursor::new(buf)); + + let result = buf.read_nbt().await.unwrap(); + assert_eq!( + result, + azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( + "hello world".to_string(), + azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( + "name".to_string(), + azalea_nbt::Tag::String("Bananrama".to_string()), + )])), + )])) + ); + } + + #[tokio::test] + async fn test_long() { + let mut buf = Vec::new(); + buf.write_long(123456).unwrap(); + + let mut buf = BufReader::new(Cursor::new(buf)); + + assert_eq!(buf.read_long().await.unwrap(), 123456); + } + + #[tokio::test] + async fn test_resource_location() { + let mut buf = Vec::new(); + buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap()) + .unwrap(); + + let mut buf = BufReader::new(Cursor::new(buf)); + + assert_eq!( + buf.read_resource_location().await.unwrap(), + ResourceLocation::new("minecraft:dirt").unwrap() + ); + } +} diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs new file mode 100644 index 00000000..c429eb7f --- /dev/null +++ b/azalea-protocol/src/mc_buf/read.rs @@ -0,0 +1,230 @@ +use async_trait::async_trait; +use azalea_core::resource_location::ResourceLocation; +use tokio::io::{AsyncRead, AsyncReadExt}; + +use super::MAX_STRING_LENGTH; + +#[async_trait] +pub trait Readable { + async fn read_int_id_list(&mut self) -> Result, String>; + async fn read_varint(&mut self) -> Result; + fn get_varint_size(&mut self, value: i32) -> u8; + fn get_varlong_size(&mut self, value: i32) -> u8; + async fn read_byte_array(&mut self) -> Result, String>; + async fn read_bytes_with_len(&mut self, n: usize) -> Result, String>; + async fn read_bytes(&mut self) -> Result, String>; + async fn read_utf(&mut self) -> Result; + async fn read_utf_with_len(&mut self, max_length: u32) -> Result; + async fn read_byte(&mut self) -> Result; + async fn read_int(&mut self) -> Result; + async fn read_boolean(&mut self) -> Result; + async fn read_nbt(&mut self) -> Result; + async fn read_long(&mut self) -> Result; + async fn read_resource_location(&mut self) -> Result; + async fn read_short(&mut self) -> Result; +} + +#[async_trait] +impl Readable for R +where + R: AsyncRead + std::marker::Unpin + std::marker::Send, +{ + async fn read_int_id_list(&mut self) -> Result, String> { + let len = self.read_varint().await?; + let mut list = Vec::with_capacity(len as usize); + for _ in 0..len { + list.push(self.read_varint().await?); + } + Ok(list) + } + + // fast varints stolen from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 + /// Read a single varint from the reader and return the value, along with the number of bytes read + async fn read_varint(&mut self) -> Result { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..4 { + self.read_exact(&mut buffer) + .await + .map_err(|_| "Invalid VarInt".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); + if buffer[0] & 0b1000_0000 == 0 { + return Ok(ans); + } + } + Ok(ans) + } + + fn get_varint_size(&mut self, value: i32) -> u8 { + for i in 1..5 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 5 + } + + fn get_varlong_size(&mut self, value: i32) -> u8 { + for i in 1..10 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 10 + } + + async fn read_byte_array(&mut self) -> Result, String> { + let length = self.read_varint().await? as usize; + Ok(self.read_bytes_with_len(length).await?) + } + + async fn read_bytes_with_len(&mut self, n: usize) -> Result, String> { + let mut bytes = vec![0; n]; + match AsyncReadExt::read_exact(self, &mut bytes).await { + Ok(_) => Ok(bytes), + Err(_) => Err("Error reading bytes".to_string()), + } + } + + async fn read_bytes(&mut self) -> Result, String> { + // read to end of the buffer + let mut bytes = vec![]; + AsyncReadExt::read_to_end(self, &mut bytes) + .await + .map_err(|_| "Error reading bytes".to_string())?; + Ok(bytes) + } + + async fn read_utf(&mut self) -> Result { + self.read_utf_with_len(MAX_STRING_LENGTH.into()).await + } + + async fn read_utf_with_len(&mut self, max_length: u32) -> Result { + let length = self.read_varint().await?; + // i don't know why it's multiplied by 4 but it's like that in mojang's code so + if length < 0 { + return Err( + "The received encoded string buffer length is less than zero! Weird string!" + .to_string(), + ); + } + if length as u32 > max_length * 4 { + return Err(format!( + "The received encoded string buffer length is longer than maximum allowed ({} > {})", + length, + max_length * 4 + )); + } + + // this is probably quite inefficient, idk how to do it better + let mut string = String::new(); + let mut buffer = vec![0; length as usize]; + 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!( + "The received string length is longer than maximum allowed ({} > {})", + length, max_length + )); + } + + Ok(string) + } + + /// Read a single byte from the reader + async fn read_byte(&mut self) -> Result { + match AsyncReadExt::read_u8(self).await { + Ok(r) => Ok(r), + Err(_) => Err("Error reading byte".to_string()), + } + } + + async fn read_int(&mut self) -> Result { + match AsyncReadExt::read_i32(self).await { + Ok(r) => Ok(r), + Err(_) => Err("Error reading int".to_string()), + } + } + + async fn read_boolean(&mut self) -> Result { + match self.read_byte().await { + Ok(0) => Ok(false), + Ok(1) => Ok(true), + _ => Err("Error reading boolean".to_string()), + } + } + + async fn read_nbt(&mut self) -> Result { + match azalea_nbt::Tag::read(self).await { + Ok(r) => Ok(r), + // Err(e) => Err(e.to_string()), + Err(e) => Err(e.to_string()).unwrap(), + } + } + + async fn read_long(&mut self) -> Result { + match AsyncReadExt::read_i64(self).await { + Ok(r) => Ok(r), + Err(_) => Err("Error reading long".to_string()), + } + } + + async fn read_resource_location(&mut self) -> Result { + // get the resource location from the string + let location_string = self.read_utf().await?; + let location = ResourceLocation::new(&location_string)?; + Ok(location) + } + + async fn read_short(&mut self) -> Result { + match AsyncReadExt::read_i16(self).await { + Ok(r) => Ok(r), + Err(_) => Err("Error reading short".to_string()), + } + } +} + +#[async_trait] +pub trait McBufReadable +where + Self: Sized, +{ + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send; +} + +#[async_trait] +pub trait McBufVarintReadable +where + Self: Sized, +{ + async fn varint_read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send; +} + +#[async_trait] +impl McBufReadable for i32 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_int().await + } +} + +#[async_trait] +impl McBufVarintReadable for i32 { + async fn varint_read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_varint().await + } +} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs new file mode 100644 index 00000000..3c3375b9 --- /dev/null +++ b/azalea-protocol/src/mc_buf/write.rs @@ -0,0 +1,180 @@ +use async_trait::async_trait; +use azalea_core::resource_location::ResourceLocation; +use byteorder::{BigEndian, WriteBytesExt}; +use std::io::Write; + +use super::MAX_STRING_LENGTH; + +#[async_trait] +pub trait Writable { + fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> + where + F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, + T: Sized, + Self: Sized; + fn write_int_id_list(&mut self, list: Vec) -> Result<(), std::io::Error>; + fn write_map( + &mut self, + map: Vec<(KT, VT)>, + key_writer: KF, + value_writer: VF, + ) -> Result<(), std::io::Error> + where + KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, + VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, + Self: Sized; + + fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error>; + fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; + fn write_varint(&mut self, value: i32) -> Result<(), std::io::Error>; + fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error>; + fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error>; + fn write_short(&mut self, n: i16) -> Result<(), std::io::Error>; + fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; + fn write_int(&mut self, n: i32) -> Result<(), std::io::Error>; + fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error>; + fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error>; + fn write_long(&mut self, n: i64) -> Result<(), std::io::Error>; + fn write_resource_location( + &mut self, + location: &ResourceLocation, + ) -> Result<(), std::io::Error>; +} + +#[async_trait] +impl Writable for Vec { + fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> + where + F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, + Self: Sized, + { + self.write_varint(list.len() as i32)?; + for item in list { + writer(self, item)?; + } + Ok(()) + } + + fn write_int_id_list(&mut self, list: Vec) -> Result<(), std::io::Error> { + self.write_list(&list, |buf, n| buf.write_varint(*n)) + } + + fn write_map( + &mut self, + map: Vec<(KT, VT)>, + key_writer: KF, + value_writer: VF, + ) -> Result<(), std::io::Error> + where + KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, + VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, + Self: Sized, + { + self.write_varint(map.len() as i32)?; + for (key, value) in map { + key_writer(self, key)?; + value_writer(self, value)?; + } + Ok(()) + } + + fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> { + WriteBytesExt::write_u8(self, n) + } + + fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { + self.extend_from_slice(bytes); + Ok(()) + } + + fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { + let mut buffer = [0]; + if value == 0 { + self.write_all(&buffer).unwrap(); + } + while value != 0 { + buffer[0] = (value & 0b0111_1111) as u8; + value = (value >> 7) & (i32::max_value() >> 6); + if value != 0 { + buffer[0] |= 0b1000_0000; + } + self.write_all(&buffer)?; + } + Ok(()) + } + + fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> { + if string.len() > len { + panic!( + "String too big (was {} bytes encoded, max {})", + string.len(), + len + ); + } + self.write_varint(string.len() as i32)?; + self.write_bytes(string.as_bytes()) + } + + fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> { + self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) + } + + fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> { + WriteBytesExt::write_i16::(self, n) + } + + fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { + self.write_varint(bytes.len() as i32)?; + self.write_bytes(bytes) + } + + fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> { + WriteBytesExt::write_i32::(self, n) + } + + fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> { + self.write_byte(if b { 1 } else { 0 }) + } + + fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error> { + nbt.write(self) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + } + + fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> { + WriteBytesExt::write_i64::(self, n) + } + + fn write_resource_location( + &mut self, + location: &ResourceLocation, + ) -> Result<(), std::io::Error> { + self.write_utf(&location.to_string()) + } +} + +pub trait McBufWritable +where + Self: Sized, +{ + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error>; +} + +pub trait McBufVarintWritable +where + Self: Sized, +{ + fn varint_write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error>; +} + +impl McBufWritable for i32 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + Writable::write_int(buf, *self) + } +} + +impl McBufVarintWritable for i32 { + fn varint_write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(*self) + } +} From bb566aa54131a23b6d9e605c81a8ff4d1d1c21d7 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 2 Jan 2022 17:07:01 -0600 Subject: [PATCH 05/83] implement for Vec --- azalea-protocol/packet-macros/src/lib.rs | 7 +------ azalea-protocol/src/mc_buf/read.rs | 10 ++++++++++ azalea-protocol/src/mc_buf/write.rs | 6 ++++++ .../game/clientbound_update_view_distance_packet.rs | 3 +-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 22c39ea9..5b2088a1 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -29,9 +29,6 @@ fn as_packet_derive( quote! { buf.write_utf(&self.#field_name)?; } } else if path.is_ident("ResourceLocation") { quote! { buf.write_resource_location(&self.#field_name)?; } - // i don't know how to do this in a way that isn't terrible - } else if path.to_token_stream().to_string() == "Vec < u8 >" { - quote! { buf.write_bytes(&self.#field_name)?; } } else if path.is_ident("u32") { if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { quote! { buf.write_varint(self.#field_name as i32)?; } @@ -49,7 +46,7 @@ fn as_packet_derive( } } else { quote! { - crate::mc_buf::McBufVarintWritable::write_into(&self.#field_name, buf)?; + crate::mc_buf::McBufWritable::write_into(&self.#field_name, buf)?; } } @@ -77,8 +74,6 @@ fn as_packet_derive( quote! { let #field_name = buf.read_utf().await?; } } else if path.is_ident("ResourceLocation") { quote! { let #field_name = buf.read_resource_location().await?; } - } else if path.to_token_stream().to_string() == "Vec < u8 >" { - quote! { let #field_name = buf.read_bytes().await?; } } else if path.is_ident("u32") { if f.attrs.iter().any(|a| a.path.is_ident("varint")) { quote! { let #field_name = buf.read_varint().await? as u32; } diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index c429eb7f..683c7d9a 100644 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -228,3 +228,13 @@ impl McBufVarintReadable for i32 { buf.read_varint().await } } + +#[async_trait] +impl McBufReadable for Vec { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_bytes().await + } +} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index 3c3375b9..7b1ac861 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -178,3 +178,9 @@ impl McBufVarintWritable for i32 { buf.write_varint(*self) } } + +impl McBufWritable for Vec { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_bytes(self) + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs index cd48b304..8301c089 100644 --- a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs @@ -1,6 +1,5 @@ -// i don't know the actual name of this packet, i couldn't find it in the source code! +// i don't know the actual name of this packet, i couldn't find it in the source code -use crate::mc_buf::{Readable, Writable}; use packet_macros::GamePacket; #[derive(Clone, Debug, GamePacket)] From 094c210dada7c0ee83c19965739312d2d00e4099 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 2 Jan 2022 17:19:04 -0600 Subject: [PATCH 06/83] switch all current macro types to the new system --- azalea-protocol/packet-macros/src/lib.rs | 46 ++--------------- azalea-protocol/src/mc_buf/read.rs | 66 ++++++++++++++++++++++++ azalea-protocol/src/mc_buf/write.rs | 42 +++++++++++++++ azalea-protocol/src/packets/mod.rs | 24 ++++++++- 4 files changed, 136 insertions(+), 42 deletions(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 5b2088a1..ba963111 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -25,31 +25,14 @@ fn as_packet_derive( // if it's a string, use buf.write_string match field_type { syn::Type::Path(syn::TypePath { path, .. }) => { - if path.is_ident("String") { - quote! { buf.write_utf(&self.#field_name)?; } - } else if path.is_ident("ResourceLocation") { - quote! { buf.write_resource_location(&self.#field_name)?; } - } else if path.is_ident("u32") { - if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { - quote! { buf.write_varint(self.#field_name as i32)?; } - } else { - quote! { buf.write_u32(self.#field_name)?; } + if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { + quote! { + crate::mc_buf::McBufVarintWritable::varint_write_into(&self.#field_name, buf)?; } - } else if path.is_ident("u16") { - quote! { buf.write_short(self.#field_name as i16)?; } - } else if path.is_ident("ConnectionProtocol") { - quote! { buf.write_varint(self.#field_name.clone() as i32)?; } } else { - if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { - quote! { - crate::mc_buf::McBufVarintWritable::varint_write_into(&self.#field_name, buf)?; - } - } else { - quote! { - crate::mc_buf::McBufWritable::write_into(&self.#field_name, buf)?; - } + quote! { + crate::mc_buf::McBufWritable::write_into(&self.#field_name, buf)?; } - } } _ => panic!( @@ -70,24 +53,6 @@ fn as_packet_derive( // if it's a string, use buf.write_string match field_type { syn::Type::Path(syn::TypePath { path, .. }) => { - if path.is_ident("String") { - quote! { let #field_name = buf.read_utf().await?; } - } else if path.is_ident("ResourceLocation") { - quote! { let #field_name = buf.read_resource_location().await?; } - } else if path.is_ident("u32") { - if f.attrs.iter().any(|a| a.path.is_ident("varint")) { - quote! { let #field_name = buf.read_varint().await? as u32; } - } else { - quote! { let #field_name = buf.read_u32().await?; } - } - } else if path.is_ident("u16") { - quote! { let #field_name = buf.read_short().await? as u16; } - } else if path.is_ident("ConnectionProtocol") { - quote! { - let #field_name = ConnectionProtocol::from_i32(buf.read_varint().await?) - .ok_or_else(|| "Invalid intention".to_string())?; - } - } else { if f.attrs.iter().any(|a| a.path.is_ident("varint")) { quote! { let #field_name = crate::mc_buf::McBufVarintReadable::varint_read_into(buf).await?; @@ -96,7 +61,6 @@ fn as_packet_derive( quote! { let #field_name = crate::mc_buf::McBufReadable::read_into(buf).await?; } - } } } _ => panic!( diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index 683c7d9a..374e5443 100644 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -238,3 +238,69 @@ impl McBufReadable for Vec { buf.read_bytes().await } } + +// string +#[async_trait] +impl McBufReadable for String { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_utf().await + } +} + +// ResourceLocation +#[async_trait] +impl McBufReadable for ResourceLocation { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_resource_location().await + } +} + +// u32 +#[async_trait] +impl McBufReadable for u32 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_int().await.map(|i| i as u32) + } +} + +// u32 varint +#[async_trait] +impl McBufVarintReadable for u32 { + async fn varint_read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_varint().await.map(|i| i as u32) + } +} + +// u16 +#[async_trait] +impl McBufReadable for u16 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_short().await.map(|i| i as u16) + } +} + +// u16 varint +#[async_trait] +impl McBufVarintReadable for u16 { + async fn varint_read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_varint().await.map(|i| i as u16) + } +} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index 7b1ac861..f22b218a 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -184,3 +184,45 @@ impl McBufWritable for Vec { buf.write_bytes(self) } } + +// string +impl McBufWritable for String { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_utf(self) + } +} + +// ResourceLocation +impl McBufWritable for ResourceLocation { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_resource_location(self) + } +} + +// u32 +impl McBufWritable for u32 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(*self as i32) + } +} + +// u32 varint +impl McBufVarintWritable for u32 { + fn varint_write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(*self as i32) + } +} + +// u16 +impl McBufWritable for u16 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(*self as i32) + } +} + +// u16 varint +impl McBufVarintWritable for u16 { + fn varint_write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(*self as i32) + } +} diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs index 0f1cd2f0..f35451c6 100644 --- a/azalea-protocol/src/packets/mod.rs +++ b/azalea-protocol/src/packets/mod.rs @@ -3,9 +3,14 @@ pub mod handshake; pub mod login; pub mod status; -use crate::connect::PacketFlow; +use crate::{ + connect::PacketFlow, + mc_buf::{McBufReadable, McBufWritable, Readable, Writable}, +}; use async_trait::async_trait; use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +use tokio::io::AsyncRead; pub const PROTOCOL_VERSION: u32 = 757; @@ -44,3 +49,20 @@ where fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error>; } + +#[async_trait] +impl McBufReadable for ConnectionProtocol { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + ConnectionProtocol::from_i32(buf.read_varint().await?) + .ok_or_else(|| "Invalid intention".to_string()) + } +} + +impl McBufWritable for ConnectionProtocol { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(self.clone() as i32) + } +} From 45871fc01e212a50ac5e6268e4677f97f8fe5bb3 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 2 Jan 2022 17:40:18 -0600 Subject: [PATCH 07/83] better parsing for entire login packet --- azalea-protocol/src/mc_buf/read.rs | 84 ++++++++++++++++- azalea-protocol/src/mc_buf/write.rs | 68 ++++++++++++-- .../packets/game/clientbound_login_packet.rs | 90 ++----------------- 3 files changed, 151 insertions(+), 91 deletions(-) diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index 374e5443..5127860e 100644 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use azalea_core::resource_location::ResourceLocation; +use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; use tokio::io::{AsyncRead, AsyncReadExt}; use super::MAX_STRING_LENGTH; @@ -304,3 +304,85 @@ impl McBufVarintReadable for u16 { buf.read_varint().await.map(|i| i as u16) } } + +// i64 +#[async_trait] +impl McBufReadable for i64 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_long().await + } +} + +// u64 +#[async_trait] +impl McBufReadable for u64 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + i64::read_into(buf).await.map(|i| i as u64) + } +} + +// bool +#[async_trait] +impl McBufReadable for bool { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_boolean().await + } +} + +// GameType +#[async_trait] +impl McBufReadable for GameType { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + GameType::from_id(buf.read_byte().await?) + } +} + +// Option +#[async_trait] +impl McBufReadable for Option { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + GameType::from_optional_id(buf.read_byte().await? as i8) + } +} + +// Vec +#[async_trait] +impl McBufReadable for Vec { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let mut vec = Vec::new(); + let length = buf.read_varint().await?; + for _ in 0..length { + vec.push(buf.read_resource_location().await?); + } + Ok(vec) + } +} + +// azalea_nbt::Tag +#[async_trait] +impl McBufReadable for azalea_nbt::Tag { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_nbt().await + } +} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index f22b218a..14dac9d1 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use azalea_core::resource_location::ResourceLocation; +use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; use byteorder::{BigEndian, WriteBytesExt}; use std::io::Write; @@ -202,27 +202,85 @@ impl McBufWritable for ResourceLocation { // u32 impl McBufWritable for u32 { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_varint(*self as i32) + i32::varint_write_into(&(*self as i32), buf) } } // u32 varint impl McBufVarintWritable for u32 { fn varint_write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_varint(*self as i32) + i32::varint_write_into(&(*self as i32), buf) } } // u16 impl McBufWritable for u16 { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_varint(*self as i32) + i32::varint_write_into(&(*self as i32), buf) } } // u16 varint impl McBufVarintWritable for u16 { fn varint_write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_varint(*self as i32) + i32::varint_write_into(&(*self as i32), buf) + } +} + +// u8 +impl McBufWritable for u8 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_byte(*self) + } +} + +// i64 +impl McBufWritable for i64 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + Writable::write_long(buf, *self) + } +} + +// u64 +impl McBufWritable for u64 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + i64::write_into(&(*self as i64), buf) + } +} + +// bool +impl McBufWritable for bool { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_boolean(*self) + } +} + +// GameType +impl McBufWritable for GameType { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + u8::write_into(&self.to_id(), buf) + } +} + +// Option +impl McBufWritable for Option { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_byte(GameType::to_optional_id(&self) as u8) + } +} + +// Vec +impl McBufWritable for Vec { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_list(&self, |buf, resource_location| { + buf.write_resource_location(resource_location) + }) + } +} + +// azalea_nbt::Tag +impl McBufWritable for azalea_nbt::Tag { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_nbt(self) } } diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs index 0286fce4..57869202 100644 --- a/azalea-protocol/src/packets/game/clientbound_login_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_login_packet.rs @@ -1,8 +1,7 @@ -use super::GamePacket; -use crate::mc_buf::{Readable, Writable}; use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; +use packet_macros::GamePacket; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, GamePacket)] pub struct ClientboundLoginPacket { pub player_id: i32, pub hardcore: bool, @@ -13,93 +12,14 @@ pub struct ClientboundLoginPacket { pub dimension_type: azalea_nbt::Tag, pub dimension: ResourceLocation, pub seed: i64, + #[varint] pub max_players: i32, + #[varint] pub chunk_radius: i32, + #[varint] pub simulation_distance: i32, pub reduced_debug_info: bool, pub show_death_screen: bool, pub is_debug: bool, pub is_flat: bool, } - -impl ClientboundLoginPacket { - pub fn get(self) -> GamePacket { - GamePacket::ClientboundLoginPacket(self) - } - - pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_int(self.player_id)?; - buf.write_boolean(self.hardcore)?; - buf.write_byte(self.game_type.to_id())?; - buf.write_byte(GameType::to_optional_id(&self.previous_game_type) as u8)?; - buf.write_list(&self.levels, |buf, resource_location| { - buf.write_resource_location(resource_location) - })?; - buf.write_nbt(&self.registry_holder)?; - buf.write_nbt(&self.dimension_type)?; - buf.write_resource_location(&self.dimension)?; - buf.write_long(self.seed)?; - buf.write_varint(self.max_players)?; - buf.write_varint(self.chunk_radius)?; - buf.write_varint(self.simulation_distance)?; - buf.write_boolean(self.reduced_debug_info)?; - buf.write_boolean(self.show_death_screen)?; - buf.write_boolean(self.is_debug)?; - buf.write_boolean(self.is_flat)?; - Ok(()) - } - - pub async fn read( - buf: &mut T, - ) -> Result { - let player_id = buf.read_int().await?; - let hardcore = buf.read_boolean().await?; - let game_type = GameType::from_id(buf.read_byte().await?)?; - let previous_game_type = GameType::from_optional_id(buf.read_byte().await? as i8)?; - - let mut levels = Vec::new(); - let length = buf.read_varint().await?; - for _ in 0..length { - levels.push(buf.read_resource_location().await?); - } - - // println!("about to read nbt"); - // // read all the bytes into a buffer, print it, and panic - // let mut registry_holder_buf = Vec::new(); - // buf.read_to_end(&mut registry_holder_buf).await.unwrap(); - // println!("{:?}", String::from_utf8_lossy(®istry_holder_buf)); - // panic!(""); - - let registry_holder = buf.read_nbt().await?; - let dimension_type = buf.read_nbt().await?; - let dimension = buf.read_resource_location().await?; - let seed = buf.read_long().await?; - let max_players = buf.read_varint().await?; - let chunk_radius = buf.read_varint().await?; - let simulation_distance = buf.read_varint().await?; - let reduced_debug_info = buf.read_boolean().await?; - let show_death_screen = buf.read_boolean().await?; - let is_debug = buf.read_boolean().await?; - let is_flat = buf.read_boolean().await?; - - Ok(ClientboundLoginPacket { - player_id, - hardcore, - game_type, - previous_game_type, - levels, - registry_holder, - dimension_type, - dimension, - seed, - max_players, - chunk_radius, - simulation_distance, - reduced_debug_info, - show_death_screen, - is_debug, - is_flat, - } - .get()) - } -} From 394f996df27bedc68be6c1f9e9764e8f78ba6282 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 2 Jan 2022 17:42:41 -0600 Subject: [PATCH 08/83] fix random warnings --- azalea-client/src/connect.rs | 7 ------- azalea-core/src/game_type.rs | 2 -- azalea-protocol/packet-macros/src/lib.rs | 4 ++-- azalea-protocol/src/mc_buf/write.rs | 4 ++-- .../src/packets/game/clientbound_custom_payload_packet.rs | 1 - .../src/packets/handshake/client_intention_packet.rs | 6 +----- .../src/packets/login/clientbound_custom_query_packet.rs | 1 - azalea-protocol/src/write.rs | 2 +- 8 files changed, 6 insertions(+), 21 deletions(-) diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index c04126dc..6ed25bff 100644 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -70,13 +70,6 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { GamePacket::ClientboundCustomPayloadPacket(p) => { println!("Got custom payload packet {:?}", p); } - // GamePacket::ClientboundKeepAlivePacket(p) => { - // println!("Got keep alive packet {:?}", p.keep_alive_id); - // } - // GamePacket::ClientboundChatMessagePacket(p) => { - // println!("Got chat message packet {:?}", p.message); - // } - _ => panic!("unhandled packet"), }, Err(e) => { println!("Error: {:?}", e); diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs index b6ff479d..f5b9fb38 100644 --- a/azalea-core/src/game_type.rs +++ b/azalea-core/src/game_type.rs @@ -1,5 +1,3 @@ -use azalea_chat; - #[derive(Hash, Clone, Debug)] pub enum GameType { SURVIVAL, diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index ba963111..957515c4 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -24,7 +24,7 @@ fn as_packet_derive( // do a different buf.write_* for each field depending on the type // if it's a string, use buf.write_string match field_type { - syn::Type::Path(syn::TypePath { path, .. }) => { + syn::Type::Path(_) => { if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) { quote! { crate::mc_buf::McBufVarintWritable::varint_write_into(&self.#field_name, buf)?; @@ -52,7 +52,7 @@ fn as_packet_derive( // do a different buf.write_* for each field depending on the type // if it's a string, use buf.write_string match field_type { - syn::Type::Path(syn::TypePath { path, .. }) => { + syn::Type::Path(_) => { if f.attrs.iter().any(|a| a.path.is_ident("varint")) { quote! { let #field_name = crate::mc_buf::McBufVarintReadable::varint_read_into(buf).await?; diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index 14dac9d1..6fbe6eab 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -265,14 +265,14 @@ impl McBufWritable for GameType { // Option impl McBufWritable for Option { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_byte(GameType::to_optional_id(&self) as u8) + buf.write_byte(GameType::to_optional_id(self) as u8) } } // Vec impl McBufWritable for Vec { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_list(&self, |buf, resource_location| { + buf.write_list(self, |buf, resource_location| { buf.write_resource_location(resource_location) }) } diff --git a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs index 4ee0ddf0..134a3109 100644 --- a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs @@ -1,4 +1,3 @@ -use crate::mc_buf::{Readable, Writable}; use azalea_core::resource_location::ResourceLocation; use packet_macros::GamePacket; diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs index 00d124a4..6216ddc4 100644 --- a/azalea-protocol/src/packets/handshake/client_intention_packet.rs +++ b/azalea-protocol/src/packets/handshake/client_intention_packet.rs @@ -1,8 +1,4 @@ -use crate::{ - mc_buf::{Readable, Writable}, - packets::ConnectionProtocol, -}; -use num_traits::FromPrimitive; +use crate::packets::ConnectionProtocol; use packet_macros::HandshakePacket; use std::hash::Hash; diff --git a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs index 77262c43..3138106e 100644 --- a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs @@ -1,4 +1,3 @@ -use crate::mc_buf::{Readable, Writable}; use azalea_core::resource_location::ResourceLocation; use packet_macros::LoginPacket; use std::hash::Hash; diff --git a/azalea-protocol/src/write.rs b/azalea-protocol/src/write.rs index 3898e74a..821ed6e8 100644 --- a/azalea-protocol/src/write.rs +++ b/azalea-protocol/src/write.rs @@ -17,7 +17,7 @@ fn packet_encoder(packet: &P) -> Result MAXIMUM_UNCOMPRESSED_LENGTH as usize { return Err(format!( "Packet too big (is {} bytes, should be less than {}): {:?}", From 96eba2b39a596dd19c29a93aaa3b5bb9b700ba62 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 3 Jan 2022 00:14:41 -0600 Subject: [PATCH 09/83] difficulty packet --- azalea-client/src/connect.rs | 3 + azalea-core/src/difficulty.rs | 96 +++++++++++++++++++ azalea-core/src/lib.rs | 10 +- azalea-protocol/src/mc_buf/read.rs | 38 +++++++- azalea-protocol/src/mc_buf/write.rs | 19 +++- .../clientbound_change_difficulty_packet.rs | 8 ++ azalea-protocol/src/packets/game/mod.rs | 15 ++- 7 files changed, 177 insertions(+), 12 deletions(-) create mode 100644 azalea-core/src/difficulty.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 6ed25bff..7b1da525 100644 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -70,6 +70,9 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { GamePacket::ClientboundCustomPayloadPacket(p) => { println!("Got custom payload packet {:?}", p); } + GamePacket::ClientboundChangeDifficultyPacket(p) => { + println!("Got difficulty packet {:?}", p); + } }, Err(e) => { println!("Error: {:?}", e); diff --git a/azalea-core/src/difficulty.rs b/azalea-core/src/difficulty.rs new file mode 100644 index 00000000..21e980ba --- /dev/null +++ b/azalea-core/src/difficulty.rs @@ -0,0 +1,96 @@ +use std::fmt::{Debug, Error, Formatter}; + +#[derive(Hash, Clone, Debug, PartialEq)] +pub enum Difficulty { + PEACEFUL, + EASY, + NORMAL, + HARD, +} + +pub enum Err { + InvalidDifficulty(String), +} + +impl Debug for Err { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + Err::InvalidDifficulty(s) => write!(f, "Invalid difficulty: {}", s), + } + } +} + +impl Difficulty { + pub fn name(&self) -> &'static str { + match self { + Difficulty::PEACEFUL => "peaceful", + Difficulty::EASY => "easy", + Difficulty::NORMAL => "normal", + Difficulty::HARD => "hard", + } + } + + pub fn from_name(name: &str) -> Result { + match name { + "peaceful" => Ok(Difficulty::PEACEFUL), + "easy" => Ok(Difficulty::EASY), + "normal" => Ok(Difficulty::NORMAL), + "hard" => Ok(Difficulty::HARD), + _ => Err(Err::InvalidDifficulty(name.to_string())), + } + } + + pub fn by_id(id: u8) -> Difficulty { + match id % 4 { + 0 => Difficulty::PEACEFUL, + 1 => Difficulty::EASY, + 2 => Difficulty::NORMAL, + 3 => Difficulty::HARD, + // this shouldn't be possible because of the modulo, so panicking is fine + _ => panic!("Unknown difficulty id: {}", id), + } + } + + pub fn id(&self) -> u8 { + match self { + Difficulty::PEACEFUL => 0, + Difficulty::EASY => 1, + Difficulty::NORMAL => 2, + Difficulty::HARD => 3, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_difficulty_from_name() { + assert_eq!( + Difficulty::PEACEFUL, + Difficulty::from_name("peaceful").unwrap() + ); + assert_eq!(Difficulty::EASY, Difficulty::from_name("easy").unwrap()); + assert_eq!(Difficulty::NORMAL, Difficulty::from_name("normal").unwrap()); + assert_eq!(Difficulty::HARD, Difficulty::from_name("hard").unwrap()); + assert!(Difficulty::from_name("invalid").is_err()); + } + + #[test] + fn test_difficulty_id() { + assert_eq!(0, Difficulty::PEACEFUL.id()); + assert_eq!(1, Difficulty::EASY.id()); + assert_eq!(2, Difficulty::NORMAL.id()); + assert_eq!(3, Difficulty::HARD.id()); + assert_eq!(4, Difficulty::PEACEFUL.id()); + } + + #[test] + fn test_difficulty_name() { + assert_eq!("peaceful", Difficulty::PEACEFUL.name()); + assert_eq!("easy", Difficulty::EASY.name()); + assert_eq!("normal", Difficulty::NORMAL.name()); + assert_eq!("hard", Difficulty::HARD.name()); + } +} diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs index 887d1686..cdf07c43 100644 --- a/azalea-core/src/lib.rs +++ b/azalea-core/src/lib.rs @@ -1,14 +1,6 @@ //! Random miscellaneous things like UUIDs that don't deserve their own crate. +pub mod difficulty; pub mod game_type; pub mod resource_location; pub mod serializable_uuid; - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index 5127860e..0fa1d099 100644 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -1,5 +1,8 @@ use async_trait::async_trait; -use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; +use azalea_core::{ + difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, +}; +use num_traits::FromPrimitive; use tokio::io::{AsyncRead, AsyncReadExt}; use super::MAX_STRING_LENGTH; @@ -338,6 +341,28 @@ impl McBufReadable for bool { } } +// u8 +#[async_trait] +impl McBufReadable for u8 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_byte().await + } +} + +// i8 +#[async_trait] +impl McBufReadable for i8 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_byte().await.map(|i| i as i8) + } +} + // GameType #[async_trait] impl McBufReadable for GameType { @@ -386,3 +411,14 @@ impl McBufReadable for azalea_nbt::Tag { buf.read_nbt().await } } + +// Difficulty +#[async_trait] +impl McBufReadable for Difficulty { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + Ok(Difficulty::by_id(u8::read_into(buf).await?)) + } +} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index 6fbe6eab..fd9faeb4 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -1,6 +1,9 @@ use async_trait::async_trait; -use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; +use azalea_core::{ + difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, +}; use byteorder::{BigEndian, WriteBytesExt}; +use num_traits::FromPrimitive; use std::io::Write; use super::MAX_STRING_LENGTH; @@ -255,6 +258,13 @@ impl McBufWritable for bool { } } +// i8 +impl McBufWritable for i8 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_byte(*self as u8) + } +} + // GameType impl McBufWritable for GameType { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { @@ -284,3 +294,10 @@ impl McBufWritable for azalea_nbt::Tag { buf.write_nbt(self) } } + +// Difficulty +impl McBufWritable for Difficulty { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + u8::write_into(&self.id(), buf) + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs b/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs new file mode 100644 index 00000000..e12cfff3 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs @@ -0,0 +1,8 @@ +use azalea_core::difficulty::Difficulty; +use packet_macros::GamePacket; + +#[derive(Clone, Debug, GamePacket)] +pub struct ClientboundChangeDifficultyPacket { + pub difficulty: Difficulty, + pub locked: bool, +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 43b3ca3d..4efe72fb 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -1,3 +1,4 @@ +pub mod clientbound_change_difficulty_packet; pub mod clientbound_custom_payload_packet; pub mod clientbound_login_packet; pub mod clientbound_update_view_distance_packet; @@ -18,12 +19,16 @@ where ClientboundCustomPayloadPacket( clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, ), + ClientboundChangeDifficultyPacket( + clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, + ), } #[async_trait] impl ProtocolPacket for GamePacket { fn id(&self) -> u32 { match self { + GamePacket::ClientboundChangeDifficultyPacket(_packet) => 0x0e, GamePacket::ClientboundCustomPayloadPacket(_packet) => 0x18, GamePacket::ClientboundLoginPacket(_packet) => 0x26, GamePacket::ClientboundUpdateViewDistancePacket(_packet) => 0x4a, @@ -31,7 +36,12 @@ impl ProtocolPacket for GamePacket { } fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - Ok(()) + match self { + GamePacket::ClientboundChangeDifficultyPacket(packet) => packet.write(buf), + GamePacket::ClientboundCustomPayloadPacket(packet) => packet.write(buf), + GamePacket::ClientboundLoginPacket(packet) => packet.write(buf), + GamePacket::ClientboundUpdateViewDistancePacket(packet) => packet.write(buf), + } } /// Read a packet by its id, ConnectionProtocol, and flow @@ -45,6 +55,9 @@ impl ProtocolPacket for GamePacket { { Ok(match flow { PacketFlow::ServerToClient => match id { + 0x0e => clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket + ::read(buf) + .await?, 0x18 => clientbound_custom_payload_packet::ClientboundCustomPayloadPacket::read(buf).await?, 0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?, 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket From bd87cbb4434ba8bdf16ad93c5353ccefc0497d13 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 3 Jan 2022 13:36:02 -0600 Subject: [PATCH 10/83] create all empty brigadier modules --- azalea-brigadier/Cargo.toml | 8 ++++ azalea-brigadier/src/ambiguity_consumer.rs | 0 .../src/arguments/argument_type.rs | 0 .../src/arguments/bool_argument_type.rs | 0 .../src/arguments/double_argument_type.rs | 0 .../src/arguments/float_argument_type.rs | 0 .../src/arguments/integer_argument_type.rs | 0 .../src/arguments/long_argument_type.rs | 0 .../src/arguments/string_argument_type.rs | 0 .../src/builder/argument_builder.rs | 0 .../src/builder/literal_argument_builder.rs | 0 .../src/builder/required_argument_builder.rs | 0 azalea-brigadier/src/command.rs | 0 azalea-brigadier/src/command_dispatcher.rs | 0 .../src/context/command_context.rs | 0 .../src/context/command_context_builder.rs | 0 .../src/context/parsed_argument.rs | 0 .../src/context/parsed_command_node.rs | 0 azalea-brigadier/src/context/string_range.rs | 0 .../src/context/suggestion_context.rs | 0 .../exceptions/builtin_exception_provider.rs | 0 .../src/exceptions/builtin_exceptions.rs | 0 .../src/exceptions/command_exception_type.rs | 0 .../exceptions/command_syntax_exception.rs | 0 .../dynamic2_command_exception_type.rs | 0 .../dynamic3_command_exception_type.rs | 0 .../dynamic4_command_exception_type.rs | 0 .../dynamicN_command_exception_type.rs | 0 .../dynamic_command_exception_type.rs | 0 .../simple_command_exception_type.rs | 0 .../src/immutable_string_reader.rs | 0 azalea-brigadier/src/lib.rs | 8 ++++ azalea-brigadier/src/literal_message.rs | 0 azalea-brigadier/src/message.rs | 0 azalea-brigadier/src/parse_results.rs | 0 azalea-brigadier/src/redirect_modifier.rs | 0 azalea-brigadier/src/result_consumer.rs | 0 .../src/single_redirect_modifier.rs | 0 azalea-brigadier/src/string_reader.rs | 0 .../src/suggestion/integer_suggestion.rs | 0 azalea-brigadier/src/suggestion/suggestion.rs | 0 .../src/suggestion/suggestion_provider.rs | 0 .../src/suggestion/suggestions.rs | 0 .../src/suggestion/suggestions_builder.rs | 0 .../src/tree/argument_command_node.rs | 0 azalea-brigadier/src/tree/command_node.rs | 0 .../src/tree/literal_command_node.rs | 0 .../src/tree/root_command_node.rs | 0 .../arguments/bool_argument_type_test.rs | 0 .../arguments/double_argument_type_test.rs | 0 .../arguments/float_argument_type_test.rs | 0 .../arguments/integer_argument_type_test.rs | 0 .../arguments/long_argument_type_test.rs | 0 .../arguments/string_argument_type_test.rs | 0 .../tests/builder/argument_builder_test.rs | 0 .../builder/literal_argument_builder_test.rs | 0 .../builder/required_argument_builder_test.rs | 0 .../tests/command_dispatcher_test.rs | 0 .../tests/command_dispatcher_usages_test.rs | 0 .../tests/command_suggestions_test.rs | 0 .../tests/context/command_context_test.rs | 0 .../tests/context/parsed_argument_test.rs | 0 ...amic_command_syntax_exception_type_test.rs | 0 ...mple_command_syntax_exception_type_test.rs | 0 azalea-brigadier/tests/string_reader_test.rs | 0 .../tests/suggestion/suggestion_test.rs | 0 .../suggestion/suggestions_builder_test.rs | 0 .../tests/suggestion/suggestions_test.rs | 0 .../tests/tree/abstract_command_node_test.rs | 0 .../tests/tree/argument_command_node_test.rs | 0 .../tests/tree/literal_command_node_test.rs | 0 .../tests/tree/root_command_node_test.rs | 0 .../clientbound_declare_commands_packet.rs | 37 +++++++++++++++++++ 73 files changed, 53 insertions(+) create mode 100644 azalea-brigadier/Cargo.toml create mode 100644 azalea-brigadier/src/ambiguity_consumer.rs create mode 100644 azalea-brigadier/src/arguments/argument_type.rs create mode 100644 azalea-brigadier/src/arguments/bool_argument_type.rs create mode 100644 azalea-brigadier/src/arguments/double_argument_type.rs create mode 100644 azalea-brigadier/src/arguments/float_argument_type.rs create mode 100644 azalea-brigadier/src/arguments/integer_argument_type.rs create mode 100644 azalea-brigadier/src/arguments/long_argument_type.rs create mode 100644 azalea-brigadier/src/arguments/string_argument_type.rs create mode 100644 azalea-brigadier/src/builder/argument_builder.rs create mode 100644 azalea-brigadier/src/builder/literal_argument_builder.rs create mode 100644 azalea-brigadier/src/builder/required_argument_builder.rs create mode 100644 azalea-brigadier/src/command.rs create mode 100644 azalea-brigadier/src/command_dispatcher.rs create mode 100644 azalea-brigadier/src/context/command_context.rs create mode 100644 azalea-brigadier/src/context/command_context_builder.rs create mode 100644 azalea-brigadier/src/context/parsed_argument.rs create mode 100644 azalea-brigadier/src/context/parsed_command_node.rs create mode 100644 azalea-brigadier/src/context/string_range.rs create mode 100644 azalea-brigadier/src/context/suggestion_context.rs create mode 100644 azalea-brigadier/src/exceptions/builtin_exception_provider.rs create mode 100644 azalea-brigadier/src/exceptions/builtin_exceptions.rs create mode 100644 azalea-brigadier/src/exceptions/command_exception_type.rs create mode 100644 azalea-brigadier/src/exceptions/command_syntax_exception.rs create mode 100644 azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs create mode 100644 azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs create mode 100644 azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs create mode 100644 azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs create mode 100644 azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs create mode 100644 azalea-brigadier/src/exceptions/simple_command_exception_type.rs create mode 100644 azalea-brigadier/src/immutable_string_reader.rs create mode 100644 azalea-brigadier/src/lib.rs create mode 100644 azalea-brigadier/src/literal_message.rs create mode 100644 azalea-brigadier/src/message.rs create mode 100644 azalea-brigadier/src/parse_results.rs create mode 100644 azalea-brigadier/src/redirect_modifier.rs create mode 100644 azalea-brigadier/src/result_consumer.rs create mode 100644 azalea-brigadier/src/single_redirect_modifier.rs create mode 100644 azalea-brigadier/src/string_reader.rs create mode 100644 azalea-brigadier/src/suggestion/integer_suggestion.rs create mode 100644 azalea-brigadier/src/suggestion/suggestion.rs create mode 100644 azalea-brigadier/src/suggestion/suggestion_provider.rs create mode 100644 azalea-brigadier/src/suggestion/suggestions.rs create mode 100644 azalea-brigadier/src/suggestion/suggestions_builder.rs create mode 100644 azalea-brigadier/src/tree/argument_command_node.rs create mode 100644 azalea-brigadier/src/tree/command_node.rs create mode 100644 azalea-brigadier/src/tree/literal_command_node.rs create mode 100644 azalea-brigadier/src/tree/root_command_node.rs create mode 100644 azalea-brigadier/tests/arguments/bool_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/double_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/float_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/integer_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/long_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/string_argument_type_test.rs create mode 100644 azalea-brigadier/tests/builder/argument_builder_test.rs create mode 100644 azalea-brigadier/tests/builder/literal_argument_builder_test.rs create mode 100644 azalea-brigadier/tests/builder/required_argument_builder_test.rs create mode 100644 azalea-brigadier/tests/command_dispatcher_test.rs create mode 100644 azalea-brigadier/tests/command_dispatcher_usages_test.rs create mode 100644 azalea-brigadier/tests/command_suggestions_test.rs create mode 100644 azalea-brigadier/tests/context/command_context_test.rs create mode 100644 azalea-brigadier/tests/context/parsed_argument_test.rs create mode 100644 azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs create mode 100644 azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs create mode 100644 azalea-brigadier/tests/string_reader_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestion_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestions_builder_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestions_test.rs create mode 100644 azalea-brigadier/tests/tree/abstract_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/argument_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/literal_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/root_command_node_test.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml new file mode 100644 index 00000000..c617ffb1 --- /dev/null +++ b/azalea-brigadier/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "azalea-brigadier" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/azalea-brigadier/src/ambiguity_consumer.rs b/azalea-brigadier/src/ambiguity_consumer.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs b/azalea-brigadier/src/exceptions/builtin_exception_provider.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/command_exception_type.rs b/azalea-brigadier/src/exceptions/command_exception_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/simple_command_exception_type.rs b/azalea-brigadier/src/exceptions/simple_command_exception_type.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs new file mode 100644 index 00000000..1b4a90c9 --- /dev/null +++ b/azalea-brigadier/src/lib.rs @@ -0,0 +1,8 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +} diff --git a/azalea-brigadier/src/literal_message.rs b/azalea-brigadier/src/literal_message.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/result_consumer.rs b/azalea-brigadier/src/result_consumer.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/suggestion/integer_suggestion.rs b/azalea-brigadier/src/suggestion/integer_suggestion.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/suggestion/suggestion.rs b/azalea-brigadier/src/suggestion/suggestion.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/bool_argument_type_test.rs b/azalea-brigadier/tests/arguments/bool_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/double_argument_type_test.rs b/azalea-brigadier/tests/arguments/double_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/float_argument_type_test.rs b/azalea-brigadier/tests/arguments/float_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/integer_argument_type_test.rs b/azalea-brigadier/tests/arguments/integer_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/long_argument_type_test.rs b/azalea-brigadier/tests/arguments/long_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/string_argument_type_test.rs b/azalea-brigadier/tests/arguments/string_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/builder/argument_builder_test.rs b/azalea-brigadier/tests/builder/argument_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/builder/literal_argument_builder_test.rs b/azalea-brigadier/tests/builder/literal_argument_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/builder/required_argument_builder_test.rs b/azalea-brigadier/tests/builder/required_argument_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/context/command_context_test.rs b/azalea-brigadier/tests/context/command_context_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/context/parsed_argument_test.rs b/azalea-brigadier/tests/context/parsed_argument_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs b/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/abstract_command_node_test.rs b/azalea-brigadier/tests/tree/abstract_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/argument_command_node_test.rs b/azalea-brigadier/tests/tree/argument_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/literal_command_node_test.rs b/azalea-brigadier/tests/tree/literal_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/root_command_node_test.rs b/azalea-brigadier/tests/tree/root_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs new file mode 100644 index 00000000..1bcf0dd4 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs @@ -0,0 +1,37 @@ +// use std::hash::Hash; + +// use crate::mc_buf::Readable; + +// use super::LoginPacket; + +// #[derive(Hash, Clone, Debug)] +// pub struct ClientboundDeclareCommandsPacket { +// pub root: RootCommandNode, +// pub public_key: Vec, +// pub nonce: Vec, +// } + +// impl ClientboundHelloPacket { +// pub fn get(self) -> LoginPacket { +// LoginPacket::ClientboundHelloPacket(self) +// } + +// pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { +// panic!("ClientboundHelloPacket::write not implemented") +// } + +// pub async fn read( +// buf: &mut T, +// ) -> Result { +// let server_id = buf.read_utf_with_len(20).await?; +// let public_key = buf.read_byte_array().await?; +// let nonce = buf.read_byte_array().await?; + +// Ok(ClientboundHelloPacket { +// server_id, +// public_key, +// nonce, +// } +// .get()) +// } +// } From e3ecb607b826abd64d0ef0cd3e1e00f64fddfbdf Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 3 Jan 2022 22:53:53 -0600 Subject: [PATCH 11/83] add argument type trait --- .../src/arguments/argument_type.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index e69de29b..4dc97ee0 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -0,0 +1,20 @@ +pub trait ArgumentType { + // T parse(StringReader reader) throws CommandSyntaxException; + + // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + // return Suggestions.empty(); + // } + + // default Collection getExamples() { + // return Collections.emptyList(); + // } + + fn parse(reader: &mut StringReader) -> Result; + + fn list_suggestions( + context: &CommandContext, + builder: &mut SuggestionsBuilder, + ) -> Result; + + fn get_examples() -> Vec; +} From d959fb2d0cc4d8ad97eae86666876e22b2e50613 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 00:14:39 -0600 Subject: [PATCH 12/83] add string_reader --- Cargo.lock | 4 + Cargo.toml | 1 + .../src/arguments/bool_argument_type.rs | 8 + azalea-brigadier/src/arguments/mod.rs | 1 + azalea-brigadier/src/builder/mod.rs | 0 azalea-brigadier/src/context/mod.rs | 0 azalea-brigadier/src/exceptions/mod.rs | 0 .../src/immutable_string_reader.rs | 10 + azalea-brigadier/src/lib.rs | 18 ++ azalea-brigadier/src/message.rs | 1 + azalea-brigadier/src/string_reader.rs | 277 ++++++++++++++++++ azalea-brigadier/src/suggestion/mod.rs | 0 azalea-brigadier/src/tree/mod.rs | 0 13 files changed, 320 insertions(+) create mode 100644 azalea-brigadier/src/arguments/mod.rs create mode 100644 azalea-brigadier/src/builder/mod.rs create mode 100644 azalea-brigadier/src/context/mod.rs create mode 100644 azalea-brigadier/src/exceptions/mod.rs create mode 100644 azalea-brigadier/src/suggestion/mod.rs create mode 100644 azalea-brigadier/src/tree/mod.rs diff --git a/Cargo.lock b/Cargo.lock index eca4d4fa..67259fef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,10 @@ dependencies = [ "uuid", ] +[[package]] +name = "azalea-brigadier" +version = "0.1.0" + [[package]] name = "azalea-chat" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d22c326b..7f958207 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ members = [ "azalea-core", "azalea-auth", "azalea-nbt", + "azalea-brigadier", ] diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index e69de29b..d4a33517 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -0,0 +1,8 @@ +struct BoolArgumentType { + // private static final Collection EXAMPLES = Arrays.asList("true", "false"); + const EXAMPLES: &'static [&'static str] = &["true", "false"]; +} + +impl ArgumentType for BoolArgumentType { + +} \ No newline at end of file diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs new file mode 100644 index 00000000..18d01d88 --- /dev/null +++ b/azalea-brigadier/src/arguments/mod.rs @@ -0,0 +1 @@ +mod argument_type; diff --git a/azalea-brigadier/src/builder/mod.rs b/azalea-brigadier/src/builder/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs index e69de29b..2e067ace 100644 --- a/azalea-brigadier/src/immutable_string_reader.rs +++ b/azalea-brigadier/src/immutable_string_reader.rs @@ -0,0 +1,10 @@ +pub trait ImmutableStringReader { + fn remaining_length(&self) -> usize; + fn total_length(&self) -> usize; + fn get_read(&self) -> &str; + fn remaining(&self) -> &str; + fn can_read_length(&self, length: usize) -> bool; + fn can_read(&self) -> bool; + fn peek(&self) -> char; + fn peek_offset(&self, offset: usize) -> char; +} diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 1b4a90c9..d0966de3 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,3 +1,21 @@ +mod ambiguity_consumer; +mod arguments; +mod builder; +mod command; +mod command_dispatcher; +mod context; +mod exceptions; +mod immutable_string_reader; +mod literal_message; +mod message; +mod parse_results; +mod redirect_modifier; +mod result_consumer; +mod single_redirect_modifier; +mod string_reader; +mod suggestion; +mod tree; + #[cfg(test)] mod tests { #[test] diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index e69de29b..376cc711 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -0,0 +1,277 @@ +use crate::immutable_string_reader::ImmutableStringReader; +use std::str::FromStr; + +#[derive(Clone)] +struct StringReader<'a> { + pub string: &'a str, + pub cursor: usize, +} + +const SYNTAX_ESCAPE: char = '\\'; +const SYNTAX_DOUBLE_QUOTE: char = '"'; +const SYNTAX_SINGLE_QUOTE: char = '\''; + +impl<'a> From<&'a str> for &StringReader<'a> { + fn from(string: &'a str) -> &StringReader<'a> { + &StringReader { string, cursor: 0 } + } +} + +impl ImmutableStringReader for StringReader<'_> { + fn remaining_length(&self) -> usize { + self.string.len() - self.cursor + } + + fn total_length(&self) -> usize { + self.string.len() + } + + fn get_read(&self) -> &str { + &self.string[self.cursor..] + } + + fn remaining(&self) -> &str { + &self.string[self.cursor..] + } + + fn can_read_length(&self, length: usize) -> bool { + self.cursor + length <= self.string.len() + } + + fn can_read(&self) -> bool { + self.can_read_length(1) + } + + fn peek(&self) -> char { + self.string.chars().nth(self.cursor).unwrap() + } + + fn peek_offset(&self, offset: usize) -> char { + self.string.chars().nth(self.cursor + offset).unwrap() + } +} + +impl StringReader<'_> { + fn read(&mut self) -> char { + let c = self.peek(); + self.cursor += 1; + c + } + + fn skip(&mut self) { + self.cursor += 1; + } + + fn is_allowed_number(c: char) -> bool { + c >= '0' && c <= '9' || c == '.' || c == '-' + } + + fn is_quoted_string_start(c: char) -> bool { + c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE + } + + fn skip_whitespace(&mut self) { + while self.can_read() && self.peek().is_whitespace() { + self.skip(); + } + } + + fn read_int(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_int() + .create_with_context(self)); + } + let result = i32::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_int() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_long(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_long() + .create_with_context(self)); + } + let result = i64::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_long() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_double(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_double() + .create_with_context(self)); + } + let result = f64::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_double() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_float(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_float() + .create_with_context(self)); + } + let result = f32::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_float() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn is_allowed_in_unquoted_string(c: char) -> bool { + c >= '0' && c <= '9' + || c >= 'A' && c <= 'Z' + || c >= 'a' && c <= 'z' + || c == '_' + || c == '-' + || c == '.' + || c == '+' + } + + fn read_unquoted_string(&self) -> &str { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) { + self.skip(); + } + &self.string[start..self.cursor] + } + + fn read_quoted_string(&self) -> Result<&str, CommandSyntaxException> { + if !self.can_read() { + return ""; + } + let next = self.peek(); + if !StringReader::<'_>::is_quoted_string_start(next) { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_start_of_quote() + .create_with_context(self)); + } + self.skip(); + self.read_string_until(next) + } + + fn read_string_until(&self, terminator: char) -> Result { + let result = String::new(); + let mut escaped = false; + while self.can_read() { + let c = self.read(); + if escaped { + if c == terminator || c == SYNTAX_ESCAPE { + result.push(c); + escaped = false; + } else { + self.cursor -= 1; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_escape() + .create_with_context(self, c)); + } + } else if c == SYNTAX_ESCAPE { + escaped = true; + } else if c == terminator { + return Ok(result); + } else { + result.push(c); + } + } + + Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_end_of_quote() + .create_with_context(self)) + } + + fn read_string(&self) -> Result { + // if (!canRead()) { + // return ""; + // } + // final char next = peek(); + // if (isQuotedStringStart(next)) { + // skip(); + // return readStringUntil(next); + // } + // return readUnquotedString(); + if !self.can_read() { + return Ok(String::new()); + } + let next = self.peek(); + if StringReader::<'_>::is_quoted_string_start(next) { + self.skip(); + return self.read_string_until(next); + } + Ok(self.read_unquoted_string().to_string()) + } + + fn read_boolean(&self) -> Result { + let start = self.cursor; + let value = self.read_string()?; + if value.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_bool() + .create_with_context(self)); + } + + if value == "true" { + return Ok(true); + } else if value == "false" { + return Ok(false); + } else { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_bool() + .create_with_context(self, value)); + } + } + + fn expect(&self, c: char) -> Result<(), CommandSyntaxException> { + if !self.can_read() || self.peek() != c { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_symbol() + .create_with_context(self, c)); + } + self.skip(); + } diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs new file mode 100644 index 00000000..e69de29b From 8331851d972b42b68e1fb64e2ec9faef6c02be32 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 14:50:41 -0600 Subject: [PATCH 13/83] string reader --- azalea-brigadier/README.md | 3 + .../src/arguments/argument_type.rs | 11 +- azalea-brigadier/src/arguments/mod.rs | 2 +- .../src/context/command_context.rs | 3 + azalea-brigadier/src/context/mod.rs | 6 + .../src/exceptions/builtin_exceptions.rs | 158 +++++++++ .../src/exceptions/command_exception_type.rs | 0 .../exceptions/command_syntax_exception.rs | 82 +++++ .../dynamic2_command_exception_type.rs | 0 .../dynamic3_command_exception_type.rs | 0 .../dynamic4_command_exception_type.rs | 0 .../dynamicN_command_exception_type.rs | 0 .../dynamic_command_exception_type.rs | 0 azalea-brigadier/src/exceptions/mod.rs | 3 + .../simple_command_exception_type.rs | 0 .../src/immutable_string_reader.rs | 2 + azalea-brigadier/src/message.rs | 14 + azalea-brigadier/src/string_reader.rs | 333 +++++++++++++----- azalea-brigadier/src/suggestion/mod.rs | 5 + .../src/suggestion/suggestions.rs | 1 + .../src/suggestion/suggestions_builder.rs | 1 + azalea-brigadier/tests/string_reader_test.rs | 0 22 files changed, 528 insertions(+), 96 deletions(-) create mode 100644 azalea-brigadier/README.md delete mode 100644 azalea-brigadier/src/exceptions/command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/simple_command_exception_type.rs delete mode 100644 azalea-brigadier/tests/string_reader_test.rs diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md new file mode 100644 index 00000000..92c0d27e --- /dev/null +++ b/azalea-brigadier/README.md @@ -0,0 +1,3 @@ +# Azalea Brigadier + +A Rustier port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library. diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 4dc97ee0..4c48d6bb 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -1,3 +1,10 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; + pub trait ArgumentType { // T parse(StringReader reader) throws CommandSyntaxException; @@ -9,12 +16,12 @@ pub trait ArgumentType { // return Collections.emptyList(); // } - fn parse(reader: &mut StringReader) -> Result; + fn parse(reader: &mut StringReader) -> Result; fn list_suggestions( context: &CommandContext, builder: &mut SuggestionsBuilder, - ) -> Result; + ) -> Result; fn get_examples() -> Vec; } diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs index 18d01d88..50b0f09b 100644 --- a/azalea-brigadier/src/arguments/mod.rs +++ b/azalea-brigadier/src/arguments/mod.rs @@ -1 +1 @@ -mod argument_type; +pub mod argument_type; diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index e69de29b..ddbb447e 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -0,0 +1,3 @@ +pub struct CommandContext { + source: S, +} diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs index e69de29b..196d7c5b 100644 --- a/azalea-brigadier/src/context/mod.rs +++ b/azalea-brigadier/src/context/mod.rs @@ -0,0 +1,6 @@ +pub mod command_context; +pub mod command_context_builder; +pub mod parsed_argument; +pub mod parsed_command_node; +pub mod string_range; +pub mod suggestion_context; diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index e69de29b..fcca49cd 100644 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -0,0 +1,158 @@ +use std::fmt; + +use crate::{immutable_string_reader::ImmutableStringReader, message::Message}; + +use super::command_syntax_exception::CommandSyntaxException; + +pub enum BuiltInExceptions { + DoubleTooSmall { found: usize, min: usize }, + DoubleTooBig { found: usize, max: usize }, + + FloatTooSmall { found: usize, min: usize }, + FloatTooBig { found: usize, max: usize }, + + IntegerTooSmall { found: usize, min: usize }, + IntegerTooBig { found: usize, max: usize }, + + LONGTooSmall { found: usize, min: usize }, + LONGTooBig { found: usize, max: usize }, + + LiteralIncorrect { expected: String }, + + ReaderExpectedStartOfQuote, + ReaderExpectedEndOfQuote, + ReaderInvalidEscape { character: char }, + ReaderInvalidBool { value: String }, + ReaderInvalidInt { value: String }, + ReaderExpectedInt, + ReaderInvalidLong { value: String }, + ReaderExpectedLong, + ReaderInvalidDouble { value: String }, + ReaderExpectedDouble, + ReaderInvalidFloat { value: String }, + ReaderExpectedFloat, + ReaderExpectedBool, + ReaderExpectedSymbol { symbol: char }, + + ReaderUnknownCommand, + ReaderUnknownArgument, + DusoatcgerExpectedArgumentSeparator, + DispatcherParseException { message: String }, +} + +impl fmt::Debug for BuiltInExceptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BuiltInExceptions::DoubleTooSmall { found, min } => { + write!(f, "Double must not be less than {}, found {}", min, found) + } + BuiltInExceptions::DoubleTooBig { found, max } => { + write!(f, "Double must not be more than {}, found {}", max, found) + } + + BuiltInExceptions::FloatTooSmall { found, min } => { + write!(f, "Float must not be less than {}, found {}", min, found) + } + BuiltInExceptions::FloatTooBig { found, max } => { + write!(f, "Float must not be more than {}, found {}", max, found) + } + + BuiltInExceptions::IntegerTooSmall { found, min } => { + write!(f, "Integer must not be less than {}, found {}", min, found) + } + BuiltInExceptions::IntegerTooBig { found, max } => { + write!(f, "Integer must not be more than {}, found {}", max, found) + } + + BuiltInExceptions::LONGTooSmall { found, min } => { + write!(f, "Long must not be less than {}, found {}", min, found) + } + BuiltInExceptions::LONGTooBig { found, max } => { + write!(f, "Long must not be more than {}, found {}", max, found) + } + + BuiltInExceptions::LiteralIncorrect { expected } => { + write!(f, "Expected literal {}", expected) + } + + BuiltInExceptions::ReaderExpectedStartOfQuote => { + write!(f, "Expected quote to start a string") + } + BuiltInExceptions::ReaderExpectedEndOfQuote => { + write!(f, "Unclosed quoted string") + } + BuiltInExceptions::ReaderInvalidEscape { character } => { + write!( + f, + "Invalid escape sequence '{}' in quoted string", + character + ) + } + BuiltInExceptions::ReaderInvalidBool { value } => { + write!( + f, + "Invalid bool, expected true or false but found '{}'", + value + ) + } + BuiltInExceptions::ReaderInvalidInt { value } => { + write!(f, "Invalid Integer '{}'", value) + } + BuiltInExceptions::ReaderExpectedInt => { + write!(f, "Expected Integer") + } + BuiltInExceptions::ReaderInvalidLong { value } => { + write!(f, "Invalid long '{}'", value) + } + BuiltInExceptions::ReaderExpectedLong => { + write!(f, "Expected long") + } + BuiltInExceptions::ReaderInvalidDouble { value } => { + write!(f, "Invalid double '{}'", value) + } + BuiltInExceptions::ReaderExpectedDouble => { + write!(f, "Expected double") + } + BuiltInExceptions::ReaderInvalidFloat { value } => { + write!(f, "Invalid Float '{}'", value) + } + BuiltInExceptions::ReaderExpectedFloat => { + write!(f, "Expected Float") + } + BuiltInExceptions::ReaderExpectedBool => { + write!(f, "Expected bool") + } + BuiltInExceptions::ReaderExpectedSymbol { symbol } => { + write!(f, "Expected '{}'", symbol) + } + + BuiltInExceptions::ReaderUnknownCommand => { + write!(f, "Unknown command") + } + BuiltInExceptions::ReaderUnknownArgument => { + write!(f, "Incorrect argument for command") + } + BuiltInExceptions::DusoatcgerExpectedArgumentSeparator => { + write!( + f, + "Expected whitespace to end one argument, but found trailing data" + ) + } + BuiltInExceptions::DispatcherParseException { message } => { + write!(f, "Could not parse command: {}", message) + } + } + } +} + +impl BuiltInExceptions { + pub fn create(self) -> CommandSyntaxException { + let message = Message::from(format!("{:?}", self)); + CommandSyntaxException::create(self, message) + } + + pub fn create_with_context(self, reader: &dyn ImmutableStringReader) -> CommandSyntaxException { + let message = Message::from(format!("{:?}", self)); + CommandSyntaxException::new(self, message, reader.string(), reader.cursor()) + } +} diff --git a/azalea-brigadier/src/exceptions/command_exception_type.rs b/azalea-brigadier/src/exceptions/command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index e69de29b..b9fbea45 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -0,0 +1,82 @@ +use std::{cmp, rc::Rc}; + +use super::builtin_exceptions::BuiltInExceptions; +use crate::message::Message; + +pub struct CommandSyntaxException { + type_: BuiltInExceptions, + message: Message, + input: Option, + cursor: Option, +} + +const CONTEXT_AMOUNT: usize = 10; +const ENABLE_COMMAND_STACK_TRACES: bool = true; + +impl CommandSyntaxException { + pub fn new(type_: BuiltInExceptions, message: Message, input: &str, cursor: usize) -> Self { + Self { + type_, + message, + input: Some(input.to_string()), + cursor: Some(cursor), + } + } + + pub fn create(type_: BuiltInExceptions, message: Message) -> Self { + Self { + type_, + message, + input: None, + cursor: None, + } + } + + pub fn message(&self) -> String { + let mut message = self.message.string(); + let context = self.context(); + if let Some(context) = context { + message.push_str(&format!( + " at position {}: {}", + self.cursor.unwrap_or(usize::MAX), + context + )); + } + message + } + + pub fn raw_message(&self) -> &Message { + &self.message + } + + pub fn context(&self) -> Option { + if let Some(input) = &self.input { + if let Some(cursor) = self.cursor { + let mut builder = String::new(); + let cursor = cmp::min(input.len(), cursor); + + if cursor > CONTEXT_AMOUNT { + builder.push_str("..."); + } + + builder.push_str(&input[cmp::max(0, cursor - CONTEXT_AMOUNT)..cursor]); + builder.push_str("<--[HERE]"); + + return Some(builder); + } + } + None + } + + pub fn get_type(&self) -> &BuiltInExceptions { + &self.type_ + } + + pub fn input(&self) -> String { + self.input() + } + + pub fn cursor(&self) -> Option { + self.cursor + } +} diff --git a/azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs index e69de29b..4a82b01e 100644 --- a/azalea-brigadier/src/exceptions/mod.rs +++ b/azalea-brigadier/src/exceptions/mod.rs @@ -0,0 +1,3 @@ +pub mod builtin_exception_provider; +pub mod builtin_exceptions; +pub mod command_syntax_exception; diff --git a/azalea-brigadier/src/exceptions/simple_command_exception_type.rs b/azalea-brigadier/src/exceptions/simple_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs index 2e067ace..53531c64 100644 --- a/azalea-brigadier/src/immutable_string_reader.rs +++ b/azalea-brigadier/src/immutable_string_reader.rs @@ -1,6 +1,8 @@ pub trait ImmutableStringReader { + fn string(&self) -> &str; fn remaining_length(&self) -> usize; fn total_length(&self) -> usize; + fn cursor(&self) -> usize; fn get_read(&self) -> &str; fn remaining(&self) -> &str; fn can_read_length(&self, length: usize) -> bool; diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index 8b137891..71d0b178 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -1 +1,15 @@ +use std::rc::Rc; +pub struct Message(Rc); + +impl Message { + pub fn string(&self) -> String { + self.0.to_string() + } +} + +impl From for Message { + fn from(s: String) -> Self { + Self(Rc::new(s)) + } +} diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 376cc711..1119403a 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,23 +1,34 @@ -use crate::immutable_string_reader::ImmutableStringReader; +use crate::{ + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + immutable_string_reader::ImmutableStringReader, +}; use std::str::FromStr; #[derive(Clone)] -struct StringReader<'a> { - pub string: &'a str, - pub cursor: usize, +pub struct StringReader<'a> { + string: &'a str, + cursor: usize, } const SYNTAX_ESCAPE: char = '\\'; const SYNTAX_DOUBLE_QUOTE: char = '"'; const SYNTAX_SINGLE_QUOTE: char = '\''; -impl<'a> From<&'a str> for &StringReader<'a> { - fn from(string: &'a str) -> &StringReader<'a> { - &StringReader { string, cursor: 0 } +// impl<'a> From<&'a str> for &StringReader<'a> {} + +impl StringReader<'_> { + fn from(string: &str) -> StringReader { + StringReader { string, cursor: 0 } } } impl ImmutableStringReader for StringReader<'_> { + fn string(&self) -> &str { + self.string + } + fn remaining_length(&self) -> usize { self.string.len() - self.cursor } @@ -27,7 +38,7 @@ impl ImmutableStringReader for StringReader<'_> { } fn get_read(&self) -> &str { - &self.string[self.cursor..] + &self.string[..self.cursor] } fn remaining(&self) -> &str { @@ -49,122 +60,122 @@ impl ImmutableStringReader for StringReader<'_> { fn peek_offset(&self, offset: usize) -> char { self.string.chars().nth(self.cursor + offset).unwrap() } + + fn cursor(&self) -> usize { + self.cursor + } } impl StringReader<'_> { - fn read(&mut self) -> char { + pub fn read(&mut self) -> char { let c = self.peek(); self.cursor += 1; c } - fn skip(&mut self) { + pub fn skip(&mut self) { self.cursor += 1; } - fn is_allowed_number(c: char) -> bool { + pub fn is_allowed_number(c: char) -> bool { c >= '0' && c <= '9' || c == '.' || c == '-' } - fn is_quoted_string_start(c: char) -> bool { + pub fn is_quoted_string_start(c: char) -> bool { c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE } - fn skip_whitespace(&mut self) { + pub fn skip_whitespace(&mut self) { while self.can_read() && self.peek().is_whitespace() { self.skip(); } } - fn read_int(&self) -> Result<(), CommandSyntaxException> { + pub fn read_int(&mut self) -> Result<(), CommandSyntaxException> { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_int() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedInt.create_with_context(self)); } let result = i32::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_int() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidInt { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn read_long(&self) -> Result<(), CommandSyntaxException> { + pub fn read_long(&mut self) -> Result<(), CommandSyntaxException> { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_long() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedLong.create_with_context(self)); } let result = i64::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_long() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidLong { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn read_double(&self) -> Result<(), CommandSyntaxException> { + pub fn read_double(&mut self) -> Result<(), CommandSyntaxException> { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_double() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedDouble.create_with_context(self)); } let result = f64::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_double() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidDouble { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn read_float(&self) -> Result<(), CommandSyntaxException> { + pub fn read_float(&mut self) -> Result<(), CommandSyntaxException> { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_float() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedFloat.create_with_context(self)); } let result = f32::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_float() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidFloat { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn is_allowed_in_unquoted_string(c: char) -> bool { + pub fn is_allowed_in_unquoted_string(c: char) -> bool { c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' @@ -174,7 +185,7 @@ impl StringReader<'_> { || c == '+' } - fn read_unquoted_string(&self) -> &str { + pub fn read_unquoted_string(&mut self) -> &str { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) { self.skip(); @@ -182,22 +193,23 @@ impl StringReader<'_> { &self.string[start..self.cursor] } - fn read_quoted_string(&self) -> Result<&str, CommandSyntaxException> { + pub fn read_quoted_string(&mut self) -> Result { if !self.can_read() { - return ""; + return Ok(String::new()); } let next = self.peek(); if !StringReader::<'_>::is_quoted_string_start(next) { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_start_of_quote() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedStartOfQuote.create_with_context(self)); } self.skip(); self.read_string_until(next) } - fn read_string_until(&self, terminator: char) -> Result { - let result = String::new(); + pub fn read_string_until( + &mut self, + terminator: char, + ) -> Result { + let mut result = String::new(); let mut escaped = false; while self.can_read() { let c = self.read(); @@ -207,9 +219,8 @@ impl StringReader<'_> { escaped = false; } else { self.cursor -= 1; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_escape() - .create_with_context(self, c)); + return Err(BuiltInExceptions::ReaderInvalidEscape { character: c } + .create_with_context(self)); } } else if c == SYNTAX_ESCAPE { escaped = true; @@ -220,21 +231,10 @@ impl StringReader<'_> { } } - Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_end_of_quote() - .create_with_context(self)) + return Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self)); } - fn read_string(&self) -> Result { - // if (!canRead()) { - // return ""; - // } - // final char next = peek(); - // if (isQuotedStringStart(next)) { - // skip(); - // return readStringUntil(next); - // } - // return readUnquotedString(); + pub fn read_string(&mut self) -> Result { if !self.can_read() { return Ok(String::new()); } @@ -246,32 +246,179 @@ impl StringReader<'_> { Ok(self.read_unquoted_string().to_string()) } - fn read_boolean(&self) -> Result { - let start = self.cursor; - let value = self.read_string()?; - if value.is_empty() { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_bool() - .create_with_context(self)); - } + pub fn read_boolean(&mut self) -> Result { + let start = self.cursor; + let value = self.read_string()?; + if value.is_empty() { + return Err(BuiltInExceptions::ReaderExpectedBool.create_with_context(self)); + } - if value == "true" { - return Ok(true); - } else if value == "false" { - return Ok(false); - } else { - self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_bool() - .create_with_context(self, value)); - } - } + if value == "true" { + return Ok(true); + } else if value == "false" { + return Ok(false); + } else { + self.cursor = start; + return Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self)); + } + } - fn expect(&self, c: char) -> Result<(), CommandSyntaxException> { - if !self.can_read() || self.peek() != c { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_symbol() - .create_with_context(self, c)); - } - self.skip(); - } + pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxException> { + if !self.can_read() || self.peek() != c { + return Err( + BuiltInExceptions::ReaderExpectedSymbol { symbol: c }.create_with_context(self) + ); + } + self.skip(); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn can_read() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.can_read(), true); + reader.skip(); // 'a' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'b' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'c' + assert_eq!(reader.can_read(), false); + } + + #[test] + fn get_remaining_length() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.remaining_length(), 3); + reader.cursor = 1; + assert_eq!(reader.remaining_length(), 2); + reader.cursor = 2; + assert_eq!(reader.remaining_length(), 1); + reader.cursor = 3; + assert_eq!(reader.remaining_length(), 0); + } + + #[test] + fn can_read_length() { + let reader = StringReader::from("abc"); + assert_eq!(reader.can_read_length(1), true); + assert_eq!(reader.can_read_length(2), true); + assert_eq!(reader.can_read_length(3), true); + assert_eq!(reader.can_read_length(4), false); + assert_eq!(reader.can_read_length(5), false); + } + + #[test] + fn peek() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.peek(), 'a'); + assert_eq!(reader.cursor(), 0); + reader.cursor = 2; + assert_eq!(reader.peek(), 'c'); + assert_eq!(reader.cursor(), 2); + } + + #[test] + fn peek_length() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.peek_offset(0), 'a'); + assert_eq!(reader.peek_offset(2), 'c'); + assert_eq!(reader.cursor(), 0); + reader.cursor = 1; + assert_eq!(reader.peek_offset(1), 'c'); + assert_eq!(reader.cursor(), 1); + } + + #[test] + fn read() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.read(), 'a'); + assert_eq!(reader.read(), 'b'); + assert_eq!(reader.read(), 'c'); + assert_eq!(reader.cursor(), 3); + } + + #[test] + fn skip() { + let mut reader = StringReader::from("abc"); + reader.skip(); + assert_eq!(reader.cursor(), 1); + } + + #[test] + fn get_remaining() { + let mut reader = StringReader::from("Hello!"); + assert_eq!(reader.remaining(), "Hello!"); + reader.cursor = 3; + assert_eq!(reader.remaining(), "lo!"); + reader.cursor = 6; + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn get_read() { + let mut reader = StringReader::from("Hello!"); + assert_eq!(reader.get_read(), ""); + reader.cursor = 3; + assert_eq!(reader.get_read(), "Hel"); + reader.cursor = 6; + assert_eq!(reader.get_read(), "Hello!"); + } + + #[test] + fn skip_whitespace_none() { + let mut reader = StringReader::from("Hello!"); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 0); + } + + #[test] + fn skip_whitespace_mixed() { + let mut reader = StringReader::from(" \t \t\nHello!"); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 5); + } + + #[test] + fn skip_whitespace_empty() { + let mut reader = StringReader::from(""); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 0); + } + + #[test] + fn read_unquoted_string() { + let mut reader = StringReader::from("hello world"); + assert_eq!(reader.read_unquoted_string(), "hello"); + assert_eq!(reader.get_read(), "hello"); + assert_eq!(reader.remaining(), "world"); + } + + #[test] + fn read_unquoted_string_empty() { + let mut reader = StringReader::from(""); + assert_eq!(reader.read_unquoted_string(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_unquoted_string_empty_with_remaining() { + let mut reader = StringReader::from(" hello world"); + assert_eq!(reader.read_unquoted_string(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), " hello world"); + } + + #[test] + fn read_quoted_string() { + let mut reader = StringReader::from("\"hello world\""); + assert_eq!(reader.read_unquoted_string(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), ""); + } +} diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs index e69de29b..050bae6c 100644 --- a/azalea-brigadier/src/suggestion/mod.rs +++ b/azalea-brigadier/src/suggestion/mod.rs @@ -0,0 +1,5 @@ +pub mod integer_suggestion; +pub mod suggestion; +pub mod suggestion_provider; +pub mod suggestions; +pub mod suggestions_builder; diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index e69de29b..354fc418 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -0,0 +1 @@ +pub struct Suggestions {} diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs index e69de29b..6960f52b 100644 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -0,0 +1 @@ +pub struct SuggestionsBuilder {} diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs deleted file mode 100644 index e69de29b..00000000 From 4626bd45cd8b3875a0ee006cf8e2c7cfb9f8cbcc Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 14:58:14 -0600 Subject: [PATCH 14/83] fix a couple tests --- .../src/exceptions/command_syntax_exception.rs | 8 +++++++- azalea-brigadier/src/string_reader.rs | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index b9fbea45..38aa1c3a 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -1,4 +1,4 @@ -use std::{cmp, rc::Rc}; +use std::{cmp, fmt, rc::Rc}; use super::builtin_exceptions::BuiltInExceptions; use crate::message::Message; @@ -80,3 +80,9 @@ impl CommandSyntaxException { self.cursor } } + +impl fmt::Debug for CommandSyntaxException { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message()) + } +} diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 1119403a..4ef35ac0 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -187,7 +187,7 @@ impl StringReader<'_> { pub fn read_unquoted_string(&mut self) -> &str { let start = self.cursor; - while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) { + while self.can_read() && StringReader::is_allowed_in_unquoted_string(self.peek()) { self.skip(); } &self.string[start..self.cursor] @@ -395,7 +395,7 @@ mod test { let mut reader = StringReader::from("hello world"); assert_eq!(reader.read_unquoted_string(), "hello"); assert_eq!(reader.get_read(), "hello"); - assert_eq!(reader.remaining(), "world"); + assert_eq!(reader.remaining(), " world"); } #[test] @@ -417,7 +417,7 @@ mod test { #[test] fn read_quoted_string() { let mut reader = StringReader::from("\"hello world\""); - assert_eq!(reader.read_unquoted_string(), "hello world"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), ""); } From c7554b40d52c33db6a5cc23a5b2e01b96dfe3a59 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 15:13:08 -0600 Subject: [PATCH 15/83] add some more tests --- .../src/exceptions/builtin_exceptions.rs | 1 + azalea-brigadier/src/string_reader.rs | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index fcca49cd..1533364b 100644 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -4,6 +4,7 @@ use crate::{immutable_string_reader::ImmutableStringReader, message::Message}; use super::command_syntax_exception::CommandSyntaxException; +#[derive(Clone, PartialEq)] pub enum BuiltInExceptions { DoubleTooSmall { found: usize, min: usize }, DoubleTooBig { found: usize, max: usize }, diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 4ef35ac0..28823bce 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -421,4 +421,87 @@ mod test { assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), ""); } + + #[test] + fn read_single_quoted_string() { + let mut reader = StringReader::from("'hello world'"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "'hello world'"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_mixed_quoted_string_double_inside_single() { + let mut reader = StringReader::from("'hello \"world\"'"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); + assert_eq!(reader.get_read(), "'hello \"world\"'"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_mixed_quoted_string_single_inside_double() { + let mut reader = StringReader::from("\"hello 'world'\""); + assert_eq!(reader.read_quoted_string().unwrap(), "hello 'world'"); + assert_eq!(reader.get_read(), "\"hello 'world'\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_empty_quoted() { + let mut reader = StringReader::from(""); + assert_eq!(reader.read_quoted_string().unwrap(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_empty_quoted_with_remaining() { + let mut reader = StringReader::from("\"\" hello world"); + assert_eq!(reader.read_quoted_string().unwrap(), ""); + assert_eq!(reader.get_read(), "\"\""); + assert_eq!(reader.remaining(), " hello world"); + } + + #[test] + fn read_quoted_string_with_escaped_quote() { + let mut reader = StringReader::from("\"hello \\\"world\\\"\""); + assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); + assert_eq!(reader.get_read(), "\"hello \\\"world\\\"\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_with_escaped_escapes() { + let mut reader = StringReader::from("\"\\\\o/\""); + assert_eq!(reader.read_quoted_string().unwrap(), "\\o/"); + assert_eq!(reader.get_read(), "\"\\\\o/\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_with_remaining() { + let mut reader = StringReader::from("\"hello world\" foo bar"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_quoted_string_with_immediate_remaining() { + let mut reader = StringReader::from("\"hello world\"foo bar"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_quoted_string_no_open() { + let mut reader = StringReader::from("hello world\""); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedStartOfQuote); + assert_eq!(e.cursor(), Some(0)); + } + } } From d56f60c05f316ab4cc37ebe7a9ad4caf91a75de6 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 15:40:54 -0600 Subject: [PATCH 16/83] add all the string reader tests --- azalea-brigadier/src/string_reader.rs | 402 +++++++++++++++++++++++++- 1 file changed, 393 insertions(+), 9 deletions(-) diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 28823bce..f32de473 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -91,7 +91,7 @@ impl StringReader<'_> { } } - pub fn read_int(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_int(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); @@ -109,10 +109,10 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } - pub fn read_long(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_long(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); @@ -130,12 +130,12 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } - pub fn read_double(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_double(&mut self) -> Result { let start = self.cursor; - while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; @@ -151,10 +151,10 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } - pub fn read_float(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_float(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); @@ -172,7 +172,7 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } pub fn is_allowed_in_unquoted_string(c: char) -> bool { @@ -504,4 +504,388 @@ mod test { assert_eq!(e.cursor(), Some(0)); } } + + #[test] + fn read_quoted_string_no_close() { + let mut reader = StringReader::from("\"hello world"); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedEndOfQuote); + assert_eq!(e.cursor(), Some(12)); + } + } + + #[test] + fn read_quoted_string_invalid_escape() { + let mut reader = StringReader::from("\"hello\\nworld\""); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: 'n' } + ); + assert_eq!(e.cursor(), Some(7)); + } + } + + #[test] + fn read_quoted_string_invalid_quote_escape() { + let mut reader = StringReader::from("'hello\\\"\'world"); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: '"' } + ); + assert_eq!(e.cursor(), Some(7)); + } + } + + #[test] + fn read_string_no_quotes() { + let mut reader = StringReader::from("hello world"); + assert_eq!(reader.read_string().unwrap(), "hello"); + assert_eq!(reader.get_read(), "hello"); + assert_eq!(reader.remaining(), " world"); + } + + #[test] + fn read_string_single_quotes() { + let mut reader = StringReader::from("'hello world'"); + assert_eq!(reader.read_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "'hello world'"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_string_double_quotes() { + let mut reader = StringReader::from("\"hello world\""); + assert_eq!(reader.read_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_int() { + let mut reader = StringReader::from("1234567890"); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_int_negative() { + let mut reader = StringReader::from("-1234567890"); + assert_eq!(reader.read_int().unwrap(), -1234567890); + assert_eq!(reader.get_read(), "-1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_int_invalid() { + let mut reader = StringReader::from("12.34"); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidInt { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_int_none() { + let mut reader = StringReader::from(""); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_int_with_remaining() { + let mut reader = StringReader::from("1234567890 foo bar"); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_int_with_remaining_immediate() { + let mut reader = StringReader::from("1234567890foo bar"); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_long() { + let mut reader = StringReader::from("1234567890"); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_long_negative() { + let mut reader = StringReader::from("-1234567890"); + assert_eq!(reader.read_long().unwrap(), -1234567890); + assert_eq!(reader.get_read(), "-1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_long_invalid() { + let mut reader = StringReader::from("12.34"); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidLong { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_long_none() { + let mut reader = StringReader::from(""); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_long_with_remaining() { + let mut reader = StringReader::from("1234567890 foo bar"); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_long_with_remaining_immediate() { + let mut reader = StringReader::from("1234567890foo bar"); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_double() { + let mut reader = StringReader::from("123"); + assert_eq!(reader.read_double().unwrap(), 123.0); + assert_eq!(reader.get_read(), "123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_double_with_decimal() { + let mut reader = StringReader::from("12.34"); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_double_negative() { + let mut reader = StringReader::from("-123"); + assert_eq!(reader.read_double().unwrap(), -123.0); + assert_eq!(reader.get_read(), "-123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_double_invalid() { + let mut reader = StringReader::from("12.34.56"); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidDouble { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_double_none() { + let mut reader = StringReader::from(""); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_double_with_remaining() { + let mut reader = StringReader::from("12.34 foo bar"); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_double_with_remaining_immediate() { + let mut reader = StringReader::from("12.34foo bar"); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_float() { + let mut reader = StringReader::from("123"); + assert_eq!(reader.read_float().unwrap(), 123.0f32); + assert_eq!(reader.get_read(), "123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_float_with_decimal() { + let mut reader = StringReader::from("12.34"); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_float_negative() { + let mut reader = StringReader::from("-123"); + assert_eq!(reader.read_float().unwrap(), -123.0f32); + assert_eq!(reader.get_read(), "-123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_float_invalid() { + let mut reader = StringReader::from("12.34.56"); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidFloat { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_float_none() { + let mut reader = StringReader::from(""); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_float_with_remaining() { + let mut reader = StringReader::from("12.34 foo bar"); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_float_with_remaining_immediate() { + let mut reader = StringReader::from("12.34foo bar"); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn expect_correct() { + let mut reader = StringReader::from("abc"); + reader.expect('a'); + assert_eq!(reader.cursor(), 1); + } + + #[test] + fn expect_incorrect() { + let mut reader = StringReader::from("bcd"); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn expect_none() { + let mut reader = StringReader::from(""); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_boolean_correct() { + let mut reader = StringReader::from("true"); + assert_eq!(reader.read_boolean().unwrap(), true); + assert_eq!(reader.get_read(), "true"); + } + + #[test] + fn read_boolean_incorrect() { + let mut reader = StringReader::from("tuesday"); + let result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidBool { + value: "tuesday".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_boolean_none() { + let mut reader = StringReader::from(""); + let result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool); + assert_eq!(e.cursor(), Some(0)); + } + } } From 315f2258190b33c63df7797a97178019f5aea02b Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 22:33:45 -0600 Subject: [PATCH 17/83] add some more stuff from brigadier --- .gitignore | 1 + Cargo.lock | 3 + azalea-brigadier/Cargo.toml | 3 +- .../src/arguments/argument_type.rs | 4 +- .../src/arguments/bool_argument_type.rs | 25 ++- azalea-brigadier/src/arguments/mod.rs | 6 + .../src/builder/argument_builder.rs | 106 +++++++++++ azalea-brigadier/src/builder/mod.rs | 3 + azalea-brigadier/src/command.rs | 10 + azalea-brigadier/src/command_dispatcher.rs | 23 +++ .../src/context/command_context.rs | 87 +++++++++ .../src/context/command_context_builder.rs | 180 ++++++++++++++++++ .../src/context/parsed_argument.rs | 24 +++ .../src/context/parsed_command_node.rs | 22 +++ azalea-brigadier/src/context/string_range.rs | 45 +++++ .../src/context/suggestion_context.rs | 6 + azalea-brigadier/src/lib.rs | 3 + azalea-brigadier/src/redirect_modifier.rs | 8 + .../src/single_redirect_modifier.rs | 8 + .../src/suggestion/suggestion_provider.rs | 14 ++ .../src/tree/argument_command_node.rs | 118 ++++++++++++ azalea-brigadier/src/tree/command_node.rs | 52 +++++ .../src/tree/literal_command_node.rs | 96 ++++++++++ azalea-brigadier/src/tree/mod.rs | 4 + .../src/tree/root_command_node.rs | 61 ++++++ 25 files changed, 902 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf7..f97818c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/doc \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 67259fef..0eeb6520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,9 @@ dependencies = [ [[package]] name = "azalea-brigadier" version = "0.1.0" +dependencies = [ + "lazy_static", +] [[package]] name = "azalea-chat" diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index c617ffb1..3694a4b7 100644 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -1,8 +1,9 @@ [package] +edition = "2021" name = "azalea-brigadier" version = "0.1.0" -edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lazy_static = "^1.4" diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 4c48d6bb..34d57285 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -5,7 +5,7 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub trait ArgumentType { +pub trait ArgumentType { // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -16,7 +16,7 @@ pub trait ArgumentType { // return Collections.emptyList(); // } - fn parse(reader: &mut StringReader) -> Result; + fn parse(reader: &mut StringReader) -> Result; fn list_suggestions( context: &CommandContext, diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index d4a33517..f4c03373 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -1,8 +1,19 @@ -struct BoolArgumentType { - // private static final Collection EXAMPLES = Arrays.asList("true", "false"); - const EXAMPLES: &'static [&'static str] = &["true", "false"]; -} +use crate::context::command_context::CommandContext; -impl ArgumentType for BoolArgumentType { - -} \ No newline at end of file +use super::argument_type::ArgumentType; + +struct BoolArgumentType {} + +impl ArgumentType for BoolArgumentType {} + +impl BoolArgumentType { + const EXAMPLES: &'static [&'static str] = &["true", "false"]; + + fn bool() -> Self { + Self {} + } + + fn get_bool(context: CommandContext, name: String) { + context.get_argument::(name) + } +} diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs index 50b0f09b..487c5db7 100644 --- a/azalea-brigadier/src/arguments/mod.rs +++ b/azalea-brigadier/src/arguments/mod.rs @@ -1 +1,7 @@ pub mod argument_type; +pub mod bool_argument_type; +pub mod double_argument_type; +pub mod float_argument_type; +pub mod integer_argument_type; +pub mod long_argument_type; +pub mod string_argument_type; diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index e69de29b..8a64a9e4 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -0,0 +1,106 @@ +use crate::{ + command::Command, + redirect_modifier::RedirectModifier, + single_redirect_modifier::SingleRedirectModifier, + tree::{command_node::CommandNode, root_command_node::RootCommandNode}, +}; + +pub struct BaseArgumentBuilder +where + T: ArgumentBuilder, +{ + arguments: RootCommandNode, + command: dyn Command, + requirement: dyn Fn(&S) -> bool, + target: Option>, + modifier: Option>, + forks: bool, +} + +pub trait ArgumentBuilder { + fn this() -> T; + fn build(self) -> dyn CommandNode; +} + +impl BaseArgumentBuilder +where + T: ArgumentBuilder, +{ + pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { + if self.target.is_some() { + return Err("Cannot add children to a redirected node".to_string()); + } + self.command = command; + Ok(self) + } + + pub fn arguments(&self) -> &Vec> { + &self.arguments.get_children() + } + + pub fn executes(&mut self, command: dyn Command) -> &mut T { + self.command = command; + self + } + + pub fn command(&self) -> dyn Command { + self.command + } + + pub fn requires(&mut self, requirement: dyn Fn(&S) -> bool) -> &mut T { + self.requirement = requirement; + self + } + + pub fn requirement(&self) -> dyn Fn(&S) -> bool { + self.requirement + } + + pub fn redirect(&mut self, target: dyn CommandNode) -> &mut T { + self.forward(target, None, false) + } + + pub fn redirect_modifier( + &mut self, + target: dyn CommandNode, + modifier: dyn SingleRedirectModifier, + ) -> &mut T { + // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false); + self.forward(target, modifier.map(|m| |o| vec![m.apply(o)]), false) + } + + pub fn fork( + &mut self, + target: dyn CommandNode, + modifier: dyn RedirectModifier, + ) -> &mut T { + self.forward(target, Some(modifier), true) + } + + pub fn forward( + &mut self, + target: dyn CommandNode, + modifier: Option>, + fork: bool, + ) -> Result<&mut T, String> { + if !self.arguments.get_children().is_empty() { + return Err("Cannot forward a node with children".to_string()); + } + self.target = Some(target); + self.modifier = modifier; + self.forks = fork; + Ok(self) + } + + pub fn redirect(&self) -> Option<&dyn CommandNode> { + self.target.as_ref() + } + + pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + self.modifier.as_ref() + } + + pub fn is_fork(&self) -> bool { + self.forks + } +} diff --git a/azalea-brigadier/src/builder/mod.rs b/azalea-brigadier/src/builder/mod.rs index e69de29b..26f2f644 100644 --- a/azalea-brigadier/src/builder/mod.rs +++ b/azalea-brigadier/src/builder/mod.rs @@ -0,0 +1,3 @@ +pub mod argument_builder; +pub mod literal_argument_builder; +pub mod required_argument_builder; diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index e69de29b..a76454b7 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -0,0 +1,10 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, +}; + +pub const SINGLE_SUCCESS: i32 = 1; + +pub trait Command { + fn run(&self, context: &mut CommandContext) -> Result; +} diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index e69de29b..c476a39b 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -0,0 +1,23 @@ +/// The core command dispatcher, for registering, parsing, and executing commands. +/// The `S` generic is a custom "source" type, such as a user or originator of a command +pub struct CommandDispatcher { + root: RootCommandNode, +} + +impl CommandDispatcher { + /// The string required to separate individual arguments in an input string + /// + /// See: [`ARGUMENT_SEPARATOR_CHAR`] + const ARGUMENT_SEPARATOR: &'static str = " "; + + /// The char required to separate individual arguments in an input string + /// + /// See: [`ARGUMENT_SEPARATOR`] + const ARGUMENT_SEPARATOR_CHAR: char = ' '; + + const USAGE_OPTIONAL_OPEN: &'static str = "["; + const USAGE_OPTIONAL_CLOSE: &'static str = "]"; + const USAGE_REQUIRED_OPEN: &'static str = "("; + const USAGE_REQUIRED_CLOSE: &'static str = ")"; + const USAGE_OR: &'static str = "|"; +} diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index ddbb447e..c6210a88 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -1,3 +1,90 @@ +use super::{ + parsed_argument::ParsedArgument, parsed_command_node::ParsedCommandNode, + string_range::StringRange, +}; +use crate::{ + arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, + tree::command_node::CommandNode, +}; +use std::collections::HashMap; + pub struct CommandContext { source: S, + input: String, + command: dyn Command, + arguments: HashMap>, + root_node: dyn CommandNode, + nodes: Vec>, + range: StringRange, + child: Option>, + modifier: Option>, + forks: bool, +} + +impl CommandContext { + pub fn clone_for(&self, source: S) -> Self { + if self.source == source { + return self.clone(); + } + Self { + source, + input: self.input.clone(), + command: self.command.clone(), + arguments: self.arguments.clone(), + root_node: self.root_node.clone(), + nodes: self.nodes.clone(), + range: self.range.clone(), + child: self.child.clone(), + modifier: self.modifier.clone(), + forks: self.forks, + } + } + + fn child(&self) -> &Option> { + &self.child + } + + fn last_child(&self) -> &CommandContext { + let mut result = self; + while result.child.is_some() { + result = result.child.as_ref().unwrap(); + } + result + } + + fn command(&self) -> &dyn Command { + &self.command + } + + fn source(&self) -> &S { + &self.source + } + + // public V getArgument(final String name, final Class clazz) { + // final ParsedArgument argument = arguments.get(name); + + // if (argument == null) { + // throw new IllegalArgumentException("No such argument '" + name + "' exists on this command"); + // } + + // final Object result = argument.getResult(); + // if (PRIMITIVE_TO_WRAPPER.getOrDefault(clazz, clazz).isAssignableFrom(result.getClass())) { + // return (V) result; + // } else { + // throw new IllegalArgumentException("Argument '" + name + "' is defined as " + result.getClass().getSimpleName() + ", not " + clazz); + // } + // } + fn get_argument(&self, name: &str) -> Result { + let argument = self.arguments.get(name); + + if argument.is_none() { + return Err(format!( + "No such argument '{}' exists on this command", + name + )); + } + + let result = argument.unwrap().result(); + Ok(result) + } } diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index e69de29b..e74b5b1c 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -0,0 +1,180 @@ +use std::collections::HashMap; + +use crate::{ + arguments::argument_type::ArgumentType, command::Command, + command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, + tree::command_node::CommandNode, +}; + +use super::{ + command_context::CommandContext, parsed_argument::ParsedArgument, + parsed_command_node::ParsedCommandNode, string_range::StringRange, + suggestion_context::SuggestionContext, +}; + +// public class CommandContextBuilder { +// private final Map> arguments = new LinkedHashMap<>(); +// private final CommandNode rootNode; +// private final List> nodes = new ArrayList<>(); +// private final CommandDispatcher dispatcher; +// private S source; +// private Command command; +// private CommandContextBuilder child; +// private StringRange range; +// private RedirectModifier modifier = null; +// private boolean forks; + +#[derive(Clone)] +pub struct CommandContextBuilder { + arguments: HashMap>, + root_node: dyn CommandNode, + nodes: Vec>, + dispatcher: CommandDispatcher, + source: S, + command: Box>, + child: Option>, + range: StringRange, + modifier: Option>>, + forks: bool, +} + +// public CommandContextBuilder(final CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start) { +// this.rootNode = rootNode; +// this.dispatcher = dispatcher; +// this.source = source; +// this.range = StringRange.at(start); +// } + +impl CommandContextBuilder { + pub fn new( + dispatcher: CommandDispatcher, + source: S, + root_node: dyn CommandNode, + start: usize, + ) -> Self { + Self { + root_node, + dispatcher, + source, + range: StringRange::at(start), + ..Default::default() + } + } + + pub fn with_source(mut self, source: S) -> Self { + self.source = source; + self + } + + pub fn source(&self) -> &S { + &self.source + } + + pub fn root_node(&self) -> &dyn CommandNode { + &self.root_node + } + + pub fn with_argument( + mut self, + name: String, + argument: ParsedArgument, + ) -> Self { + self.arguments.insert(name, argument); + self + } + + pub fn arguments(&self) -> &HashMap> { + &self.arguments + } + + pub fn with_command(mut self, command: Box>) -> Self { + self.command = command; + self + } + + pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { + self.nodes.push(ParsedCommandNode::new(node, range)); + self.range = StringRange::encompassing(&self.range, &range); + self.modifier = node.redirect_modifier(); + self.forks = node.is_fork(); + self + } + + pub fn with_child(mut self, child: CommandContextBuilder) -> Self { + self.child = Some(child); + self + } + + pub fn child(&self) -> Option<&CommandContextBuilder> { + self.child.as_ref() + } + + pub fn last_child(&self) -> Option<&CommandContextBuilder> { + let mut result = self; + while let Some(child) = result.child() { + result = child; + } + Some(result) + } + + pub fn command(&self) -> &dyn Command { + &*self.command + } + + pub fn nodes(&self) -> &Vec> { + &self.nodes + } + + pub fn build(self, input: &str) -> CommandContext { + CommandContext { + source: self.source, + input, + arguments: self.arguments, + command: self.command, + root_node: self.root_node, + nodes: self.nodes, + range: self.range, + child: self.child.map(|child| child.build(input)), + modifier: self.modifier, + forks: self.forks, + } + } + + pub fn dispatcher(&self) -> &CommandDispatcher { + &self.dispatcher + } + + pub fn range(&self) -> &StringRange { + &self.range + } + + pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { + if self.range.start() <= cursor { + if self.range.end() < cursor { + if let Some(child) = self.child() { + child.find_suggestion_context(cursor); + } else if !self.nodes.is_empty() { + let last = self.nodes.last().unwrap(); + let end = last.range().end() + 1; + return SuggestionContext::new(last.node(), end); + } else { + return SuggestionContext::new(self.root_node, self.range.start()); + } + } else { + let prev = self.root_node; + for node in &self.nodes { + let node_range = node.range(); + if node_range.start() <= cursor && cursor <= node_range.end() { + return SuggestionContext::new(prev, node_range.start()); + } + prev = node.node(); + } + if prev.is_none() { + return Err(String::from("Can't find node before cursor")); + } + return SuggestionContext::new(prev.unwrap(), self.range.start()); + } + } + Err(String::from("Can't find node before cursor")) + } +} diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index e69de29b..5f9c2cdb 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -0,0 +1,24 @@ +use super::string_range::StringRange; + +#[derive(PartialEq, Eq, Hash)] +pub struct ParsedArgument { + range: StringRange, + result: T, +} + +impl ParsedArgument { + fn new(start: usize, end: usize, result: T) -> Self { + Self { + range: StringRange::between(start, end), + result, + } + } + + fn range(&self) -> &StringRange { + &self.range + } + + fn result(&self) -> &T { + &self.result + } +} diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index e69de29b..98e99959 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -0,0 +1,22 @@ +use super::string_range::StringRange; +use crate::tree::command_node::CommandNode; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct ParsedCommandNode { + node: dyn CommandNode, + range: StringRange, +} + +impl ParsedCommandNode { + fn new(node: dyn CommandNode, range: StringRange) -> Self { + Self { node, range } + } + + fn node(&self) -> &dyn CommandNode { + &self.node + } + + fn range(&self) -> &StringRange { + &self.range + } +} diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs index e69de29b..d775ab68 100644 --- a/azalea-brigadier/src/context/string_range.rs +++ b/azalea-brigadier/src/context/string_range.rs @@ -0,0 +1,45 @@ +use std::cmp; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StringRange { + start: usize, + end: usize, +} + +impl StringRange { + pub fn new(start: usize, end: usize) -> Self { + Self { start, end } + } + + pub fn at(pos: usize) -> Self { + Self::new(pos, pos) + } + + pub fn between(start: usize, end: usize) -> Self { + Self::new(start, end) + } + + pub fn encompassing(a: &Self, b: &Self) -> Self { + Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end)) + } + + pub fn start(&self) -> usize { + self.start + } + + pub fn end(&self) -> usize { + self.end + } + + pub fn get(&self, reader: &str) -> &str { + &reader[self.start..self.end] + } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } + + pub fn length(&self) -> usize { + self.end - self.start + } +} diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index e69de29b..540a5f23 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -0,0 +1,6 @@ +use crate::tree::command_node::CommandNode; + +pub struct SuggestionContext { + parent: dyn CommandNode, + start_pos: usize, +} diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index d0966de3..b2345abb 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate lazy_static; + mod ambiguity_consumer; mod arguments; mod builder; diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index e69de29b..cfefd120 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -0,0 +1,8 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, +}; + +pub trait RedirectModifier { + fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; +} diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs index e69de29b..dd63244d 100644 --- a/azalea-brigadier/src/single_redirect_modifier.rs +++ b/azalea-brigadier/src/single_redirect_modifier.rs @@ -0,0 +1,8 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, +}; + +pub trait SingleRedirectModifier { + fn apply(&self, context: CommandContext) -> Result; +} diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs index e69de29b..3027d460 100644 --- a/azalea-brigadier/src/suggestion/suggestion_provider.rs +++ b/azalea-brigadier/src/suggestion/suggestion_provider.rs @@ -0,0 +1,14 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, +}; + +use super::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}; + +pub trait SuggestionProvider { + fn suggestions( + &self, + context: &CommandContext, + builder: &SuggestionsBuilder, + ) -> Result; +} diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index e69de29b..df7d3f5c 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -0,0 +1,118 @@ +use std::fmt::{Display, Formatter}; + +use crate::{ + arguments::argument_type::ArgumentType, + context::{ + command_context::CommandContext, command_context_builder::CommandContextBuilder, + parsed_argument::ParsedArgument, + }, + exceptions::command_syntax_exception::CommandSyntaxException, + string_reader::StringReader, + suggestion::{ + suggestion_provider::SuggestionProvider, suggestions::Suggestions, + suggestions_builder::SuggestionsBuilder, + }, +}; + +use super::command_node::{BaseCommandNode, CommandNode}; + +const USAGE_ARGUMENT_OPEN: &str = "<"; +const USAGE_ARGUMENT_CLOSE: &str = ">"; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct ArgumentCommandNode { + name: String, + type_: dyn ArgumentType, + custom_suggestions: dyn SuggestionProvider, + // Since Rust doesn't have extending, we put the struct this is extending as the "base" field + pub base: BaseCommandNode, +} + +impl ArgumentCommandNode { + fn get_type(&self) -> &dyn ArgumentType { + &self.type_ + } + + fn custom_suggestions(&self) -> &dyn SuggestionProvider { + &self.custom_suggestions + } +} + +impl CommandNode for ArgumentCommandNode { + fn name(&self) -> &str { + &self.name + } + + fn parse( + &self, + reader: StringReader, + context_builder: CommandContextBuilder, + ) -> Result<(), CommandSyntaxException> { + // final int start = reader.getCursor(); + // final T result = type.parse(reader); + // final ParsedArgument parsed = new ParsedArgument<>(start, reader.getCursor(), result); + + // contextBuilder.withArgument(name, parsed); + // contextBuilder.withNode(this, parsed.getRange()); + + let start = reader.get_cursor(); + let result = self.get_type().parse(reader)?; + let parsed = ParsedArgument::new(start, reader.get_cursor(), result); + + context_builder.with_argument(&self.name, parsed); + context_builder.with_node(self, parsed.get_range()); + + Ok(()) + } + + fn list_suggestions( + &self, + context: CommandContext, + builder: SuggestionsBuilder, + ) -> Result { + if self.custom_suggestions.is_none() { + self.get_type().list_suggestions(context, builder) + } else { + self.custom_suggestions.get_suggestions(context, builder) + } + } + + fn is_valid_input(&self, input: &str) -> bool { + let reader = StringReader::new(input); + let result = self.get_type().parse(reader); + if result.is_ok() { + return !reader.can_read() || reader.peek() == ' '; + } else { + return false; + } + } + + fn usage_text(&self) -> &str { + USAGE_ARGUMENT_OPEN + self.name + USAGE_ARGUMENT_CLOSE + } + + fn create_builder(&self) -> RequiredArgumentBuilder { + let builder = RequiredArgumentBuilder::argument(&self.name, &self.type_); + builder.requires(self.base.get_requirement()); + builder.forward( + self.base.get_redirect(), + self.base.get_redirect_modifier(), + self.base.is_fork(), + ); + builder.suggests(self.custom_suggestions()); + if self.base.get_command() != None { + builder.executes(self.base.get_command().unwrap()); + } + builder + } + + fn get_examples(&self) -> Vec { + self.type_.get_examples() + } +} + +impl Display for ArgumentCommandNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "", self.name, self.type_) + } +} diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index e69de29b..286820b9 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -0,0 +1,52 @@ +use std::collections::HashMap; + +use crate::{ + builder::argument_builder::ArgumentBuilder, + command::Command, + context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, + exceptions::command_syntax_exception::CommandSyntaxException, + redirect_modifier::RedirectModifier, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; + +use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; + +pub struct BaseCommandNode { + // private final Map> children = new LinkedHashMap<>(); + // private final Map> literals = new LinkedHashMap<>(); + // private final Map> arguments = new LinkedHashMap<>(); + // private final Predicate requirement; + // private final CommandNode redirect; + // private final RedirectModifier modifier; + // private final boolean forks; + // private Command command; + children: HashMap>, + literals: HashMap>, + arguments: HashMap>, + requirement: Option bool>, + redirect: Option>, + modifier: Option>, + forks: bool, + command: Option>, +} + +impl BaseCommandNode {} + +pub trait CommandNode { + fn name(&self) -> &str; + fn usage_text(&self) -> &str; + fn parse( + &self, + reader: StringReader, + context_builder: CommandContextBuilder, + ) -> Result<(), CommandSyntaxException>; + fn list_suggestions( + &self, + context: CommandContext, + builder: SuggestionsBuilder, + ) -> Result; + fn is_valid_input(&self, input: &str) -> bool; + fn create_builder(&self) -> dyn ArgumentBuilder; + fn get_examples(&self) -> Vec; +} diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index e69de29b..bb0e613c 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -0,0 +1,96 @@ +use crate::{ + context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; + +use super::command_node::{BaseCommandNode, CommandNode}; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct LiteralCommandNode { + literal: String, + literal_lowercase: String, + // Since Rust doesn't have extending, we put the struct this is extending as the "base" field + pub base: BaseCommandNode, +} + +impl LiteralCommandNode { + pub fn literal(&self) -> &String { + &self.literal + } + + pub fn parse(&self, reader: StringReader) -> i32 { + let start = reader.get_cursor(); + if reader.can_read(self.literal.len()) { + let end = start + self.literal.len(); + if reader.get_string()[start..end].eq(&self.literal) { + reader.set_cursor(end); + if !reader.can_read() || reader.peek() == ' ' { + return end as i32; + } else { + reader.set_cursor(start); + } + } + } + -1 + } +} + +impl CommandNode for LiteralCommandNode { + fn name(&self) -> &str { + &self.literal + } + + fn parse( + &self, + reader: StringReader, + context_builder: CommandContextBuilder, + ) -> Result<(), CommandSyntaxException> { + let start = reader.get_cursor(); + let end = self.parse(reader); + if end > -1 { + return Ok(()); + } + + Err(BuiltInExceptions::LiteralIncorrect { + expected: self.literal(), + } + .create_with_context(reader)) + } + + fn list_suggestions( + &self, + context: CommandContext, + builder: SuggestionsBuilder, + ) -> Result { + if self + .literal_lowercase + .starts_with(&builder.remaining_lowercase()) + { + builder.suggest(self.literal()) + } else { + Suggestions::empty() + } + } + + fn is_valid_input(&self, input: &str) -> bool { + self.parse(StringReader::from(input)) > -1 + } + + fn usage_text(&self) -> &str { + self.literal + } + + fn create_builder(&self) -> LiteralArgumentBuilder { + let builder = LiteralArgumentBuilder::literal(self.literal()); + builder.requires(self.requirement()); + builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); + if self.command().is_some() { + builder.executes(self.command().unwrap()); + } + builder + } +} diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs index e69de29b..3dc22583 100644 --- a/azalea-brigadier/src/tree/mod.rs +++ b/azalea-brigadier/src/tree/mod.rs @@ -0,0 +1,4 @@ +pub mod argument_command_node; +pub mod command_node; +pub mod literal_command_node; +pub mod root_command_node; diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index e69de29b..004ab6a8 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -0,0 +1,61 @@ +use std::fmt::{Display, Formatter}; + +use crate::{ + context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; + +use super::command_node::{BaseCommandNode, CommandNode}; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct RootCommandNode { + // Since Rust doesn't have extending, we put the struct this is extending as the "base" field + pub base: BaseCommandNode, +} + +impl CommandNode for RootCommandNode { + fn name(&self) -> &str { + "" + } + + fn parse( + &self, + reader: StringReader, + context_builder: CommandContextBuilder, + ) -> Result<(), CommandSyntaxException> { + } + + fn list_suggestions( + &self, + context: CommandContext, + builder: SuggestionsBuilder, + ) -> Result { + Suggestions::empty() + } + + fn is_valid_input(&self, input: &str) -> bool { + false + } + + fn usage_text(&self) -> &str { + "" + } + + fn create_builder(&self) -> () { + panic!("Cannot convert root into a builder"); + } + + fn get_examples(&self) -> Vec { + vec![] + } +} + +impl Display for RootCommandNode<()> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} From fec7a2bfedc562306523b9d3c51b97e376dc32d9 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 23:46:23 -0600 Subject: [PATCH 18/83] AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA --- .../src/arguments/argument_type.rs | 12 +- .../src/arguments/bool_argument_type.rs | 2 +- .../src/builder/argument_builder.rs | 7 +- .../src/builder/required_argument_builder.rs | 120 ++++++++++++++++++ azalea-brigadier/src/command_dispatcher.rs | 2 + .../src/context/command_context.rs | 6 +- .../src/context/command_context_builder.rs | 14 +- .../src/context/parsed_argument.rs | 6 +- .../src/tree/argument_command_node.rs | 11 +- azalea-brigadier/src/tree/command_node.rs | 11 +- 10 files changed, 160 insertions(+), 31 deletions(-) diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 34d57285..ea453a1a 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -5,7 +5,12 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub trait ArgumentType { +pub trait ArgumentResult {} + +pub trait ArgumentType +where + T: ArgumentResult, +{ // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -16,12 +21,13 @@ pub trait ArgumentType { // return Collections.emptyList(); // } - fn parse(reader: &mut StringReader) -> Result; + fn parse(&self, reader: &mut StringReader) -> Result; fn list_suggestions( + &self, context: &CommandContext, builder: &mut SuggestionsBuilder, ) -> Result; - fn get_examples() -> Vec; + fn get_examples(&self) -> Vec; } diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index f4c03373..74df3331 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -4,7 +4,7 @@ use super::argument_type::ArgumentType; struct BoolArgumentType {} -impl ArgumentType for BoolArgumentType {} +impl ArgumentType for BoolArgumentType {} impl BoolArgumentType { const EXAMPLES: &'static [&'static str] = &["true", "false"]; diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 8a64a9e4..19706a22 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -10,7 +10,7 @@ where T: ArgumentBuilder, { arguments: RootCommandNode, - command: dyn Command, + command: Option>, requirement: dyn Fn(&S) -> bool, target: Option>, modifier: Option>, @@ -18,7 +18,6 @@ where } pub trait ArgumentBuilder { - fn this() -> T; fn build(self) -> dyn CommandNode; } @@ -92,11 +91,11 @@ where Ok(self) } - pub fn redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNode> { self.target.as_ref() } - pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { self.modifier.as_ref() } diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index e69de29b..3ec613c4 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -0,0 +1,120 @@ +use crate::{ + arguments::argument_type::ArgumentType, + suggestion::suggestion_provider::SuggestionProvider, + tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, +}; + +use super::argument_builder::BaseArgumentBuilder; + +// private RequiredArgumentBuilder(final String name, final ArgumentType type) { +// this.name = name; +// this.type = type; +// } + +// public static RequiredArgumentBuilder argument(final String name, final ArgumentType type) { +// return new RequiredArgumentBuilder<>(name, type); +// } + +// public RequiredArgumentBuilder suggests(final SuggestionProvider provider) { +// this.suggestionsProvider = provider; +// return getThis(); +// } + +// public SuggestionProvider getSuggestionsProvider() { +// return suggestionsProvider; +// } + +// @Override +// protected RequiredArgumentBuilder getThis() { +// return this; +// } + +// public ArgumentType getType() { +// return type; +// } + +// public String getName() { +// return name; +// } + +// public ArgumentCommandNode build() { +// final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); + +// for (final CommandNode argument : getArguments()) { +// result.addChild(argument); +// } + +// return result; +// } + +pub struct RequiredArgumentBuilder { + // private final String name; + // private final ArgumentType type; + // private SuggestionProvider suggestionsProvider = null; + name: String, + type_: dyn ArgumentType, + suggestions_provider: Option>, + + pub base: BaseArgumentBuilder, +} + +impl RequiredArgumentBuilder { + pub fn new(name: String, type_: dyn ArgumentType) -> Self { + Self { + name, + type_, + suggestions_provider: None, + base: BaseArgumentBuilder::new(name, type_), + } + } + + pub fn argument(name: String, type_: dyn ArgumentType) -> Self { + Self::new(name, type_) + } + + pub fn suggests(mut self, provider: dyn SuggestionProvider) -> Self { + self.suggestions_provider = Some(provider); + self + } + + pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { + self.suggestions_provider.as_ref() + } + + pub fn get_type(&self) -> &dyn ArgumentType { + &self.type_ + } + + pub fn name(&self) -> &str { + &self.name + } + + // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); + + // for (final CommandNode argument : getArguments()) { + // result.addChild(argument); + // } + + // return result; + pub fn build(self) -> ArgumentCommandNode { + let result = ArgumentCommandNode { + name: self.name, + type_: &self.type_, + base: BaseCommandNode { + command: self.base.command, + requirement: self.base.requirement, + redirect: self.base.redirect, + modifier: self.base.modifier, + forks: self.base.forks, + ..BaseCommandNode::default() + }, + custom_suggestions: self.base.custom_suggestions, + }; + + for argument in self.base.arguments { + result.add_child(argument); + } + + result + } +} diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index c476a39b..0e9b9036 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,3 +1,5 @@ +use crate::tree::root_command_node::RootCommandNode; + /// The core command dispatcher, for registering, parsing, and executing commands. /// The `S` generic is a custom "source" type, such as a user or originator of a command pub struct CommandDispatcher { diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index c6210a88..7a2189d9 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -3,7 +3,9 @@ use super::{ string_range::StringRange, }; use crate::{ - arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, + arguments::argument_type::{ArgumentResult, ArgumentType}, + command::Command, + redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; use std::collections::HashMap; @@ -12,7 +14,7 @@ pub struct CommandContext { source: S, input: String, command: dyn Command, - arguments: HashMap>, + arguments: HashMap>>, root_node: dyn CommandNode, nodes: Vec>, range: StringRange, diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index e74b5b1c..ac4a36bb 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::ArgumentType, command::Command, - command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, + arguments::argument_type::{ArgumentResult, ArgumentType}, + command::Command, + command_dispatcher::CommandDispatcher, + redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; @@ -26,7 +28,7 @@ use super::{ #[derive(Clone)] pub struct CommandContextBuilder { - arguments: HashMap>, + arguments: HashMap>>, root_node: dyn CommandNode, nodes: Vec>, dispatcher: CommandDispatcher, @@ -77,13 +79,15 @@ impl CommandContextBuilder { pub fn with_argument( mut self, name: String, - argument: ParsedArgument, + argument: ParsedArgument>, ) -> Self { self.arguments.insert(name, argument); self } - pub fn arguments(&self) -> &HashMap> { + pub fn arguments( + &self, + ) -> &HashMap>> { &self.arguments } diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 5f9c2cdb..77a47078 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -1,12 +1,14 @@ +use std::marker::PhantomData; + use super::string_range::StringRange; #[derive(PartialEq, Eq, Hash)] -pub struct ParsedArgument { +pub struct ParsedArgument { range: StringRange, result: T, } -impl ParsedArgument { +impl ParsedArgument { fn new(start: usize, end: usize, result: T) -> Self { Self { range: StringRange::between(start, end), diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index df7d3f5c..51add3d5 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,7 +1,8 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::ArgumentType, + arguments::argument_type::{ArgumentResult, ArgumentType}, + builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, parsed_argument::ParsedArgument, @@ -22,14 +23,14 @@ const USAGE_ARGUMENT_CLOSE: &str = ">"; #[derive(Hash, PartialEq, Eq, Debug, Clone)] pub struct ArgumentCommandNode { name: String, - type_: dyn ArgumentType, + type_: Box>, custom_suggestions: dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode, } impl ArgumentCommandNode { - fn get_type(&self) -> &dyn ArgumentType { + fn get_type(&self) -> &dyn ArgumentType { &self.type_ } @@ -45,7 +46,7 @@ impl CommandNode for ArgumentCommandNode { fn parse( &self, - reader: StringReader, + reader: &mut StringReader, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { // final int start = reader.getCursor(); @@ -68,7 +69,7 @@ impl CommandNode for ArgumentCommandNode { fn list_suggestions( &self, context: CommandContext, - builder: SuggestionsBuilder, + builder: &mut SuggestionsBuilder, ) -> Result { if self.custom_suggestions.is_none() { self.get_type().list_suggestions(context, builder) diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index 286820b9..717ea5f1 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::{ + arguments::argument_type::{ArgumentResult, ArgumentType}, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -13,17 +14,9 @@ use crate::{ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; pub struct BaseCommandNode { - // private final Map> children = new LinkedHashMap<>(); - // private final Map> literals = new LinkedHashMap<>(); - // private final Map> arguments = new LinkedHashMap<>(); - // private final Predicate requirement; - // private final CommandNode redirect; - // private final RedirectModifier modifier; - // private final boolean forks; - // private Command command; children: HashMap>, literals: HashMap>, - arguments: HashMap>, + arguments: HashMap>>, requirement: Option bool>, redirect: Option>, modifier: Option>, From cb4d871f6f56a484dc87151ea3ad55f7e3bbed97 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Jan 2022 00:42:30 +0000 Subject: [PATCH 19/83] work a bit on brigadier --- .../src/arguments/argument_type.rs | 26 +++++++++++++++---- .../src/arguments/bool_argument_type.rs | 2 +- .../src/context/command_context_builder.rs | 6 ++--- azalea-brigadier/src/tree/command_node.rs | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index ea453a1a..f5ca457b 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -1,3 +1,4 @@ +use super::bool_argument_type::BoolArgumentType; use crate::{ context::command_context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, @@ -5,12 +6,27 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub trait ArgumentResult {} +pub enum DefaultArguments { + Bool(BoolArgumentType), +} -pub trait ArgumentType -where - T: ArgumentResult, -{ +/* +define_arguments! { + Entity(EntityArgumentType) +} + +=== + +enum CustomArguments { + Entity(EntityArgumentType) +} +enum BrigadierArguments { + BuiltIn(DefaultArguments) + Custom(CustomArguments) +} +*/ + +pub trait ArgumentType { // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index 74df3331..9bdf42e5 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -2,7 +2,7 @@ use crate::context::command_context::CommandContext; use super::argument_type::ArgumentType; -struct BoolArgumentType {} +pub struct BoolArgumentType {} impl ArgumentType for BoolArgumentType {} diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index ac4a36bb..5766ea9a 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,10 +1,8 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, - command::Command, - command_dispatcher::CommandDispatcher, - redirect_modifier::RedirectModifier, + arguments::argument_type::ArgumentType, command::Command, + command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index 717ea5f1..0d9212aa 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -16,7 +16,7 @@ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::Li pub struct BaseCommandNode { children: HashMap>, literals: HashMap>, - arguments: HashMap>>, + arguments: HashMap>>, requirement: Option bool>, redirect: Option>, modifier: Option>, From 60b129b3a62b66dd389d1775892405fe735b9540 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 10 Jan 2022 20:29:46 -0600 Subject: [PATCH 20/83] progress --- .../src/arguments/argument_type.rs | 28 +++++--- .../src/arguments/bool_argument_type.rs | 49 +++++++++++-- .../src/builder/argument_builder.rs | 50 +++++++------ .../src/builder/literal_argument_builder.rs | 32 +++++++++ .../src/builder/required_argument_builder.rs | 70 ++++--------------- azalea-brigadier/src/command.rs | 4 +- azalea-brigadier/src/command_dispatcher.rs | 26 ++++++- .../src/context/command_context.rs | 26 ++++--- .../src/context/command_context_builder.rs | 56 +++++++-------- .../src/context/parsed_argument.rs | 3 +- .../src/context/parsed_command_node.rs | 10 +-- .../src/context/suggestion_context.rs | 4 +- azalea-brigadier/src/redirect_modifier.rs | 4 +- .../src/single_redirect_modifier.rs | 4 +- .../src/suggestion/suggestion_provider.rs | 4 +- .../src/tree/argument_command_node.rs | 24 +++---- azalea-brigadier/src/tree/command_node.rs | 28 ++++---- .../src/tree/literal_command_node.rs | 26 +++++-- .../src/tree/root_command_node.rs | 12 ++-- 19 files changed, 259 insertions(+), 201 deletions(-) diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index f5ca457b..107b1cbf 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -6,27 +6,30 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub enum DefaultArguments { - Bool(BoolArgumentType), +pub trait Types { + fn bool(value: bool) -> Self; } /* -define_arguments! { +#[derive(Types)] +enum BrigadierTypes { Entity(EntityArgumentType) } === -enum CustomArguments { +enum BrigadierTypes { + Bool(BoolArgumentType) + Entity(EntityArgumentType) } -enum BrigadierArguments { - BuiltIn(DefaultArguments) - Custom(CustomArguments) -} */ -pub trait ArgumentType { +pub trait ArgumentType +where + Self: Sized, + T: Types, +{ // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -41,9 +44,12 @@ pub trait ArgumentType { fn list_suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &mut SuggestionsBuilder, - ) -> Result; + ) -> Result + where + S: Sized, + T: Sized; fn get_examples(&self) -> Vec; } diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index 9bdf42e5..dc2c6896 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -1,10 +1,51 @@ -use crate::context::command_context::CommandContext; +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; -use super::argument_type::ArgumentType; +use super::argument_type::{ArgumentType, Types}; pub struct BoolArgumentType {} -impl ArgumentType for BoolArgumentType {} +impl ArgumentType for BoolArgumentType +where + T: Types, +{ + fn parse(&self, reader: &mut StringReader) -> Result { + Ok(T::bool(reader.read_boolean()?)) + } + + fn list_suggestions( + &self, + context: &CommandContext, + builder: &mut SuggestionsBuilder, + ) -> Result + where + S: Sized, + T: Sized, + { + // if ("true".startsWith(builder.getRemainingLowerCase())) { + // builder.suggest("true"); + // } + // if ("false".startsWith(builder.getRemainingLowerCase())) { + // builder.suggest("false"); + // } + // return builder.buildFuture(); + if "true".starts_with(builder.get_remaining_lower_case()) { + builder.suggest("true"); + } + if "false".starts_with(builder.get_remaining_lower_case()) { + builder.suggest("false"); + } + Ok(builder.build_future()) + } + + fn get_examples(&self) -> Vec { + vec![] + } +} impl BoolArgumentType { const EXAMPLES: &'static [&'static str] = &["true", "false"]; @@ -13,7 +54,7 @@ impl BoolArgumentType { Self {} } - fn get_bool(context: CommandContext, name: String) { + fn get_bool(context: CommandContext, name: String) { context.get_argument::(name) } } diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 19706a22..bd2a2c15 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -5,27 +5,25 @@ use crate::{ tree::{command_node::CommandNode, root_command_node::RootCommandNode}, }; -pub struct BaseArgumentBuilder +pub struct BaseArgumentBuilder<'a, S, T> where - T: ArgumentBuilder, + S: Sized, + T: Sized, { - arguments: RootCommandNode, - command: Option>, - requirement: dyn Fn(&S) -> bool, - target: Option>, - modifier: Option>, + arguments: RootCommandNode<'a, S, T>, + command: Option<&'a dyn Command>, + requirement: &'a dyn Fn(&S) -> bool, + target: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } pub trait ArgumentBuilder { - fn build(self) -> dyn CommandNode; + fn build(self) -> dyn CommandNode; } -impl BaseArgumentBuilder -where - T: ArgumentBuilder, -{ - pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { +impl BaseArgumentBuilder<'_, S, T> { + pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); } @@ -33,20 +31,20 @@ where Ok(self) } - pub fn arguments(&self) -> &Vec> { + pub fn arguments(&self) -> &Vec<&dyn CommandNode> { &self.arguments.get_children() } - pub fn executes(&mut self, command: dyn Command) -> &mut T { + pub fn executes(&mut self, command: dyn Command) -> &mut T { self.command = command; self } - pub fn command(&self) -> dyn Command { + pub fn command(&self) -> dyn Command { self.command } - pub fn requires(&mut self, requirement: dyn Fn(&S) -> bool) -> &mut T { + pub fn requires(&mut self, requirement: &dyn Fn(&S) -> bool) -> &mut T { self.requirement = requirement; self } @@ -55,14 +53,14 @@ where self.requirement } - pub fn redirect(&mut self, target: dyn CommandNode) -> &mut T { + pub fn redirect(&mut self, target: &dyn CommandNode) -> &mut T { self.forward(target, None, false) } pub fn redirect_modifier( &mut self, - target: dyn CommandNode, - modifier: dyn SingleRedirectModifier, + target: &dyn CommandNode, + modifier: &dyn SingleRedirectModifier, ) -> &mut T { // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false); self.forward(target, modifier.map(|m| |o| vec![m.apply(o)]), false) @@ -70,16 +68,16 @@ where pub fn fork( &mut self, - target: dyn CommandNode, - modifier: dyn RedirectModifier, + target: &dyn CommandNode, + modifier: &dyn RedirectModifier, ) -> &mut T { self.forward(target, Some(modifier), true) } pub fn forward( &mut self, - target: dyn CommandNode, - modifier: Option>, + target: &dyn CommandNode, + modifier: Option<&dyn RedirectModifier>, fork: bool, ) -> Result<&mut T, String> { if !self.arguments.get_children().is_empty() { @@ -91,11 +89,11 @@ where Ok(self) } - pub fn get_redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNode> { self.target.as_ref() } - pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { self.modifier.as_ref() } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index e69de29b..cf9f1ee9 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -0,0 +1,32 @@ +use crate::tree::literal_command_node::LiteralCommandNode; + +use super::argument_builder::BaseArgumentBuilder; + +pub struct LiteralArgumentBuilder<'a, S, T> { + literal: String, + + pub base: BaseArgumentBuilder<'a, S, T>, +} + +impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> { + pub fn new(literal: String) -> Self { + Self { + literal, + base: BaseArgumentBuilder::default(), + } + } + + pub fn literal(name: String) -> Self { + Self::new(name) + } + + pub fn build(self) -> LiteralCommandNode<'a, S, T> { + let result = LiteralCommandNode::new(self.literal, self.base); + + for argument in self.base.arguments { + result.add_child(argument); + } + + result + } +} diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 3ec613c4..6f6fa8eb 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,92 +1,50 @@ use crate::{ - arguments::argument_type::ArgumentType, suggestion::suggestion_provider::SuggestionProvider, tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, }; use super::argument_builder::BaseArgumentBuilder; -// private RequiredArgumentBuilder(final String name, final ArgumentType type) { -// this.name = name; -// this.type = type; -// } - -// public static RequiredArgumentBuilder argument(final String name, final ArgumentType type) { -// return new RequiredArgumentBuilder<>(name, type); -// } - -// public RequiredArgumentBuilder suggests(final SuggestionProvider provider) { -// this.suggestionsProvider = provider; -// return getThis(); -// } - -// public SuggestionProvider getSuggestionsProvider() { -// return suggestionsProvider; -// } - -// @Override -// protected RequiredArgumentBuilder getThis() { -// return this; -// } - -// public ArgumentType getType() { -// return type; -// } - -// public String getName() { -// return name; -// } - -// public ArgumentCommandNode build() { -// final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); - -// for (final CommandNode argument : getArguments()) { -// result.addChild(argument); -// } - -// return result; -// } - -pub struct RequiredArgumentBuilder { +pub struct RequiredArgumentBuilder<'a, S, T> { // private final String name; // private final ArgumentType type; // private SuggestionProvider suggestionsProvider = null; name: String, - type_: dyn ArgumentType, - suggestions_provider: Option>, + type_: &'a T, + suggestions_provider: Option<&'a dyn SuggestionProvider>, - pub base: BaseArgumentBuilder, + pub base: BaseArgumentBuilder<'a, S, T>, } -impl RequiredArgumentBuilder { - pub fn new(name: String, type_: dyn ArgumentType) -> Self { +impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> { + pub fn new(name: String, type_: T) -> Self { Self { name, - type_, + type_: &type_, suggestions_provider: None, base: BaseArgumentBuilder::new(name, type_), } } - pub fn argument(name: String, type_: dyn ArgumentType) -> Self { + pub fn argument(name: String, type_: T) -> Self { Self::new(name, type_) } - pub fn suggests(mut self, provider: dyn SuggestionProvider) -> Self { + pub fn suggests(mut self, provider: &dyn SuggestionProvider) -> Self { self.suggestions_provider = Some(provider); self } - pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { + pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { self.suggestions_provider.as_ref() } - pub fn get_type(&self) -> &dyn ArgumentType { - &self.type_ + pub fn get_type(&self) -> &T { + self.type_ } pub fn name(&self) -> &str { - &self.name + self.name } // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); @@ -96,7 +54,7 @@ impl RequiredArgumentBuilder { // } // return result; - pub fn build(self) -> ArgumentCommandNode { + pub fn build(self) -> ArgumentCommandNode<'a, S, T> { let result = ArgumentCommandNode { name: self.name, type_: &self.type_, diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index a76454b7..520c8a52 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -5,6 +5,6 @@ use crate::{ pub const SINGLE_SUCCESS: i32 = 1; -pub trait Command { - fn run(&self, context: &mut CommandContext) -> Result; +pub trait Command { + fn run(&self, context: &mut CommandContext) -> Result; } diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index 0e9b9036..d0351547 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -2,11 +2,12 @@ use crate::tree::root_command_node::RootCommandNode; /// The core command dispatcher, for registering, parsing, and executing commands. /// The `S` generic is a custom "source" type, such as a user or originator of a command -pub struct CommandDispatcher { - root: RootCommandNode, +#[derive(Default)] +pub struct CommandDispatcher<'a, S, T> { + root: RootCommandNode<'a, S, T>, } -impl CommandDispatcher { +impl CommandDispatcher<'_, S, T> { /// The string required to separate individual arguments in an input string /// /// See: [`ARGUMENT_SEPARATOR_CHAR`] @@ -22,4 +23,23 @@ impl CommandDispatcher { const USAGE_REQUIRED_OPEN: &'static str = "("; const USAGE_REQUIRED_CLOSE: &'static str = ")"; const USAGE_OR: &'static str = "|"; + + /// Create a new [`CommandDispatcher`] with the specified root node. + /// This is often useful to copy existing or pre-defined command trees. + /// # Example + /// ``` + /// use azalea_brigadier::{ + /// command_dispatcher::CommandDispatcher, + /// tree::root_command_node::RootCommandNode, + /// }; + /// + /// let mut dispatcher = CommandDispatcher::new(RootCommandNode::new()); + /// ``` + /// # Arguments + /// * `root` - the existing [`RootCommandNode`] to use as the basis for this tree + /// # Returns + /// A new [`CommandDispatcher`] with the specified root node. + fn new(root: RootCommandNode) -> Self { + Self { root } + } } diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 7a2189d9..36741906 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -3,27 +3,25 @@ use super::{ string_range::StringRange, }; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, - command::Command, - redirect_modifier::RedirectModifier, + arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; use std::collections::HashMap; -pub struct CommandContext { +pub struct CommandContext<'a, S, T> { source: S, input: String, - command: dyn Command, - arguments: HashMap>>, - root_node: dyn CommandNode, - nodes: Vec>, + command: &'a dyn Command, + arguments: HashMap>, + root_node: &'a dyn CommandNode, + nodes: Vec>, range: StringRange, - child: Option>, - modifier: Option>, + child: Option>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -impl CommandContext { +impl CommandContext<'_, S, T> { pub fn clone_for(&self, source: S) -> Self { if self.source == source { return self.clone(); @@ -42,11 +40,11 @@ impl CommandContext { } } - fn child(&self) -> &Option> { + fn child(&self) -> &Option> { &self.child } - fn last_child(&self) -> &CommandContext { + fn last_child(&self) -> &CommandContext { let mut result = self; while result.child.is_some() { result = result.child.as_ref().unwrap(); @@ -54,7 +52,7 @@ impl CommandContext { result } - fn command(&self) -> &dyn Command { + fn command(&self) -> &dyn Command { &self.command } diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 5766ea9a..878d7692 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -25,16 +25,16 @@ use super::{ // private boolean forks; #[derive(Clone)] -pub struct CommandContextBuilder { - arguments: HashMap>>, - root_node: dyn CommandNode, - nodes: Vec>, - dispatcher: CommandDispatcher, +pub struct CommandContextBuilder<'a, S, T> { + arguments: HashMap>, + root_node: &'a dyn CommandNode, + nodes: Vec>, + dispatcher: CommandDispatcher<'a, S, T>, source: S, - command: Box>, - child: Option>, + command: Box>, + child: Option>, range: StringRange, - modifier: Option>>, + modifier: Option>>, forks: bool, } @@ -45,15 +45,15 @@ pub struct CommandContextBuilder { // this.range = StringRange.at(start); // } -impl CommandContextBuilder { +impl CommandContextBuilder<'_, S, T> { pub fn new( - dispatcher: CommandDispatcher, + dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNode, + root_node: dyn CommandNode, start: usize, ) -> Self { Self { - root_node, + root_node: &root_node, dispatcher, source, range: StringRange::at(start), @@ -70,31 +70,25 @@ impl CommandContextBuilder { &self.source } - pub fn root_node(&self) -> &dyn CommandNode { + pub fn root_node(&self) -> &dyn CommandNode { &self.root_node } - pub fn with_argument( - mut self, - name: String, - argument: ParsedArgument>, - ) -> Self { + pub fn with_argument(mut self, name: String, argument: ParsedArgument) -> Self { self.arguments.insert(name, argument); self } - pub fn arguments( - &self, - ) -> &HashMap>> { + pub fn arguments(&self) -> &HashMap> { &self.arguments } - pub fn with_command(mut self, command: Box>) -> Self { + pub fn with_command(mut self, command: &dyn Command) -> Self { self.command = command; self } - pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { + pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { self.nodes.push(ParsedCommandNode::new(node, range)); self.range = StringRange::encompassing(&self.range, &range); self.modifier = node.redirect_modifier(); @@ -102,16 +96,16 @@ impl CommandContextBuilder { self } - pub fn with_child(mut self, child: CommandContextBuilder) -> Self { + pub fn with_child(mut self, child: CommandContextBuilder) -> Self { self.child = Some(child); self } - pub fn child(&self) -> Option<&CommandContextBuilder> { + pub fn child(&self) -> Option<&CommandContextBuilder> { self.child.as_ref() } - pub fn last_child(&self) -> Option<&CommandContextBuilder> { + pub fn last_child(&self) -> Option<&CommandContextBuilder> { let mut result = self; while let Some(child) = result.child() { result = child; @@ -119,15 +113,15 @@ impl CommandContextBuilder { Some(result) } - pub fn command(&self) -> &dyn Command { + pub fn command(&self) -> &dyn Command { &*self.command } - pub fn nodes(&self) -> &Vec> { + pub fn nodes(&self) -> &Vec> { &self.nodes } - pub fn build(self, input: &str) -> CommandContext { + pub fn build(self, input: &str) -> CommandContext { CommandContext { source: self.source, input, @@ -142,7 +136,7 @@ impl CommandContextBuilder { } } - pub fn dispatcher(&self) -> &CommandDispatcher { + pub fn dispatcher(&self) -> &CommandDispatcher { &self.dispatcher } @@ -150,7 +144,7 @@ impl CommandContextBuilder { &self.range } - pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { + pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { if self.range.start() <= cursor { if self.range.end() < cursor { if let Some(child) = self.child() { diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 77a47078..75c07784 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -1,10 +1,9 @@ -use std::marker::PhantomData; - use super::string_range::StringRange; #[derive(PartialEq, Eq, Hash)] pub struct ParsedArgument { range: StringRange, + // T is an item in an enum result: T, } diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index 98e99959..a006aa4b 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -2,17 +2,17 @@ use super::string_range::StringRange; use crate::tree::command_node::CommandNode; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ParsedCommandNode { - node: dyn CommandNode, +pub struct ParsedCommandNode<'a, S, T> { + node: &'a dyn CommandNode, range: StringRange, } -impl ParsedCommandNode { - fn new(node: dyn CommandNode, range: StringRange) -> Self { +impl ParsedCommandNode<'_, S, T> { + fn new(node: &dyn CommandNode, range: StringRange) -> Self { Self { node, range } } - fn node(&self) -> &dyn CommandNode { + fn node(&self) -> &dyn CommandNode { &self.node } diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index 540a5f23..42bc550e 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -1,6 +1,6 @@ use crate::tree::command_node::CommandNode; -pub struct SuggestionContext { - parent: dyn CommandNode, +pub struct SuggestionContext<'a, S, T> { + parent: &'a dyn CommandNode, start_pos: usize, } diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index cfefd120..fd2e1bf7 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -3,6 +3,6 @@ use crate::{ exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait RedirectModifier { - fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; +pub trait RedirectModifier { + fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; } diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs index dd63244d..95055fd8 100644 --- a/azalea-brigadier/src/single_redirect_modifier.rs +++ b/azalea-brigadier/src/single_redirect_modifier.rs @@ -3,6 +3,6 @@ use crate::{ exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait SingleRedirectModifier { - fn apply(&self, context: CommandContext) -> Result; +pub trait SingleRedirectModifier { + fn apply(&self, context: CommandContext) -> Result; } diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs index 3027d460..9720d3b9 100644 --- a/azalea-brigadier/src/suggestion/suggestion_provider.rs +++ b/azalea-brigadier/src/suggestion/suggestion_provider.rs @@ -5,10 +5,10 @@ use crate::{ use super::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}; -pub trait SuggestionProvider { +pub trait SuggestionProvider { fn suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &SuggestionsBuilder, ) -> Result; } diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 51add3d5..4d38b41f 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, + arguments::argument_type::ArgumentType, builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, @@ -21,25 +21,25 @@ const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ArgumentCommandNode { +pub struct ArgumentCommandNode<'a, S, T> { name: String, - type_: Box>, - custom_suggestions: dyn SuggestionProvider, + type_: &'a T, + custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode, + pub base: BaseCommandNode<'a, S, T>, } -impl ArgumentCommandNode { - fn get_type(&self) -> &dyn ArgumentType { +impl ArgumentCommandNode<'_, S, T> { + fn get_type(&self) -> &T { &self.type_ } - fn custom_suggestions(&self) -> &dyn SuggestionProvider { + fn custom_suggestions(&self) -> &dyn SuggestionProvider { &self.custom_suggestions } } -impl CommandNode for ArgumentCommandNode { +impl CommandNode for ArgumentCommandNode<'_, S, T> { fn name(&self) -> &str { &self.name } @@ -47,7 +47,7 @@ impl CommandNode for ArgumentCommandNode { fn parse( &self, reader: &mut StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { // final int start = reader.getCursor(); // final T result = type.parse(reader); @@ -68,7 +68,7 @@ impl CommandNode for ArgumentCommandNode { fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: &mut SuggestionsBuilder, ) -> Result { if self.custom_suggestions.is_none() { @@ -112,7 +112,7 @@ impl CommandNode for ArgumentCommandNode { } } -impl Display for ArgumentCommandNode { +impl Display for ArgumentCommandNode<'_, String, String> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "", self.name, self.type_) } diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index 0d9212aa..bcba9c03 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, + arguments::argument_type::ArgumentType, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -13,33 +13,33 @@ use crate::{ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; -pub struct BaseCommandNode { - children: HashMap>, - literals: HashMap>, - arguments: HashMap>>, - requirement: Option bool>, - redirect: Option>, - modifier: Option>, +pub struct BaseCommandNode<'a, S, T> { + children: HashMap>, + literals: HashMap>, + arguments: HashMap>, + requirement: Option<&'a dyn Fn(&S) -> bool>, + redirect: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, - command: Option>, + command: Option<&'a dyn Command>, } -impl BaseCommandNode {} +impl BaseCommandNode<'_, S, T> {} -pub trait CommandNode { +pub trait CommandNode { fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException>; fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result; fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> dyn ArgumentBuilder; + fn create_builder(&self) -> dyn ArgumentBuilder; fn get_examples(&self) -> Vec; } diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index bb0e613c..fe933669 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,8 +1,11 @@ use crate::{ + builder::literal_argument_builder::LiteralArgumentBuilder, + command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, + redirect_modifier::RedirectModifier, string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; @@ -10,14 +13,23 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct LiteralCommandNode { +pub struct LiteralCommandNode<'a, S, T> { literal: String, literal_lowercase: String, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode, + pub base: BaseCommandNode<'a, S, T>, } -impl LiteralCommandNode { +impl<'a, S, T> LiteralCommandNode<'a, S, T> { + pub fn new(literal: String, base: BaseCommandNode) -> Self { + let literal_lowercase = literal.to_lowercase(); + Self { + literal, + literal_lowercase, + base, + } + } + pub fn literal(&self) -> &String { &self.literal } @@ -39,7 +51,7 @@ impl LiteralCommandNode { } } -impl CommandNode for LiteralCommandNode { +impl CommandNode for LiteralCommandNode<'_, S, T> { fn name(&self) -> &str { &self.literal } @@ -47,7 +59,7 @@ impl CommandNode for LiteralCommandNode { fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { let start = reader.get_cursor(); let end = self.parse(reader); @@ -63,7 +75,7 @@ impl CommandNode for LiteralCommandNode { fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { if self @@ -84,7 +96,7 @@ impl CommandNode for LiteralCommandNode { self.literal } - fn create_builder(&self) -> LiteralArgumentBuilder { + fn create_builder(&self) -> LiteralArgumentBuilder { let builder = LiteralArgumentBuilder::literal(self.literal()); builder.requires(self.requirement()); builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index 004ab6a8..25a5a4b2 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -12,12 +12,12 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct RootCommandNode { +pub struct RootCommandNode<'a, S, T> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode, + pub base: BaseCommandNode<'a, S, T>, } -impl CommandNode for RootCommandNode { +impl CommandNode for RootCommandNode<'_, S, T> { fn name(&self) -> &str { "" } @@ -25,13 +25,13 @@ impl CommandNode for RootCommandNode { fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { } fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { Suggestions::empty() @@ -54,7 +54,7 @@ impl CommandNode for RootCommandNode { } } -impl Display for RootCommandNode<()> { +impl Display for RootCommandNode<'_, S, T> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") } From cc4fe62fc82842e0bde628437a45d55c6a82f1f3 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 11 Jan 2022 00:01:47 -0600 Subject: [PATCH 21/83] adfsfasdfaSDQAWERTERYTUYghyubnjnrdfxcv etgvbhy0ujn- --- .../src/arguments/argument_type.rs | 6 +- .../src/arguments/bool_argument_type.rs | 6 +- .../src/context/command_context.rs | 11 +- .../src/context/command_context_builder.rs | 2 +- .../src/context/parsed_argument.rs | 2 +- .../src/context/parsed_command_node.rs | 9 +- azalea-brigadier/src/context/string_range.rs | 2 +- azalea-brigadier/src/message.rs | 1 + azalea-brigadier/src/suggestion/suggestion.rs | 90 ++++++++++++++ .../src/suggestion/suggestions.rs | 58 +++++++++ .../src/suggestion/suggestions_builder.rs | 115 +++++++++++++++++- .../src/tree/argument_command_node.rs | 23 ++-- azalea-brigadier/src/tree/command_node.rs | 2 +- .../src/tree/root_command_node.rs | 1 - 14 files changed, 299 insertions(+), 29 deletions(-) diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 107b1cbf..46026735 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -7,7 +7,9 @@ use crate::{ }; pub trait Types { - fn bool(value: bool) -> Self; + fn bool(value: bool) -> Self + where + Self: Sized; } /* @@ -28,7 +30,7 @@ enum BrigadierTypes { pub trait ArgumentType where Self: Sized, - T: Types, + T: Types + ?Sized, { // T parse(StringReader reader) throws CommandSyntaxException; diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index dc2c6896..1237caa0 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -33,13 +33,13 @@ where // builder.suggest("false"); // } // return builder.buildFuture(); - if "true".starts_with(builder.get_remaining_lower_case()) { + if "true".starts_with(builder.remaining_lowercase()) { builder.suggest("true"); } - if "false".starts_with(builder.get_remaining_lower_case()) { + if "false".starts_with(builder.remaining_lowercase()) { builder.suggest("false"); } - Ok(builder.build_future()) + Ok(builder.build()) } fn get_examples(&self) -> Vec { diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 36741906..68144a40 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -14,17 +14,20 @@ pub struct CommandContext<'a, S, T> { command: &'a dyn Command, arguments: HashMap>, root_node: &'a dyn CommandNode, - nodes: Vec>, + nodes: Vec>, range: StringRange, - child: Option>, + child: Option<&'a CommandContext<'a, S, T>>, modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -impl CommandContext<'_, S, T> { +impl CommandContext<'_, S, T> +where + S: PartialEq, +{ pub fn clone_for(&self, source: S) -> Self { if self.source == source { - return self.clone(); + return *self; } Self { source, diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 878d7692..88e26343 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -28,7 +28,7 @@ use super::{ pub struct CommandContextBuilder<'a, S, T> { arguments: HashMap>, root_node: &'a dyn CommandNode, - nodes: Vec>, + nodes: Vec>, dispatcher: CommandDispatcher<'a, S, T>, source: S, command: Box>, diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 75c07784..447a1223 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -1,6 +1,6 @@ use super::string_range::StringRange; -#[derive(PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash, Clone)] pub struct ParsedArgument { range: StringRange, // T is an item in an enum diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index a006aa4b..14168a06 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -1,14 +1,13 @@ use super::string_range::StringRange; use crate::tree::command_node::CommandNode; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ParsedCommandNode<'a, S, T> { - node: &'a dyn CommandNode, +pub struct ParsedCommandNode { + node: Box>, range: StringRange, } -impl ParsedCommandNode<'_, S, T> { - fn new(node: &dyn CommandNode, range: StringRange) -> Self { +impl ParsedCommandNode { + fn new(node: dyn CommandNode, range: StringRange) -> Self { Self { node, range } } diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs index d775ab68..87098a1a 100644 --- a/azalea-brigadier/src/context/string_range.rs +++ b/azalea-brigadier/src/context/string_range.rs @@ -1,6 +1,6 @@ use std::cmp; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StringRange { start: usize, end: usize, diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index 71d0b178..42894d0e 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -1,5 +1,6 @@ use std::rc::Rc; +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Message(Rc); impl Message { diff --git a/azalea-brigadier/src/suggestion/suggestion.rs b/azalea-brigadier/src/suggestion/suggestion.rs index e69de29b..4cbed7be 100644 --- a/azalea-brigadier/src/suggestion/suggestion.rs +++ b/azalea-brigadier/src/suggestion/suggestion.rs @@ -0,0 +1,90 @@ +use std::cmp; + +use crate::{context::string_range::StringRange, message::Message}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Suggestion { + range: StringRange, + text: String, + tooltip: Option, +} + +impl Suggestion { + pub fn new(range: StringRange, text: String) -> Suggestion { + Suggestion { + range, + text, + tooltip: None, + } + } + + pub fn new_with_tooltip(range: StringRange, text: String, tooltip: Message) -> Suggestion { + Suggestion { + range, + text, + tooltip: Some(tooltip), + } + } + + pub fn range(&self) -> &StringRange { + &self.range + } + + pub fn text(&self) -> &String { + &self.text + } + + pub fn tooltip(&self) -> Option<&Message> { + self.tooltip.as_ref() + } + + pub fn apply(&self, input: &str) -> String { + if self.range.start() == 0 && self.range.end() == input.len() { + return self.text.clone(); + } + let mut result = String::new(); + if self.range.start() > 0 { + result.push_str(&input[0..self.range.start()]); + } + result.push_str(&self.text); + if self.range.end() < input.len() { + result.push_str(&input[self.range.end()..]); + } + result + } + + pub fn expand(&self, command: &str, range: StringRange) -> Suggestion { + if range == self.range { + return self.clone(); + } + let mut result = String::new(); + if range.start() < self.range.start() { + result.push_str(&command[range.start()..self.range.start()]); + } + result.push_str(&self.text); + if range.end() > self.range.end() { + result.push_str(&command[self.range.end()..range.end()]); + } + Suggestion { + range, + text: result, + tooltip: self.tooltip.clone(), + } + } + + pub fn compare_ignore_case(&self, b: &Suggestion) -> cmp::Ordering { + self.text.to_lowercase().cmp(&b.text.to_lowercase()) + } +} + +impl PartialOrd for Suggestion { + fn partial_cmp(&self, other: &Suggestion) -> Option { + Some(self.text.cmp(&other.text)) + } +} + +impl Ord for Suggestion { + fn cmp(&self, other: &Suggestion) -> cmp::Ordering { + self.text.cmp(&other.text) + } +} diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 354fc418..18572d20 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -1 +1,59 @@ +use std::cmp; + +use crate::{context::string_range::StringRange, message::Message}; + pub struct Suggestions {} + +// #[cfg(test)] +// mod tests { +// use crate::suggestion::suggestion::Suggestion; + +// use super::*; + +// #[test] +// fn merge_empty() { +// let merged = Suggestions::merge("foo b", vec![]); +// assert_eq!(merged.is_empty(), true); +// } + +// #[test] +// fn merge_single() { +// let suggestions = Suggestions::new(StringRange::at(5), "ar".to_string()); +// let merged = Suggestions::merge("foo b", vec![suggestions]); +// assert_eq!(merged, suggestions); +// } + +// #[test] +// fn merge_multiple() { +// let a = Suggestions::new( +// StringRange::at(5), +// vec![ +// Suggestion::new(StringRange::at(5), "ar".to_string()), +// Suggestion::new(StringRange::at(5), "az".to_string()), +// Suggestion::new(StringRange::at(5), "Az".to_string()), +// ], +// ); +// let b = Suggestions::new( +// StringRange::between(4, 5), +// vec![ +// Suggestion::new(StringRange::between(4, 5), "foo".to_string()), +// Suggestion::new(StringRange::between(4, 5), "qux".to_string()), +// Suggestion::new(StringRange::between(4, 5), "apple".to_string()), +// Suggestion::new(StringRange::between(4, 5), "Bar".to_string()), +// ], +// ); +// let merged = Suggestions::merge("foo b", vec![a, b]); +// assert_eq!( +// merged.get_list(), +// vec![ +// Suggestion::new(StringRange::between(4, 5), "apple".to_string()), +// Suggestion::new(StringRange::between(4, 5), "bar".to_string()), +// Suggestion::new(StringRange::between(4, 5), "Bar".to_string()), +// Suggestion::new(StringRange::between(4, 5), "baz".to_string()), +// Suggestion::new(StringRange::between(4, 5), "bAz".to_string()), +// Suggestion::new(StringRange::between(4, 5), "foo".to_string()), +// Suggestion::new(StringRange::between(4, 5), "qux".to_string()), +// ] +// ); +// } +// } diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs index 6960f52b..7853a3a2 100644 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -1 +1,114 @@ -pub struct SuggestionsBuilder {} +use crate::context::string_range::StringRange; + +use super::{suggestion::Suggestion, suggestions::Suggestions}; + +pub struct SuggestionsBuilder { + input: String, + input_lowercase: String, + start: usize, + remaining: String, + remaining_lowercase: String, + result: Vec, +} + +impl SuggestionsBuilder { + pub fn new_with_lowercase( + input: String, + input_lowercase: String, + start: usize, + ) -> SuggestionsBuilder { + SuggestionsBuilder { + input, + input_lowercase, + start, + remaining: input.get(start..).unwrap().to_string(), + remaining_lowercase: input_lowercase.get(start..).unwrap().to_string(), + result: Vec::new(), + } + } + + pub fn new(input: String, start: usize) -> SuggestionsBuilder { + SuggestionsBuilder::new_with_lowercase(input, input.to_lowercase(), start) + } + + pub fn input(&self) -> &str { + &self.input + } + + pub fn start(&self) -> usize { + self.start + } + + pub fn remaining(&self) -> &str { + &self.remaining + } + + pub fn remaining_lowercase(&self) -> &str { + &self.remaining_lowercase + } + + pub fn build(&self) -> Suggestions { + Suggestions::create(self.input(), self.result) + } + + pub fn suggest(&mut self, text: &str) -> &mut SuggestionsBuilder { + if text == self.remaining { + return self; + } + self.result.push(Suggestion::new( + StringRange::between(self.start, self.input.len()), + text, + )); + self + } + + pub fn suggest_with_tooltip(&mut self, text: &str, tooltip: &str) -> &mut SuggestionsBuilder { + if text == self.remaining { + return self; + } + self.result.push(Suggestion::new_with_tooltip( + StringRange::between(self.start, self.input.len()), + text, + tooltip, + )); + self + } + + pub fn suggest_with_value(&mut self, value: i32) -> &mut SuggestionsBuilder { + self.result.push(IntegerSuggestion::new( + StringRange::between(self.start, self.input.len()), + value, + )); + self + } + + pub fn suggest_with_value_and_tooltip( + &mut self, + value: i32, + tooltip: &str, + ) -> &mut SuggestionsBuilder { + self.result.push(IntegerSuggestion::new_with_tooltip( + StringRange::between(self.start, self.input.len()), + value, + tooltip, + )); + self + } + + pub fn add(&mut self, other: &SuggestionsBuilder) -> &mut SuggestionsBuilder { + self.result.extend(other.result.iter().cloned()); + self + } + + pub fn create_offset(&self, start: usize) -> SuggestionsBuilder { + SuggestionsBuilder::new_with_lowercase( + self.input.clone(), + self.input_lowercase.clone(), + start, + ) + } + + pub fn restart(&self) -> SuggestionsBuilder { + self.create_offset(self.start) + } +} diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 4d38b41f..647b6c35 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,13 +1,14 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::ArgumentType, + arguments::argument_type::{ArgumentType, Types}, builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, parsed_argument::ParsedArgument, }, exceptions::command_syntax_exception::CommandSyntaxException, + immutable_string_reader::ImmutableStringReader, string_reader::StringReader, suggestion::{ suggestion_provider::SuggestionProvider, suggestions::Suggestions, @@ -20,11 +21,15 @@ use super::command_node::{BaseCommandNode, CommandNode}; const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ArgumentCommandNode<'a, S, T> { +pub struct ArgumentCommandNode<'a, S, T> +where + // each argument command node has its own different type + T: ArgumentType, +{ name: String, type_: &'a T, - custom_suggestions: &'a dyn SuggestionProvider, + custom_suggestions: Option<&'a dyn SuggestionProvider>, + // custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, } @@ -34,12 +39,12 @@ impl ArgumentCommandNode<'_, S, T> { &self.type_ } - fn custom_suggestions(&self) -> &dyn SuggestionProvider { - &self.custom_suggestions + fn custom_suggestions(&self) -> Option<&dyn SuggestionProvider> { + self.custom_suggestions } } -impl CommandNode for ArgumentCommandNode<'_, S, T> { +impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> { fn name(&self) -> &str { &self.name } @@ -56,7 +61,7 @@ impl CommandNode for ArgumentCommandNode<'_, S, T> { // contextBuilder.withArgument(name, parsed); // contextBuilder.withNode(this, parsed.getRange()); - let start = reader.get_cursor(); + let start = reader.cursor(); let result = self.get_type().parse(reader)?; let parsed = ParsedArgument::new(start, reader.get_cursor(), result); @@ -112,7 +117,7 @@ impl CommandNode for ArgumentCommandNode<'_, S, T> { } } -impl Display for ArgumentCommandNode<'_, String, String> { +impl Display for ArgumentCommandNode<'_, (), (), ()> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "", self.name, self.type_) } diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index bcba9c03..8e262f0b 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -31,7 +31,7 @@ pub trait CommandNode { fn usage_text(&self) -> &str; fn parse( &self, - reader: StringReader, + reader: &mut StringReader, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException>; fn list_suggestions( diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index 25a5a4b2..36787340 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -11,7 +11,6 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] pub struct RootCommandNode<'a, S, T> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, From 270507736af57aae6801dc9eb3c3132139d0d07b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 12 Jan 2022 00:40:43 +0000 Subject: [PATCH 22/83] a --- Cargo.lock | 28 ++++++ azalea-brigadier/Cargo.toml | 1 + azalea-brigadier/README.md | 1 + .../src/arguments/argument_type.rs | 28 ++++-- .../src/arguments/bool_argument_type.rs | 1 + .../src/builder/argument_builder.rs | 8 +- .../src/builder/literal_argument_builder.rs | 15 +++- .../src/builder/required_argument_builder.rs | 11 ++- azalea-brigadier/src/command.rs | 4 +- azalea-brigadier/src/command_dispatcher.rs | 17 +++- .../src/context/command_context_builder.rs | 16 +++- .../src/context/parsed_command_node.rs | 9 ++ azalea-brigadier/src/redirect_modifier.rs | 5 +- .../src/suggestion/integer_suggestion.rs | 1 + .../src/suggestion/suggestions.rs | 86 ++++++++++++++++++- .../src/suggestion/suggestions_builder.rs | 4 +- .../src/tree/argument_command_node.rs | 17 +++- azalea-brigadier/src/tree/command_node.rs | 59 +++++++++++-- .../src/tree/literal_command_node.rs | 20 ++++- .../src/tree/root_command_node.rs | 19 +++- 20 files changed, 307 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0eeb6520..a0ca81fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,7 @@ dependencies = [ name = "azalea-brigadier" version = "0.1.0" dependencies = [ + "dyn-clonable", "lazy_static", ] @@ -336,6 +337,33 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + [[package]] name = "either" version = "1.6.1" diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index 3694a4b7..4e8968d7 100644 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -7,3 +7,4 @@ version = "0.1.0" [dependencies] lazy_static = "^1.4" +dyn-clonable = "^0.9" diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md index 92c0d27e..df69b5c0 100644 --- a/azalea-brigadier/README.md +++ b/azalea-brigadier/README.md @@ -1,3 +1,4 @@ # Azalea Brigadier A Rustier port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library. + diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 46026735..890cdea0 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -5,11 +5,19 @@ use crate::{ string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; +use dyn_clonable::*; -pub trait Types { +#[clonable] +// This should be applied to an Enum +pub trait Types: Clone { fn bool(value: bool) -> Self where Self: Sized; + + /// Get the less specific ArgumentType from this enum + fn inner(&self) -> Box> + where + Self: Sized; } /* @@ -25,12 +33,21 @@ enum BrigadierTypes { Entity(EntityArgumentType) } + +impl Types for BrigadierTypes { + fn inner(&self) -> dyn ArgumentType { + match self { + Bool(t) => t, + Entity(t) => t + } + } +} */ -pub trait ArgumentType +#[clonable] +pub trait ArgumentType: Clone where - Self: Sized, - T: Types + ?Sized, + T: Types, { // T parse(StringReader reader) throws CommandSyntaxException; @@ -42,7 +59,7 @@ where // return Collections.emptyList(); // } - fn parse(&self, reader: &mut StringReader) -> Result; + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; fn list_suggestions( &self, @@ -50,6 +67,7 @@ where builder: &mut SuggestionsBuilder, ) -> Result where + Self: Sized, S: Sized, T: Sized; diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index 1237caa0..b04488c1 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -7,6 +7,7 @@ use crate::{ use super::argument_type::{ArgumentType, Types}; +#[derive(Clone)] pub struct BoolArgumentType {} impl ArgumentType for BoolArgumentType diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index bd2a2c15..0360b05a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,4 +1,5 @@ use crate::{ + arguments::argument_type::{ArgumentType, Types}, command::Command, redirect_modifier::RedirectModifier, single_redirect_modifier::SingleRedirectModifier, @@ -8,7 +9,7 @@ use crate::{ pub struct BaseArgumentBuilder<'a, S, T> where S: Sized, - T: Sized, + T: Sized + ArgumentType, { arguments: RootCommandNode<'a, S, T>, command: Option<&'a dyn Command>, @@ -22,7 +23,10 @@ pub trait ArgumentBuilder { fn build(self) -> dyn CommandNode; } -impl BaseArgumentBuilder<'_, S, T> { +impl BaseArgumentBuilder<'_, S, T> +where + T: ArgumentType, +{ pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index cf9f1ee9..a4cb3f84 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,14 +1,23 @@ -use crate::tree::literal_command_node::LiteralCommandNode; +use crate::{ + arguments::argument_type::{ArgumentType, Types}, + tree::literal_command_node::LiteralCommandNode, +}; use super::argument_builder::BaseArgumentBuilder; -pub struct LiteralArgumentBuilder<'a, S, T> { +pub struct LiteralArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ literal: String, pub base: BaseArgumentBuilder<'a, S, T>, } -impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> { +impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ pub fn new(literal: String) -> Self { Self { literal, diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 6f6fa8eb..b5f99828 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,11 +1,15 @@ use crate::{ + arguments::argument_type::{ArgumentType, Types}, suggestion::suggestion_provider::SuggestionProvider, tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, }; use super::argument_builder::BaseArgumentBuilder; -pub struct RequiredArgumentBuilder<'a, S, T> { +pub struct RequiredArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ // private final String name; // private final ArgumentType type; // private SuggestionProvider suggestionsProvider = null; @@ -16,7 +20,10 @@ pub struct RequiredArgumentBuilder<'a, S, T> { pub base: BaseArgumentBuilder<'a, S, T>, } -impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> { +impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ pub fn new(name: String, type_: T) -> Self { Self { name, diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index 520c8a52..dcbf3ffd 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -2,9 +2,11 @@ use crate::{ context::command_context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, }; +use dyn_clonable::*; pub const SINGLE_SUCCESS: i32 = 1; -pub trait Command { +#[clonable] +pub trait Command: Clone { fn run(&self, context: &mut CommandContext) -> Result; } diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index d0351547..f2fc7528 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,13 +1,22 @@ -use crate::tree::root_command_node::RootCommandNode; +use crate::{ + arguments::argument_type::{ArgumentType, Types}, + tree::root_command_node::RootCommandNode, +}; /// The core command dispatcher, for registering, parsing, and executing commands. /// The `S` generic is a custom "source" type, such as a user or originator of a command -#[derive(Default)] -pub struct CommandDispatcher<'a, S, T> { +#[derive(Default, Clone)] +pub struct CommandDispatcher<'a, S, T> +where + T: ArgumentType, +{ root: RootCommandNode<'a, S, T>, } -impl CommandDispatcher<'_, S, T> { +impl CommandDispatcher<'_, S, T> +where + T: ArgumentType, +{ /// The string required to separate individual arguments in an input string /// /// See: [`ARGUMENT_SEPARATOR_CHAR`] diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 88e26343..639a97ee 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::ArgumentType, command::Command, - command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, + arguments::argument_type::{ArgumentType, Types}, + command::Command, + command_dispatcher::CommandDispatcher, + redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; @@ -25,7 +27,10 @@ use super::{ // private boolean forks; #[derive(Clone)] -pub struct CommandContextBuilder<'a, S, T> { +pub struct CommandContextBuilder<'a, S, T> +where + T: ArgumentType, +{ arguments: HashMap>, root_node: &'a dyn CommandNode, nodes: Vec>, @@ -45,7 +50,10 @@ pub struct CommandContextBuilder<'a, S, T> { // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S, T> { +impl CommandContextBuilder<'_, S, T> +where + T: ArgumentType, +{ pub fn new( dispatcher: CommandDispatcher, source: S, diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index 14168a06..c0be355c 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -19,3 +19,12 @@ impl ParsedCommandNode { &self.range } } + +impl Clone for ParsedCommandNode { + fn clone_from(&mut self, source: &Self) { + Self { + node: self.node.clone(), + range: self.range.clone(), + } + } +} diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index fd2e1bf7..fdb0a080 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -1,8 +1,11 @@ +use dyn_clonable::*; + use crate::{ context::command_context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait RedirectModifier { +#[clonable] +pub trait RedirectModifier: Clone { fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; } diff --git a/azalea-brigadier/src/suggestion/integer_suggestion.rs b/azalea-brigadier/src/suggestion/integer_suggestion.rs index e69de29b..acee2329 100644 --- a/azalea-brigadier/src/suggestion/integer_suggestion.rs +++ b/azalea-brigadier/src/suggestion/integer_suggestion.rs @@ -0,0 +1 @@ +pub struct IntegerSuggestion {} diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 18572d20..778c5de8 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -1,8 +1,90 @@ -use std::cmp; +use std::{cmp, collections::HashSet}; use crate::{context::string_range::StringRange, message::Message}; -pub struct Suggestions {} +use super::suggestion::Suggestion; + +#[derive(PartialEq, Eq, Hash, Default)] +pub struct Suggestions { + range: StringRange, + suggestions: Vec, +} + +impl Suggestions { + fn range(&self) -> &StringRange { + &self.range + } + + fn list(&self) -> &Vec { + &self.suggestions + } + + fn is_empty(&self) -> bool { + self.suggestions.is_empty() + } + + fn merge(command: &str, input: &Vec) { + if input.is_empty() { + return Self::default(); + } else if input.len() == 1 { + return input.iter().next(); + } + let texts = HashSet::new(); + for suggestions in input { + texts.extend(suggestions.list()) + } + Self::new(command, texts) + } + + // public static Suggestions create(final String command, final Collection suggestions) { + // if (suggestions.isEmpty()) { + // return EMPTY; + // } + // int start = Integer.MAX_VALUE; + // int end = Integer.MIN_VALUE; + // for (final Suggestion suggestion : suggestions) { + // start = Math.min(suggestion.getRange().getStart(), start); + // end = Math.max(suggestion.getRange().getEnd(), end); + // } + // final StringRange range = new StringRange(start, end); + // final Set texts = new HashSet<>(); + // for (final Suggestion suggestion : suggestions) { + // texts.add(suggestion.expand(command, range)); + // } + // final List sorted = new ArrayList<>(texts); + // sorted.sort((a, b) -> a.compareToIgnoreCase(b)); + // return new Suggestions(range, sorted); + pub fn new(command: String, suggestions: Vec) -> Self { + if suggestions.is_empty() { + return Self::default(); + } + let mut start = usize::MAX; + let mut end = usize::MIN; + for suggestion in suggestions { + let start = cmp::min(suggestion.range().start(), start); + let end = cmp::max(suggestion.range().end(), end); + } + let range = StringRange::new(start, end); + let texts = HashSet::new(); + for suggestion in suggestions { + texts.insert(suggestion.expand(command, range)); + } + let sorted = texts.sort_by(|a, b| a.compare_ignore_case(b)); + Suggestions { + range, + suggestions: sorted, + } + } +} + +impl Default for Suggestions { + fn default() -> Self { + Self { + range: StringRange::at(0), + suggestions: vec![], + } + } +} // #[cfg(test)] // mod tests { diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs index 7853a3a2..bc8f6f5d 100644 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -1,6 +1,8 @@ use crate::context::string_range::StringRange; -use super::{suggestion::Suggestion, suggestions::Suggestions}; +use super::{ + integer_suggestion::IntegerSuggestion, suggestion::Suggestion, suggestions::Suggestions, +}; pub struct SuggestionsBuilder { input: String, diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 647b6c35..3fc1bb50 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -21,6 +21,7 @@ use super::command_node::{BaseCommandNode, CommandNode}; const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; +#[derive(Clone)] pub struct ArgumentCommandNode<'a, S, T> where // each argument command node has its own different type @@ -34,7 +35,10 @@ where pub base: BaseCommandNode<'a, S, T>, } -impl ArgumentCommandNode<'_, S, T> { +impl ArgumentCommandNode<'_, S, T> +where + T: ArgumentType, +{ fn get_type(&self) -> &T { &self.type_ } @@ -44,7 +48,11 @@ impl ArgumentCommandNode<'_, S, T> { } } -impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> { +impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> +where + T: ArgumentType + Clone, + S: Clone, +{ fn name(&self) -> &str { &self.name } @@ -117,7 +125,10 @@ impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> { } } -impl Display for ArgumentCommandNode<'_, (), (), ()> { +impl Display for ArgumentCommandNode<'_, S, T> +where + T: ArgumentType, +{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "", self.name, self.type_) } diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index 8e262f0b..f3be1597 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,7 +1,6 @@ -use std::collections::HashMap; - +use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; use crate::{ - arguments::argument_type::ArgumentType, + arguments::argument_type::{ArgumentType, Types}, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -10,10 +9,14 @@ use crate::{ string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; +use dyn_clonable::*; +use std::{collections::HashMap, fmt::Debug}; -use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; - -pub struct BaseCommandNode<'a, S, T> { +#[derive(Default)] +pub struct BaseCommandNode<'a, S, T> +where + T: ArgumentType, +{ children: HashMap>, literals: HashMap>, arguments: HashMap>, @@ -24,9 +27,49 @@ pub struct BaseCommandNode<'a, S, T> { command: Option<&'a dyn Command>, } -impl BaseCommandNode<'_, S, T> {} +impl BaseCommandNode<'_, S, T> where T: ArgumentType {} -pub trait CommandNode { +impl Clone for BaseCommandNode<'_, S, T> +where + T: ArgumentType, +{ + fn clone(&self) -> Self { + Self { + children: self.children.clone(), + literals: self.literals.clone(), + arguments: self.arguments.clone(), + requirement: self.requirement.clone(), + redirect: self.redirect.clone(), + modifier: self.modifier.clone(), + forks: self.forks.clone(), + command: self.command.clone(), + } + } +} + +impl Debug for BaseCommandNode<'_, S, T> +where + T: ArgumentType, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BaseCommandNode") + .field("children", &self.children) + .field("literals", &self.literals) + .field("arguments", &self.arguments) + .field("requirement", &self.requirement) + .field("redirect", &self.redirect) + .field("modifier", &self.modifier) + .field("forks", &self.forks) + .field("command", &self.command) + .finish() + } +} + +#[clonable] +pub trait CommandNode: Clone +where + T: ArgumentType, +{ fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index fe933669..021a3ea6 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,4 +1,5 @@ use crate::{ + arguments::argument_type::{ArgumentType, Types}, builder::literal_argument_builder::LiteralArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -12,15 +13,22 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct LiteralCommandNode<'a, S, T> { +#[derive(Debug, Clone)] +pub struct LiteralCommandNode<'a, S, T> +where + // each argument command node has its own different type + T: ArgumentType, +{ literal: String, literal_lowercase: String, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, } -impl<'a, S, T> LiteralCommandNode<'a, S, T> { +impl<'a, S, T> LiteralCommandNode<'a, S, T> +where + T: ArgumentType, +{ pub fn new(literal: String, base: BaseCommandNode) -> Self { let literal_lowercase = literal.to_lowercase(); Self { @@ -51,7 +59,11 @@ impl<'a, S, T> LiteralCommandNode<'a, S, T> { } } -impl CommandNode for LiteralCommandNode<'_, S, T> { +impl CommandNode for LiteralCommandNode<'_, S, T> +where + T: ArgumentType + Clone, + S: Clone, +{ fn name(&self) -> &str { &self.literal } diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index 36787340..ded5fa77 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -1,6 +1,7 @@ use std::fmt::{Display, Formatter}; use crate::{ + arguments::argument_type::{ArgumentType, Types}, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, @@ -11,12 +12,21 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; -pub struct RootCommandNode<'a, S, T> { +#[derive(Clone, Default)] +pub struct RootCommandNode<'a, S, T> +where + // each argument command node has its own different type + T: ArgumentType, +{ // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, } -impl CommandNode for RootCommandNode<'_, S, T> { +impl CommandNode for RootCommandNode<'_, S, T> +where + T: ArgumentType + Clone, + S: Clone, +{ fn name(&self) -> &str { "" } @@ -53,7 +63,10 @@ impl CommandNode for RootCommandNode<'_, S, T> { } } -impl Display for RootCommandNode<'_, S, T> { +impl Display for RootCommandNode<'_, S, T> +where + T: ArgumentType, +{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") } From eb111be1f107696939b994f5de6e060cf972a732 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 13 Jan 2022 00:43:09 +0000 Subject: [PATCH 23/83] a --- azalea-brigadier/src/ambiguity_consumer.rs | 1 + .../src/arguments/argument_type.rs | 26 ++------ .../src/arguments/bool_argument_type.rs | 18 +++--- .../src/arguments/double_argument_type.rs | 1 + .../src/arguments/float_argument_type.rs | 1 + .../src/arguments/integer_argument_type.rs | 1 + .../src/arguments/long_argument_type.rs | 1 + .../src/arguments/string_argument_type.rs | 1 + .../src/builder/argument_builder.rs | 59 +++++++++--------- .../src/builder/literal_argument_builder.rs | 17 ++--- .../src/builder/required_argument_builder.rs | 33 +++++----- azalea-brigadier/src/command.rs | 4 +- azalea-brigadier/src/command_dispatcher.rs | 19 ++---- .../src/context/command_context.rs | 24 +++---- .../src/context/command_context_builder.rs | 62 ++++++++----------- .../src/context/parsed_argument.rs | 3 +- .../src/context/parsed_command_node.rs | 12 ++-- .../src/context/suggestion_context.rs | 4 +- .../exceptions/builtin_exception_provider.rs | 1 + azalea-brigadier/src/literal_message.rs | 1 + azalea-brigadier/src/parse_results.rs | 1 + azalea-brigadier/src/redirect_modifier.rs | 4 +- azalea-brigadier/src/result_consumer.rs | 1 + .../src/single_redirect_modifier.rs | 4 +- .../src/suggestion/suggestion_provider.rs | 4 +- .../src/suggestion/suggestions.rs | 2 +- .../src/tree/argument_command_node.rs | 48 ++++++-------- azalea-brigadier/src/tree/command_node.rs | 44 +++++-------- .../src/tree/literal_command_node.rs | 26 +++----- .../src/tree/root_command_node.rs | 22 +++---- .../tests/command_dispatcher_test.rs | 1 + .../tests/command_dispatcher_usages_test.rs | 1 + .../tests/command_suggestions_test.rs | 1 + 33 files changed, 188 insertions(+), 260 deletions(-) diff --git a/azalea-brigadier/src/ambiguity_consumer.rs b/azalea-brigadier/src/ambiguity_consumer.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/ambiguity_consumer.rs +++ b/azalea-brigadier/src/ambiguity_consumer.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 890cdea0..3d1b1168 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -7,19 +7,6 @@ use crate::{ }; use dyn_clonable::*; -#[clonable] -// This should be applied to an Enum -pub trait Types: Clone { - fn bool(value: bool) -> Self - where - Self: Sized; - - /// Get the less specific ArgumentType from this enum - fn inner(&self) -> Box> - where - Self: Sized; -} - /* #[derive(Types)] enum BrigadierTypes { @@ -45,10 +32,8 @@ impl Types for BrigadierTypes { */ #[clonable] -pub trait ArgumentType: Clone -where - T: Types, -{ +pub trait ArgumentType: Clone { + type Into; // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -59,17 +44,16 @@ where // return Collections.emptyList(); // } - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; + fn parse(&self, reader: &mut StringReader) -> Result; fn list_suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &mut SuggestionsBuilder, ) -> Result where Self: Sized, - S: Sized, - T: Sized; + S: Sized; fn get_examples(&self) -> Vec; } diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index b04488c1..c06f40c1 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -5,27 +5,25 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -use super::argument_type::{ArgumentType, Types}; +use super::argument_type::ArgumentType; #[derive(Clone)] pub struct BoolArgumentType {} -impl ArgumentType for BoolArgumentType -where - T: Types, -{ - fn parse(&self, reader: &mut StringReader) -> Result { - Ok(T::bool(reader.read_boolean()?)) +impl ArgumentType for BoolArgumentType { + type Into = bool; + + fn parse(&self, reader: &mut StringReader) -> Result { + Ok(reader.read_boolean()?) } fn list_suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &mut SuggestionsBuilder, ) -> Result where S: Sized, - T: Sized, { // if ("true".startsWith(builder.getRemainingLowerCase())) { // builder.suggest("true"); @@ -55,7 +53,7 @@ impl BoolArgumentType { Self {} } - fn get_bool(context: CommandContext, name: String) { + fn get_bool(context: CommandContext, name: String) { context.get_argument::(name) } } diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/double_argument_type.rs +++ b/azalea-brigadier/src/arguments/double_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/float_argument_type.rs +++ b/azalea-brigadier/src/arguments/float_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/integer_argument_type.rs +++ b/azalea-brigadier/src/arguments/integer_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/long_argument_type.rs +++ b/azalea-brigadier/src/arguments/long_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/string_argument_type.rs +++ b/azalea-brigadier/src/arguments/string_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 0360b05a..6355456a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,33 +1,32 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, single_redirect_modifier::SingleRedirectModifier, tree::{command_node::CommandNode, root_command_node::RootCommandNode}, }; -pub struct BaseArgumentBuilder<'a, S, T> +pub struct BaseArgumentBuilder<'a, S> where S: Sized, - T: Sized + ArgumentType, { - arguments: RootCommandNode<'a, S, T>, - command: Option<&'a dyn Command>, + arguments: RootCommandNode<'a, S>, + command: Option<&'a dyn Command>, requirement: &'a dyn Fn(&S) -> bool, - target: Option<&'a dyn CommandNode>, - modifier: Option<&'a dyn RedirectModifier>, + target: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -pub trait ArgumentBuilder { - fn build(self) -> dyn CommandNode; +pub trait ArgumentBuilder +where + T: ArgumentBuilder, +{ + fn build(self) -> dyn CommandNode; } -impl BaseArgumentBuilder<'_, S, T> -where - T: ArgumentType, -{ - pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { +impl BaseArgumentBuilder<'_, S> { + pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut Self, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); } @@ -35,20 +34,20 @@ where Ok(self) } - pub fn arguments(&self) -> &Vec<&dyn CommandNode> { + pub fn arguments(&self) -> &Vec<&dyn CommandNode> { &self.arguments.get_children() } - pub fn executes(&mut self, command: dyn Command) -> &mut T { + pub fn executes(&mut self, command: dyn Command) -> &mut Self { self.command = command; self } - pub fn command(&self) -> dyn Command { + pub fn command(&self) -> dyn Command { self.command } - pub fn requires(&mut self, requirement: &dyn Fn(&S) -> bool) -> &mut T { + pub fn requires(&mut self, requirement: &dyn Fn(&S) -> bool) -> &mut Self { self.requirement = requirement; self } @@ -57,33 +56,33 @@ where self.requirement } - pub fn redirect(&mut self, target: &dyn CommandNode) -> &mut T { + pub fn redirect(&mut self, target: &dyn CommandNode) -> &mut Self { self.forward(target, None, false) } pub fn redirect_modifier( &mut self, - target: &dyn CommandNode, - modifier: &dyn SingleRedirectModifier, - ) -> &mut T { + target: &dyn CommandNode, + modifier: &dyn SingleRedirectModifier, + ) -> &mut Self { // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false); self.forward(target, modifier.map(|m| |o| vec![m.apply(o)]), false) } pub fn fork( &mut self, - target: &dyn CommandNode, - modifier: &dyn RedirectModifier, - ) -> &mut T { + target: &dyn CommandNode, + modifier: &dyn RedirectModifier, + ) -> &mut Self { self.forward(target, Some(modifier), true) } pub fn forward( &mut self, - target: &dyn CommandNode, - modifier: Option<&dyn RedirectModifier>, + target: &dyn CommandNode, + modifier: Option<&dyn RedirectModifier>, fork: bool, - ) -> Result<&mut T, String> { + ) -> Result<&mut Self, String> { if !self.arguments.get_children().is_empty() { return Err("Cannot forward a node with children".to_string()); } @@ -93,11 +92,11 @@ where Ok(self) } - pub fn get_redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNode> { self.target.as_ref() } - pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { self.modifier.as_ref() } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index a4cb3f84..8039dff0 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,23 +1,16 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, - tree::literal_command_node::LiteralCommandNode, + arguments::argument_type::ArgumentType, tree::literal_command_node::LiteralCommandNode, }; use super::argument_builder::BaseArgumentBuilder; -pub struct LiteralArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ +pub struct LiteralArgumentBuilder<'a, S> { literal: String, - pub base: BaseArgumentBuilder<'a, S, T>, + pub base: BaseArgumentBuilder<'a, S>, } -impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ +impl<'a, S> LiteralArgumentBuilder<'a, S> { pub fn new(literal: String) -> Self { Self { literal, @@ -29,7 +22,7 @@ where Self::new(name) } - pub fn build(self) -> LiteralCommandNode<'a, S, T> { + pub fn build(self) -> LiteralCommandNode<'a, S> { let result = LiteralCommandNode::new(self.literal, self.base); for argument in self.base.arguments { diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index b5f99828..29af7f6f 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,30 +1,25 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, suggestion::suggestion_provider::SuggestionProvider, tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, }; +use std::any::Any; use super::argument_builder::BaseArgumentBuilder; -pub struct RequiredArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ +pub struct RequiredArgumentBuilder<'a, S> { // private final String name; // private final ArgumentType type; // private SuggestionProvider suggestionsProvider = null; name: String, - type_: &'a T, - suggestions_provider: Option<&'a dyn SuggestionProvider>, + type_: Box>, + suggestions_provider: Option<&'a dyn SuggestionProvider>, - pub base: BaseArgumentBuilder<'a, S, T>, + pub base: BaseArgumentBuilder<'a, S>, } -impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ - pub fn new(name: String, type_: T) -> Self { +impl<'a, S> RequiredArgumentBuilder<'a, S> { + pub fn new(name: String, type_: dyn ArgumentType) -> Self { Self { name, type_: &type_, @@ -33,20 +28,20 @@ where } } - pub fn argument(name: String, type_: T) -> Self { + pub fn argument(name: String, type_: dyn ArgumentType) -> Self { Self::new(name, type_) } - pub fn suggests(mut self, provider: &dyn SuggestionProvider) -> Self { + pub fn suggests(mut self, provider: &dyn SuggestionProvider) -> Self { self.suggestions_provider = Some(provider); self } - pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { + pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { self.suggestions_provider.as_ref() } - pub fn get_type(&self) -> &T { + pub fn get_type(&self) -> &dyn ArgumentType { self.type_ } @@ -54,14 +49,14 @@ where self.name } - // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); + // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); // for (final CommandNode argument : getArguments()) { // result.addChild(argument); // } // return result; - pub fn build(self) -> ArgumentCommandNode<'a, S, T> { + pub fn build(self) -> ArgumentCommandNode<'a, S> { let result = ArgumentCommandNode { name: self.name, type_: &self.type_, diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index dcbf3ffd..a529f23c 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -7,6 +7,6 @@ use dyn_clonable::*; pub const SINGLE_SUCCESS: i32 = 1; #[clonable] -pub trait Command: Clone { - fn run(&self, context: &mut CommandContext) -> Result; +pub trait Command: Clone { + fn run(&self, context: &mut CommandContext) -> Result; } diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index f2fc7528..72a353ad 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,22 +1,13 @@ -use crate::{ - arguments::argument_type::{ArgumentType, Types}, - tree::root_command_node::RootCommandNode, -}; +use crate::{arguments::argument_type::ArgumentType, tree::root_command_node::RootCommandNode}; /// The core command dispatcher, for registering, parsing, and executing commands. /// The `S` generic is a custom "source" type, such as a user or originator of a command #[derive(Default, Clone)] -pub struct CommandDispatcher<'a, S, T> -where - T: ArgumentType, -{ - root: RootCommandNode<'a, S, T>, +pub struct CommandDispatcher<'a, S> { + root: RootCommandNode<'a, S>, } -impl CommandDispatcher<'_, S, T> -where - T: ArgumentType, -{ +impl CommandDispatcher<'_, S> { /// The string required to separate individual arguments in an input string /// /// See: [`ARGUMENT_SEPARATOR_CHAR`] @@ -48,7 +39,7 @@ where /// * `root` - the existing [`RootCommandNode`] to use as the basis for this tree /// # Returns /// A new [`CommandDispatcher`] with the specified root node. - fn new(root: RootCommandNode) -> Self { + fn new(root: RootCommandNode) -> Self { Self { root } } } diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 68144a40..4f0b4d49 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -6,22 +6,22 @@ use crate::{ arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; -use std::collections::HashMap; +use std::{any::Any, collections::HashMap}; -pub struct CommandContext<'a, S, T> { +pub struct CommandContext<'a, S> { source: S, input: String, - command: &'a dyn Command, - arguments: HashMap>, - root_node: &'a dyn CommandNode, - nodes: Vec>, + command: &'a dyn Command, + arguments: HashMap>>, + root_node: &'a dyn CommandNode, + nodes: Vec>, range: StringRange, - child: Option<&'a CommandContext<'a, S, T>>, - modifier: Option<&'a dyn RedirectModifier>, + child: Option<&'a CommandContext<'a, S>>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -impl CommandContext<'_, S, T> +impl CommandContext<'_, S> where S: PartialEq, { @@ -43,11 +43,11 @@ where } } - fn child(&self) -> &Option> { + fn child(&self) -> &Option> { &self.child } - fn last_child(&self) -> &CommandContext { + fn last_child(&self) -> &CommandContext { let mut result = self; while result.child.is_some() { result = result.child.as_ref().unwrap(); @@ -55,7 +55,7 @@ where result } - fn command(&self) -> &dyn Command { + fn command(&self) -> &dyn Command { &self.command } diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 639a97ee..95da4064 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,10 +1,8 @@ -use std::collections::HashMap; +use std::{any::Any, collections::HashMap}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, - command::Command, - command_dispatcher::CommandDispatcher, - redirect_modifier::RedirectModifier, + arguments::argument_type::ArgumentType, command::Command, + command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; @@ -27,19 +25,16 @@ use super::{ // private boolean forks; #[derive(Clone)] -pub struct CommandContextBuilder<'a, S, T> -where - T: ArgumentType, -{ - arguments: HashMap>, - root_node: &'a dyn CommandNode, - nodes: Vec>, - dispatcher: CommandDispatcher<'a, S, T>, +pub struct CommandContextBuilder<'a, S> { + arguments: HashMap>>, + root_node: &'a dyn CommandNode, + nodes: Vec>, + dispatcher: CommandDispatcher<'a, S>, source: S, - command: Box>, - child: Option>, + command: Box>, + child: Option>, range: StringRange, - modifier: Option>>, + modifier: Option>>, forks: bool, } @@ -50,14 +45,11 @@ where // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S, T> -where - T: ArgumentType, -{ +impl CommandContextBuilder<'_, S> { pub fn new( - dispatcher: CommandDispatcher, + dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNode, + root_node: dyn CommandNode, start: usize, ) -> Self { Self { @@ -78,25 +70,25 @@ where &self.source } - pub fn root_node(&self) -> &dyn CommandNode { + pub fn root_node(&self) -> &dyn CommandNode { &self.root_node } - pub fn with_argument(mut self, name: String, argument: ParsedArgument) -> Self { + pub fn with_argument(mut self, name: String, argument: ParsedArgument>) -> Self { self.arguments.insert(name, argument); self } - pub fn arguments(&self) -> &HashMap> { + pub fn arguments(&self) -> &HashMap>> { &self.arguments } - pub fn with_command(mut self, command: &dyn Command) -> Self { + pub fn with_command(mut self, command: &dyn Command) -> Self { self.command = command; self } - pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { + pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { self.nodes.push(ParsedCommandNode::new(node, range)); self.range = StringRange::encompassing(&self.range, &range); self.modifier = node.redirect_modifier(); @@ -104,16 +96,16 @@ where self } - pub fn with_child(mut self, child: CommandContextBuilder) -> Self { + pub fn with_child(mut self, child: CommandContextBuilder) -> Self { self.child = Some(child); self } - pub fn child(&self) -> Option<&CommandContextBuilder> { + pub fn child(&self) -> Option<&CommandContextBuilder> { self.child.as_ref() } - pub fn last_child(&self) -> Option<&CommandContextBuilder> { + pub fn last_child(&self) -> Option<&CommandContextBuilder> { let mut result = self; while let Some(child) = result.child() { result = child; @@ -121,15 +113,15 @@ where Some(result) } - pub fn command(&self) -> &dyn Command { + pub fn command(&self) -> &dyn Command { &*self.command } - pub fn nodes(&self) -> &Vec> { + pub fn nodes(&self) -> &Vec> { &self.nodes } - pub fn build(self, input: &str) -> CommandContext { + pub fn build(self, input: &str) -> CommandContext { CommandContext { source: self.source, input, @@ -144,7 +136,7 @@ where } } - pub fn dispatcher(&self) -> &CommandDispatcher { + pub fn dispatcher(&self) -> &CommandDispatcher { &self.dispatcher } @@ -152,7 +144,7 @@ where &self.range } - pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { + pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { if self.range.start() <= cursor { if self.range.end() < cursor { if let Some(child) = self.child() { diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 447a1223..e0bdf97b 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -3,12 +3,11 @@ use super::string_range::StringRange; #[derive(PartialEq, Eq, Hash, Clone)] pub struct ParsedArgument { range: StringRange, - // T is an item in an enum result: T, } impl ParsedArgument { - fn new(start: usize, end: usize, result: T) -> Self { + fn new(start: usize, end: usize, result: &T) -> Self { Self { range: StringRange::between(start, end), result, diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index c0be355c..16c6ed8b 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -1,17 +1,17 @@ use super::string_range::StringRange; use crate::tree::command_node::CommandNode; -pub struct ParsedCommandNode { - node: Box>, +pub struct ParsedCommandNode { + node: Box>, range: StringRange, } -impl ParsedCommandNode { - fn new(node: dyn CommandNode, range: StringRange) -> Self { +impl ParsedCommandNode { + fn new(node: dyn CommandNode, range: StringRange) -> Self { Self { node, range } } - fn node(&self) -> &dyn CommandNode { + fn node(&self) -> &dyn CommandNode { &self.node } @@ -20,7 +20,7 @@ impl ParsedCommandNode { } } -impl Clone for ParsedCommandNode { +impl Clone for ParsedCommandNode { fn clone_from(&mut self, source: &Self) { Self { node: self.node.clone(), diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index 42bc550e..252cb6ed 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -1,6 +1,6 @@ use crate::tree::command_node::CommandNode; -pub struct SuggestionContext<'a, S, T> { - parent: &'a dyn CommandNode, +pub struct SuggestionContext<'a, S> { + parent: &'a dyn CommandNode, start_pos: usize, } diff --git a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs b/azalea-brigadier/src/exceptions/builtin_exception_provider.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs +++ b/azalea-brigadier/src/exceptions/builtin_exception_provider.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/literal_message.rs b/azalea-brigadier/src/literal_message.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/literal_message.rs +++ b/azalea-brigadier/src/literal_message.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index fdb0a080..7a6d4db5 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -6,6 +6,6 @@ use crate::{ }; #[clonable] -pub trait RedirectModifier: Clone { - fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; +pub trait RedirectModifier: Clone { + fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; } diff --git a/azalea-brigadier/src/result_consumer.rs b/azalea-brigadier/src/result_consumer.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/result_consumer.rs +++ b/azalea-brigadier/src/result_consumer.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs index 95055fd8..dd63244d 100644 --- a/azalea-brigadier/src/single_redirect_modifier.rs +++ b/azalea-brigadier/src/single_redirect_modifier.rs @@ -3,6 +3,6 @@ use crate::{ exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait SingleRedirectModifier { - fn apply(&self, context: CommandContext) -> Result; +pub trait SingleRedirectModifier { + fn apply(&self, context: CommandContext) -> Result; } diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs index 9720d3b9..3027d460 100644 --- a/azalea-brigadier/src/suggestion/suggestion_provider.rs +++ b/azalea-brigadier/src/suggestion/suggestion_provider.rs @@ -5,10 +5,10 @@ use crate::{ use super::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}; -pub trait SuggestionProvider { +pub trait SuggestionProvider { fn suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &SuggestionsBuilder, ) -> Result; } diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 778c5de8..9f0ee06d 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -4,7 +4,7 @@ use crate::{context::string_range::StringRange, message::Message}; use super::suggestion::Suggestion; -#[derive(PartialEq, Eq, Hash, Default)] +#[derive(PartialEq, Eq, Hash)] pub struct Suggestions { range: StringRange, suggestions: Vec, diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 3fc1bb50..fb9a75fa 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,7 +1,10 @@ -use std::fmt::{Display, Formatter}; +use std::{ + any::Any, + fmt::{Display, Formatter}, +}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, @@ -22,35 +25,27 @@ const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; #[derive(Clone)] -pub struct ArgumentCommandNode<'a, S, T> -where - // each argument command node has its own different type - T: ArgumentType, -{ +pub struct ArgumentCommandNode<'a, S> { name: String, - type_: &'a T, - custom_suggestions: Option<&'a dyn SuggestionProvider>, - // custom_suggestions: &'a dyn SuggestionProvider, + type_: Box>, + custom_suggestions: Option<&'a dyn SuggestionProvider>, + // custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S, T>, + pub base: BaseCommandNode<'a, S>, } -impl ArgumentCommandNode<'_, S, T> -where - T: ArgumentType, -{ - fn get_type(&self) -> &T { - &self.type_ +impl ArgumentCommandNode<'_, S> { + fn get_type(&self) -> &dyn ArgumentType { + self.type_ } - fn custom_suggestions(&self) -> Option<&dyn SuggestionProvider> { + fn custom_suggestions(&self) -> Option<&dyn SuggestionProvider> { self.custom_suggestions } } -impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> +impl<'a, S> CommandNode for ArgumentCommandNode<'a, S> where - T: ArgumentType + Clone, S: Clone, { fn name(&self) -> &str { @@ -60,11 +55,11 @@ where fn parse( &self, reader: &mut StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { // final int start = reader.getCursor(); // final T result = type.parse(reader); - // final ParsedArgument parsed = new ParsedArgument<>(start, reader.getCursor(), result); + // final ParsedArgument parsed = new ParsedArgument<>(start, reader.getCursor(), result); // contextBuilder.withArgument(name, parsed); // contextBuilder.withNode(this, parsed.getRange()); @@ -81,7 +76,7 @@ where fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: &mut SuggestionsBuilder, ) -> Result { if self.custom_suggestions.is_none() { @@ -105,7 +100,7 @@ where USAGE_ARGUMENT_OPEN + self.name + USAGE_ARGUMENT_CLOSE } - fn create_builder(&self) -> RequiredArgumentBuilder { + fn create_builder(&self) -> RequiredArgumentBuilder { let builder = RequiredArgumentBuilder::argument(&self.name, &self.type_); builder.requires(self.base.get_requirement()); builder.forward( @@ -125,10 +120,7 @@ where } } -impl Display for ArgumentCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Display for ArgumentCommandNode<'_, S> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "", self.name, self.type_) } diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index f3be1597..b8f416eb 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,6 +1,6 @@ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -10,29 +10,23 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; use dyn_clonable::*; -use std::{collections::HashMap, fmt::Debug}; +use std::{any::Any, collections::HashMap, fmt::Debug}; #[derive(Default)] -pub struct BaseCommandNode<'a, S, T> -where - T: ArgumentType, -{ - children: HashMap>, - literals: HashMap>, - arguments: HashMap>, +pub struct BaseCommandNode<'a, S> { + children: HashMap>, + literals: HashMap>, + arguments: HashMap>, requirement: Option<&'a dyn Fn(&S) -> bool>, - redirect: Option<&'a dyn CommandNode>, - modifier: Option<&'a dyn RedirectModifier>, + redirect: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, - command: Option<&'a dyn Command>, + command: Option<&'a dyn Command>, } -impl BaseCommandNode<'_, S, T> where T: ArgumentType {} +impl BaseCommandNode<'_, S> {} -impl Clone for BaseCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Clone for BaseCommandNode<'_, S> { fn clone(&self) -> Self { Self { children: self.children.clone(), @@ -47,10 +41,7 @@ where } } -impl Debug for BaseCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Debug for BaseCommandNode<'_, S> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("BaseCommandNode") .field("children", &self.children) @@ -66,23 +57,20 @@ where } #[clonable] -pub trait CommandNode: Clone -where - T: ArgumentType, -{ +pub trait CommandNode: Clone { fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( &self, reader: &mut StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException>; fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result; fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> dyn ArgumentBuilder; + fn create_builder(&self) -> dyn ArgumentBuilder; fn get_examples(&self) -> Vec; } diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index 021a3ea6..a722121f 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,5 +1,5 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, builder::literal_argument_builder::LiteralArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -14,22 +14,15 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Debug, Clone)] -pub struct LiteralCommandNode<'a, S, T> -where - // each argument command node has its own different type - T: ArgumentType, -{ +pub struct LiteralCommandNode<'a, S> { literal: String, literal_lowercase: String, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S, T>, + pub base: BaseCommandNode<'a, S>, } -impl<'a, S, T> LiteralCommandNode<'a, S, T> -where - T: ArgumentType, -{ - pub fn new(literal: String, base: BaseCommandNode) -> Self { +impl<'a, S> LiteralCommandNode<'a, S> { + pub fn new(literal: String, base: BaseCommandNode) -> Self { let literal_lowercase = literal.to_lowercase(); Self { literal, @@ -59,9 +52,8 @@ where } } -impl CommandNode for LiteralCommandNode<'_, S, T> +impl CommandNode for LiteralCommandNode<'_, S> where - T: ArgumentType + Clone, S: Clone, { fn name(&self) -> &str { @@ -71,7 +63,7 @@ where fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { let start = reader.get_cursor(); let end = self.parse(reader); @@ -87,7 +79,7 @@ where fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { if self @@ -108,7 +100,7 @@ where self.literal } - fn create_builder(&self) -> LiteralArgumentBuilder { + fn create_builder(&self) -> LiteralArgumentBuilder { let builder = LiteralArgumentBuilder::literal(self.literal()); builder.requires(self.requirement()); builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index ded5fa77..c3139a05 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, @@ -13,18 +13,13 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Clone, Default)] -pub struct RootCommandNode<'a, S, T> -where - // each argument command node has its own different type - T: ArgumentType, -{ +pub struct RootCommandNode<'a, S> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S, T>, + pub base: BaseCommandNode<'a, S>, } -impl CommandNode for RootCommandNode<'_, S, T> +impl CommandNode for RootCommandNode<'_, S> where - T: ArgumentType + Clone, S: Clone, { fn name(&self) -> &str { @@ -34,13 +29,13 @@ where fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { } fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { Suggestions::empty() @@ -63,10 +58,7 @@ where } } -impl Display for RootCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Display for RootCommandNode<'_, S> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") } diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/tests/command_dispatcher_test.rs +++ b/azalea-brigadier/tests/command_dispatcher_test.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/tests/command_dispatcher_usages_test.rs +++ b/azalea-brigadier/tests/command_dispatcher_usages_test.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/tests/command_suggestions_test.rs +++ b/azalea-brigadier/tests/command_suggestions_test.rs @@ -0,0 +1 @@ + From 760816c81fab414cc42ab1e75506fc816bcf9681 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 13 Jan 2022 20:08:53 -0600 Subject: [PATCH 24/83] stuff --- .../src/arguments/argument_type.rs | 7 +- .../src/builder/argument_builder.rs | 44 +++++-- .../src/builder/literal_argument_builder.rs | 14 ++- .../src/builder/required_argument_builder.rs | 4 +- .../src/tree/argument_command_node.rs | 6 +- azalea-brigadier/src/tree/command_node.rs | 119 ++++++++++++++++-- 6 files changed, 164 insertions(+), 30 deletions(-) diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 3d1b1168..37cc9354 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -1,3 +1,5 @@ +use std::any::Any; + use super::bool_argument_type::BoolArgumentType; use crate::{ context::command_context::CommandContext, @@ -31,8 +33,7 @@ impl Types for BrigadierTypes { } */ -#[clonable] -pub trait ArgumentType: Clone { +pub trait ArgumentType { type Into; // T parse(StringReader reader) throws CommandSyntaxException; @@ -49,7 +50,7 @@ pub trait ArgumentType: Clone { fn list_suggestions( &self, context: &CommandContext, - builder: &mut SuggestionsBuilder, + builder: &SuggestionsBuilder, ) -> Result where Self: Sized, diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 6355456a..8c30bd44 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -3,7 +3,10 @@ use crate::{ command::Command, redirect_modifier::RedirectModifier, single_redirect_modifier::SingleRedirectModifier, - tree::{command_node::CommandNode, root_command_node::RootCommandNode}, + tree::{ + command_node::{BaseCommandNode, CommandNode}, + root_command_node::RootCommandNode, + }, }; pub struct BaseArgumentBuilder<'a, S> @@ -11,10 +14,10 @@ where S: Sized, { arguments: RootCommandNode<'a, S>, - command: Option<&'a dyn Command>, - requirement: &'a dyn Fn(&S) -> bool, - target: Option<&'a dyn CommandNode>, - modifier: Option<&'a dyn RedirectModifier>, + command: Option>>, + requirement: Box bool>, + target: Option>>, + modifier: Option>>, forks: bool, } @@ -22,15 +25,18 @@ pub trait ArgumentBuilder where T: ArgumentBuilder, { - fn build(self) -> dyn CommandNode; + fn build(self) -> Box>; } -impl BaseArgumentBuilder<'_, S> { - pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut Self, String> { +impl<'a, S> BaseArgumentBuilder<'a, S> { + pub fn then( + &mut self, + command: Box>, + ) -> Result<&mut Self, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); } - self.command = command; + self.command = Some(command); Ok(self) } @@ -103,4 +109,24 @@ impl BaseArgumentBuilder<'_, S> { pub fn is_fork(&self) -> bool { self.forks } + + pub fn build(self) -> BaseCommandNode<'a, S> { + let result: BaseCommandNode<'a, S> = BaseCommandNode { + command: self.command, + requirement: self.requirement, + redirect: self.target, + modifier: self.modifier, + forks: self.forks, + + arguments: Default::default(), + children: Default::default(), + literals: Default::default(), + }; + + for argument in self.arguments() { + result.add_child(argument); + } + + result + } } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index 8039dff0..0bd6071c 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,8 +1,9 @@ use crate::{ - arguments::argument_type::ArgumentType, tree::literal_command_node::LiteralCommandNode, + arguments::argument_type::ArgumentType, + tree::{command_node::CommandNode, literal_command_node::LiteralCommandNode}, }; -use super::argument_builder::BaseArgumentBuilder; +use super::argument_builder::{ArgumentBuilder, BaseArgumentBuilder}; pub struct LiteralArgumentBuilder<'a, S> { literal: String, @@ -21,9 +22,14 @@ impl<'a, S> LiteralArgumentBuilder<'a, S> { pub fn literal(name: String) -> Self { Self::new(name) } +} - pub fn build(self) -> LiteralCommandNode<'a, S> { - let result = LiteralCommandNode::new(self.literal, self.base); +impl<'a, S, T> ArgumentBuilder for LiteralArgumentBuilder<'a, S> +where + T: ArgumentBuilder, +{ + fn build(self) -> Box> { + let result = LiteralCommandNode::new(self.literal, self.base.build()); for argument in self.base.arguments { result.add_child(argument); diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 29af7f6f..b69c9dab 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -19,10 +19,10 @@ pub struct RequiredArgumentBuilder<'a, S> { } impl<'a, S> RequiredArgumentBuilder<'a, S> { - pub fn new(name: String, type_: dyn ArgumentType) -> Self { + pub fn new(name: String, type_: Box>) -> Self { Self { name, - type_: &type_, + type_: type_, suggestions_provider: None, base: BaseArgumentBuilder::new(name, type_), } diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index fb9a75fa..e33c3d3e 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -77,7 +77,7 @@ where fn list_suggestions( &self, context: CommandContext, - builder: &mut SuggestionsBuilder, + builder: &SuggestionsBuilder, ) -> Result { if self.custom_suggestions.is_none() { self.get_type().list_suggestions(context, builder) @@ -118,6 +118,10 @@ where fn get_examples(&self) -> Vec { self.type_.get_examples() } + + fn base(&self) -> &BaseCommandNode { + &self.base + } } impl Display for ArgumentCommandNode<'_, S> { diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index b8f416eb..f59b1cef 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,4 +1,7 @@ -use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; +use super::{ + argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode, + root_command_node::RootCommandNode, +}; use crate::{ arguments::argument_type::ArgumentType, builder::argument_builder::ArgumentBuilder, @@ -12,19 +15,98 @@ use crate::{ use dyn_clonable::*; use std::{any::Any, collections::HashMap, fmt::Debug}; -#[derive(Default)] pub struct BaseCommandNode<'a, S> { - children: HashMap>, + children: HashMap>>, literals: HashMap>, arguments: HashMap>, - requirement: Option<&'a dyn Fn(&S) -> bool>, - redirect: Option<&'a dyn CommandNode>, - modifier: Option<&'a dyn RedirectModifier>, + requirement: Box bool>, + redirect: Option>>, + modifier: Option>>, forks: bool, - command: Option<&'a dyn Command>, + command: Option>>, } -impl BaseCommandNode<'_, S> {} +impl BaseCommandNode<'_, S> { + pub fn command(&self) -> &Option>> { + &self.command + } + + pub fn children(&self) -> &HashMap>> { + &self.children + } + + pub fn child(&self, name: &str) -> Option<&dyn CommandNode> { + self.children.get(name).map(|child| child.as_ref()) + } + + pub fn redirect(&self) -> Option<&dyn CommandNode> { + self.redirect.as_ref().map(|redirect| redirect.as_ref()) + } + + pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + self.modifier.as_ref().map(|modifier| modifier.as_ref()) + } + + pub fn can_use(&self, source: S) -> bool { + (self.requirement)(&source) + } + + // public void addChild(final CommandNode node) { + // if (node instanceof RootCommandNode) { + // throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode"); + // } + + // final CommandNode child = children.get(node.getName()); + // if (child != null) { + // // We've found something to merge onto + // if (node.getCommand() != null) { + // child.command = node.getCommand(); + // } + // for (final CommandNode grandchild : node.getChildren()) { + // child.addChild(grandchild); + // } + // } else { + // children.put(node.getName(), node); + // if (node instanceof LiteralCommandNode) { + // literals.put(node.getName(), (LiteralCommandNode) node); + // } else if (node instanceof ArgumentCommandNode) { + // arguments.put(node.getName(), (ArgumentCommandNode) node); + // } + // } + // } + + pub fn add_child(&self, node: &dyn CommandNode) -> Result<(), String> { + if (&node as &dyn Any).is::>() { + return Err(String::from( + "Cannot add a RootCommandNode as a child to any other CommandNode", + )); + } + + let child = self.children.get(node.name()); + if let Some(child) = child { + // We've found something to merge onto + if let Some(command) = node.base.command() { + child.command = Some(command); + } + for grandchild in node.children() { + child.add_child(grandchild)?; + } + } else { + self.children.insert(node.name().to_string(), node); + if let Some(literal) = + &node.clone_boxed() as &dyn Any as &dyn Any as &LiteralCommandNode + { + self.literals + .insert(node.name().to_string(), literal.clone_boxed()); + } else if let Some(argument) = + &node.clone_boxed() as &dyn Any as &dyn Any as &ArgumentCommandNode + { + self.arguments + .insert(node.name().to_string(), argument.clone_boxed()); + } + } + } +} impl Clone for BaseCommandNode<'_, S> { fn clone(&self) -> Self { @@ -56,8 +138,22 @@ impl Debug for BaseCommandNode<'_, S> { } } -#[clonable] -pub trait CommandNode: Clone { +impl Default for BaseCommandNode<'_, S> { + fn default() -> Self { + Self { + children: HashMap::new(), + literals: HashMap::new(), + arguments: HashMap::new(), + requirement: Box::new(|_| true), + redirect: None, + modifier: None, + forks: false, + command: None, + } + } +} + +pub trait CommandNode { fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( @@ -68,9 +164,10 @@ pub trait CommandNode: Clone { fn list_suggestions( &self, context: CommandContext, - builder: SuggestionsBuilder, + builder: &SuggestionsBuilder, ) -> Result; fn is_valid_input(&self, input: &str) -> bool; fn create_builder(&self) -> dyn ArgumentBuilder; fn get_examples(&self) -> Vec; + fn base(&self) -> &BaseCommandNode; } From 30a86e1de5d8bf302f05d091b0a7b4cc6721d911 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 20 Jan 2022 00:49:10 +0000 Subject: [PATCH 25/83] bbbbbbbbbbbb --- .../src/builder/argument_builder.rs | 36 +++++++++++------ .../src/builder/literal_argument_builder.rs | 23 +++++++---- .../src/builder/required_argument_builder.rs | 40 ++++++++++--------- 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 8c30bd44..ec2756ca 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -29,14 +29,11 @@ where } impl<'a, S> BaseArgumentBuilder<'a, S> { - pub fn then( - &mut self, - command: Box>, - ) -> Result<&mut Self, String> { + pub fn then(&mut self, argument: Box>) -> Result<&mut Self, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); } - self.command = Some(command); + Ok(self) } @@ -44,25 +41,25 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { &self.arguments.get_children() } - pub fn executes(&mut self, command: dyn Command) -> &mut Self { - self.command = command; + pub fn executes(&mut self, command: Box>) -> &mut Self { + self.command = Some(command); self } - pub fn command(&self) -> dyn Command { + pub fn command(&self) -> Option>> { self.command } - pub fn requires(&mut self, requirement: &dyn Fn(&S) -> bool) -> &mut Self { + pub fn requires(&mut self, requirement: Box bool>) -> &mut Self { self.requirement = requirement; self } - pub fn requirement(&self) -> dyn Fn(&S) -> bool { + pub fn requirement(&self) -> Box bool> { self.requirement } - pub fn redirect(&mut self, target: &dyn CommandNode) -> &mut Self { + pub fn redirect(&mut self, target: Box>) -> &mut Self { self.forward(target, None, false) } @@ -85,8 +82,8 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { pub fn forward( &mut self, - target: &dyn CommandNode, - modifier: Option<&dyn RedirectModifier>, + target: Box>, + modifier: Option>>, fork: bool, ) -> Result<&mut Self, String> { if !self.arguments.get_children().is_empty() { @@ -130,3 +127,16 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { result } } + +impl Default for BaseArgumentBuilder<'_, S> { + fn default() -> Self { + Self { + arguments: Default::default(), + command: Default::default(), + requirement: Default::default(), + target: Default::default(), + modifier: Default::default(), + forks: Default::default(), + } + } +} diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index 0bd6071c..5bceb6eb 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,14 +1,23 @@ +use super::argument_builder::{ArgumentBuilder, BaseArgumentBuilder}; use crate::{ arguments::argument_type::ArgumentType, - tree::{command_node::CommandNode, literal_command_node::LiteralCommandNode}, + command::Command, + redirect_modifier::RedirectModifier, + tree::{ + command_node::CommandNode, literal_command_node::LiteralCommandNode, + root_command_node::RootCommandNode, + }, }; -use super::argument_builder::{ArgumentBuilder, BaseArgumentBuilder}; - pub struct LiteralArgumentBuilder<'a, S> { - literal: String, + arguments: RootCommandNode<'a, S>, + command: Option>>, + requirement: Box bool>, + target: Option>>, + modifier: Option>>, + forks: bool, - pub base: BaseArgumentBuilder<'a, S>, + literal: String, } impl<'a, S> LiteralArgumentBuilder<'a, S> { @@ -31,10 +40,10 @@ where fn build(self) -> Box> { let result = LiteralCommandNode::new(self.literal, self.base.build()); - for argument in self.base.arguments { + for argument in self.base.arguments() { result.add_child(argument); } - result + Box::new(result) } } diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index b69c9dab..b577f3ed 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -8,14 +8,16 @@ use std::any::Any; use super::argument_builder::BaseArgumentBuilder; pub struct RequiredArgumentBuilder<'a, S> { - // private final String name; - // private final ArgumentType type; - // private SuggestionProvider suggestionsProvider = null; + arguments: RootCommandNode<'a, S>, + command: Option>>, + requirement: Box bool>, + target: Option>>, + modifier: Option>>, + forks: bool, + name: String, type_: Box>, - suggestions_provider: Option<&'a dyn SuggestionProvider>, - - pub base: BaseArgumentBuilder<'a, S>, + suggestions_provider: Option>>, } impl<'a, S> RequiredArgumentBuilder<'a, S> { @@ -24,29 +26,29 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> { name, type_: type_, suggestions_provider: None, - base: BaseArgumentBuilder::new(name, type_), + base: BaseArgumentBuilder::default(), } } - pub fn argument(name: String, type_: dyn ArgumentType) -> Self { + pub fn argument(name: String, type_: Box>) -> Self { Self::new(name, type_) } - pub fn suggests(mut self, provider: &dyn SuggestionProvider) -> Self { + pub fn suggests(mut self, provider: Box>) -> Self { self.suggestions_provider = Some(provider); self } - pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { - self.suggestions_provider.as_ref() + pub fn suggestions_provider(&self) -> Option>> { + self.suggestions_provider } - pub fn get_type(&self) -> &dyn ArgumentType { + pub fn get_type(&self) -> Box> { self.type_ } pub fn name(&self) -> &str { - self.name + &self.name } // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); @@ -59,19 +61,19 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> { pub fn build(self) -> ArgumentCommandNode<'a, S> { let result = ArgumentCommandNode { name: self.name, - type_: &self.type_, + type_: self.type_, base: BaseCommandNode { - command: self.base.command, - requirement: self.base.requirement, - redirect: self.base.redirect, - modifier: self.base.modifier, + command: self.base.command(), + requirement: self.base.requirement(), + redirect: self.base.get_redirect(), + modifier: self.base.get_redirect_modifier(), forks: self.base.forks, ..BaseCommandNode::default() }, custom_suggestions: self.base.custom_suggestions, }; - for argument in self.base.arguments { + for argument in self.base.arguments() { result.add_child(argument); } From d9e52f8d965473517ddf6f11f9ac3be9aa14e14d Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 1 Feb 2022 00:12:46 -0600 Subject: [PATCH 26/83] b --- .../src/builder/argument_builder.rs | 37 ++-- .../src/builder/literal_argument_builder.rs | 29 ++- .../src/builder/required_argument_builder.rs | 35 ++-- azalea-brigadier/src/command_dispatcher.rs | 1 + .../src/context/command_context.rs | 4 +- .../src/context/command_context_builder.rs | 21 ++- .../src/context/parsed_command_node.rs | 8 +- .../src/context/suggestion_context.rs | 4 +- azalea-brigadier/src/string_reader.rs | 14 +- .../src/tree/argument_command_node.rs | 67 ++++++- azalea-brigadier/src/tree/command_node.rs | 168 +++++++++--------- .../src/tree/literal_command_node.rs | 61 ++++--- .../src/tree/root_command_node.rs | 31 ++-- 13 files changed, 280 insertions(+), 200 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index ec2756ca..d0770be2 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -4,40 +4,35 @@ use crate::{ redirect_modifier::RedirectModifier, single_redirect_modifier::SingleRedirectModifier, tree::{ - command_node::{BaseCommandNode, CommandNode}, + command_node::{BaseCommandNode, CommandNodeTrait}, root_command_node::RootCommandNode, }, }; +use std::fmt::Debug; -pub struct BaseArgumentBuilder<'a, S> -where - S: Sized, -{ +pub struct BaseArgumentBuilder<'a, S> { arguments: RootCommandNode<'a, S>, command: Option>>, requirement: Box bool>, - target: Option>>, + target: Option>>, modifier: Option>>, forks: bool, } -pub trait ArgumentBuilder -where - T: ArgumentBuilder, -{ - fn build(self) -> Box>; +pub trait ArgumentBuilder { + fn build(self) -> Box>; } impl<'a, S> BaseArgumentBuilder<'a, S> { - pub fn then(&mut self, argument: Box>) -> Result<&mut Self, String> { + pub fn then(&mut self, argument: Box>) -> Result<&mut Self, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); } - + self.arguments.add_child(argument.build()); Ok(self) } - pub fn arguments(&self) -> &Vec<&dyn CommandNode> { + pub fn arguments(&self) -> &Vec<&dyn CommandNodeTrait> { &self.arguments.get_children() } @@ -59,13 +54,13 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { self.requirement } - pub fn redirect(&mut self, target: Box>) -> &mut Self { + pub fn redirect(&mut self, target: Box>) -> &mut Self { self.forward(target, None, false) } pub fn redirect_modifier( &mut self, - target: &dyn CommandNode, + target: &dyn CommandNodeTrait, modifier: &dyn SingleRedirectModifier, ) -> &mut Self { // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false); @@ -74,7 +69,7 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { pub fn fork( &mut self, - target: &dyn CommandNode, + target: &dyn CommandNodeTrait, modifier: &dyn RedirectModifier, ) -> &mut Self { self.forward(target, Some(modifier), true) @@ -82,20 +77,20 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { pub fn forward( &mut self, - target: Box>, - modifier: Option>>, + target: Option>>, + modifier: Option<&dyn RedirectModifier>, fork: bool, ) -> Result<&mut Self, String> { if !self.arguments.get_children().is_empty() { return Err("Cannot forward a node with children".to_string()); } - self.target = Some(target); + self.target = target; self.modifier = modifier; self.forks = fork; Ok(self) } - pub fn get_redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNodeTrait> { self.target.as_ref() } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index 5bceb6eb..8250d45d 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -4,27 +4,38 @@ use crate::{ command::Command, redirect_modifier::RedirectModifier, tree::{ - command_node::CommandNode, literal_command_node::LiteralCommandNode, + command_node::CommandNodeTrait, literal_command_node::LiteralCommandNode, root_command_node::RootCommandNode, }, }; +use std::fmt::Debug; -pub struct LiteralArgumentBuilder<'a, S> { +pub struct LiteralArgumentBuilder<'a, S> +where + , +{ arguments: RootCommandNode<'a, S>, command: Option>>, requirement: Box bool>, - target: Option>>, + target: Option>>, modifier: Option>>, forks: bool, - literal: String, } -impl<'a, S> LiteralArgumentBuilder<'a, S> { +impl<'a, S> LiteralArgumentBuilder<'a, S> +where + , +{ pub fn new(literal: String) -> Self { Self { literal, - base: BaseArgumentBuilder::default(), + arguments: RootCommandNode::new(), + command: None, + requirement: Box::new(|_| true), + target: None, + modifier: None, + forks: false, } } @@ -33,11 +44,11 @@ impl<'a, S> LiteralArgumentBuilder<'a, S> { } } -impl<'a, S, T> ArgumentBuilder for LiteralArgumentBuilder<'a, S> +impl<'a, S> ArgumentBuilder for LiteralArgumentBuilder<'a, S> where - T: ArgumentBuilder, + , { - fn build(self) -> Box> { + fn build(self) -> Box> { let result = LiteralCommandNode::new(self.literal, self.base.build()); for argument in self.base.arguments() { diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index b577f3ed..fe6f2ecc 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,17 +1,23 @@ +use super::argument_builder::BaseArgumentBuilder; use crate::{ arguments::argument_type::ArgumentType, + command::Command, + redirect_modifier::RedirectModifier, suggestion::suggestion_provider::SuggestionProvider, - tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, + tree::{ + argument_command_node::ArgumentCommandNode, + command_node::{BaseCommandNode, CommandNodeTrait}, + root_command_node::RootCommandNode, + }, }; use std::any::Any; - -use super::argument_builder::BaseArgumentBuilder; +use std::fmt::Debug; pub struct RequiredArgumentBuilder<'a, S> { arguments: RootCommandNode<'a, S>, command: Option>>, requirement: Box bool>, - target: Option>>, + target: Option>>, modifier: Option>>, forks: bool, @@ -26,7 +32,12 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> { name, type_: type_, suggestions_provider: None, - base: BaseArgumentBuilder::default(), + arguments: RootCommandNode::new(), + command: None, + requirement: Box::new(|_| true), + target: None, + modifier: None, + forks: false, } } @@ -62,15 +73,13 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> { let result = ArgumentCommandNode { name: self.name, type_: self.type_, - base: BaseCommandNode { - command: self.base.command(), - requirement: self.base.requirement(), - redirect: self.base.get_redirect(), - modifier: self.base.get_redirect_modifier(), - forks: self.base.forks, - ..BaseCommandNode::default() - }, + command: self.base.command(), + requirement: self.base.requirement(), + redirect: self.base.get_redirect(), + modifier: self.base.get_redirect_modifier(), + forks: self.base.forks, custom_suggestions: self.base.custom_suggestions, + ..ArgumentCommandNode::default() }; for argument in self.base.arguments() { diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index 72a353ad..98288a48 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,4 +1,5 @@ use crate::{arguments::argument_type::ArgumentType, tree::root_command_node::RootCommandNode}; +use std::fmt::Debug; /// The core command dispatcher, for registering, parsing, and executing commands. /// The `S` generic is a custom "source" type, such as a user or originator of a command diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 4f0b4d49..8db1487f 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, - tree::command_node::CommandNode, + tree::command_node::CommandNodeTrait, }; use std::{any::Any, collections::HashMap}; @@ -13,7 +13,7 @@ pub struct CommandContext<'a, S> { input: String, command: &'a dyn Command, arguments: HashMap>>, - root_node: &'a dyn CommandNode, + root_node: &'a dyn CommandNodeTrait, nodes: Vec>, range: StringRange, child: Option<&'a CommandContext<'a, S>>, diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 95da4064..969f9cfd 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,10 +1,10 @@ -use std::{any::Any, collections::HashMap}; - use crate::{ arguments::argument_type::ArgumentType, command::Command, command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, - tree::command_node::CommandNode, + tree::command_node::CommandNodeTrait, }; +use std::fmt::Debug; +use std::{any::Any, collections::HashMap}; use super::{ command_context::CommandContext, parsed_argument::ParsedArgument, @@ -27,12 +27,12 @@ use super::{ #[derive(Clone)] pub struct CommandContextBuilder<'a, S> { arguments: HashMap>>, - root_node: &'a dyn CommandNode, + root_node: &'a dyn CommandNodeTrait, nodes: Vec>, dispatcher: CommandDispatcher<'a, S>, source: S, command: Box>, - child: Option>, + child: Box>>, range: StringRange, modifier: Option>>, forks: bool, @@ -45,11 +45,14 @@ pub struct CommandContextBuilder<'a, S> { // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S> { +impl CommandContextBuilder<'_, S> +where + , +{ pub fn new( dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNode, + root_node: dyn CommandNodeTrait, start: usize, ) -> Self { Self { @@ -70,7 +73,7 @@ impl CommandContextBuilder<'_, S> { &self.source } - pub fn root_node(&self) -> &dyn CommandNode { + pub fn root_node(&self) -> &dyn CommandNodeTrait { &self.root_node } @@ -88,7 +91,7 @@ impl CommandContextBuilder<'_, S> { self } - pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { + pub fn with_node(mut self, node: dyn CommandNodeTrait, range: StringRange) -> Self { self.nodes.push(ParsedCommandNode::new(node, range)); self.range = StringRange::encompassing(&self.range, &range); self.modifier = node.redirect_modifier(); diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index 16c6ed8b..21d1b2e9 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -1,17 +1,17 @@ use super::string_range::StringRange; -use crate::tree::command_node::CommandNode; +use crate::tree::command_node::CommandNodeTrait; pub struct ParsedCommandNode { - node: Box>, + node: Box>, range: StringRange, } impl ParsedCommandNode { - fn new(node: dyn CommandNode, range: StringRange) -> Self { + fn new(node: dyn CommandNodeTrait, range: StringRange) -> Self { Self { node, range } } - fn node(&self) -> &dyn CommandNode { + fn node(&self) -> &dyn CommandNodeTrait { &self.node } diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index 252cb6ed..51a053c1 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -1,6 +1,6 @@ -use crate::tree::command_node::CommandNode; +use crate::tree::command_node::CommandNodeTrait; pub struct SuggestionContext<'a, S> { - parent: &'a dyn CommandNode, + parent: &'a dyn CommandNodeTrait, start_pos: usize, } diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index f32de473..694edccb 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -9,7 +9,7 @@ use std::str::FromStr; #[derive(Clone)] pub struct StringReader<'a> { string: &'a str, - cursor: usize, + pub cursor: usize, } const SYNTAX_ESCAPE: char = '\\'; @@ -18,9 +18,15 @@ const SYNTAX_SINGLE_QUOTE: char = '\''; // impl<'a> From<&'a str> for &StringReader<'a> {} -impl StringReader<'_> { - fn from(string: &str) -> StringReader { - StringReader { string, cursor: 0 } +// impl StringReader<'_> { +// fn from(string: &str) -> StringReader { +// StringReader { string, cursor: 0 } +// } +// } + +impl<'a> From<&'a str> for StringReader<'a> { + fn from(string: &'a str) -> Self { + Self { string, cursor: 0 } } } diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index e33c3d3e..0997ec17 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,17 +1,20 @@ use std::{ any::Any, - fmt::{Display, Formatter}, + collections::HashMap, + fmt::{Debug, Display, Formatter}, }; use crate::{ arguments::argument_type::ArgumentType, builder::required_argument_builder::RequiredArgumentBuilder, + command::Command, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, parsed_argument::ParsedArgument, }, exceptions::command_syntax_exception::CommandSyntaxException, immutable_string_reader::ImmutableStringReader, + redirect_modifier::RedirectModifier, string_reader::StringReader, suggestion::{ suggestion_provider::SuggestionProvider, suggestions::Suggestions, @@ -19,12 +22,16 @@ use crate::{ }, }; -use super::command_node::{BaseCommandNode, CommandNode}; +use super::{ + command_node::{BaseCommandNode, CommandNodeTrait}, + literal_command_node::LiteralCommandNode, + root_command_node::RootCommandNode, +}; const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ArgumentCommandNode<'a, S> { name: String, type_: Box>, @@ -32,6 +39,15 @@ pub struct ArgumentCommandNode<'a, S> { // custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S>, + + children: HashMap>>, + literals: HashMap>, + arguments: HashMap>, + pub requirement: Box bool>, + redirect: Option>>, + modifier: Option>>, + forks: bool, + pub command: Option>>, } impl ArgumentCommandNode<'_, S> { @@ -44,10 +60,7 @@ impl ArgumentCommandNode<'_, S> { } } -impl<'a, S> CommandNode for ArgumentCommandNode<'a, S> -where - S: Clone, -{ +impl<'a, S> CommandNodeTrait for ArgumentCommandNode<'a, S> { fn name(&self) -> &str { &self.name } @@ -119,8 +132,44 @@ where self.type_.get_examples() } - fn base(&self) -> &BaseCommandNode { - &self.base + fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + self.modifier.as_ref().map(|modifier| modifier.as_ref()) + } + + fn can_use(&self, source: S) -> bool { + (self.requirement)(&source) + } + + fn add_child(&self, node: &Box>) -> Result<(), String> { + let dynamic_node = node as &dyn Any; + if dynamic_node.is::>() { + return Err(String::from( + "Cannot add a RootCommandNode as a child to any other CommandNode", + )); + } + + let mut child = self.children.get(node.name()); + if let Some(child) = child { + // We've found something to merge onto + if let Some(command) = node.base().command() { + child.base_mut().command = Some(*command); + } + for grandchild in node.base().children().values() { + child.base_mut().add_child(&*grandchild)?; + } + Ok(()) + } else { + self.children.insert(node.name().to_string(), *node); + + if let Some(dynamic_node) = dynamic_node.downcast_ref::>() { + self.literals.insert(node.name().to_string(), *dynamic_node); + } else if let Some(dynamic_node) = dynamic_node.downcast_ref::>() + { + self.arguments + .insert(node.name().to_string(), *dynamic_node); + } + Ok(()) + } } } diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index f59b1cef..b7801363 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -12,116 +12,111 @@ use crate::{ string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -use dyn_clonable::*; +use std::ops::Deref; use std::{any::Any, collections::HashMap, fmt::Debug}; -pub struct BaseCommandNode<'a, S> { - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - command: Option>>, +enum CommandNodeEnum<'a, S> { + Literal(LiteralCommandNode<'a, S>), + Argument(ArgumentCommandNode<'a, S>), + Root(RootCommandNode<'a, S>), } -impl BaseCommandNode<'_, S> { - pub fn command(&self) -> &Option>> { - &self.command +impl Deref for CommandNodeEnum<'_, S> { + type Target = dyn CommandNodeTrait; + + fn deref(&self) -> &Self::Target { + match self { + CommandNodeEnum::Literal(node) => node, + CommandNodeEnum::Argument(node) => node, + CommandNodeEnum::Root(node) => node, + } + } +} + +impl From> for CommandNodeEnum<'_, S> { + fn from(node: LiteralCommandNode<'_, S>) -> Self { + CommandNodeEnum::Literal(node) + } +} + +impl From> for CommandNodeEnum<'_, S> { + fn from(node: ArgumentCommandNode<'_, S>) -> Self { + CommandNodeEnum::Argument(node) + } +} + +impl From> for CommandNodeEnum<'_, S> { + fn from(node: RootCommandNode<'_, S>) -> Self { + CommandNodeEnum::Root(node) + } +} + +impl CommandNodeEnum<'_, S> { + fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + (*self).modifier.as_ref().map(|modifier| modifier.as_ref()) } - pub fn children(&self) -> &HashMap>> { - &self.children - } - - pub fn child(&self, name: &str) -> Option<&dyn CommandNode> { - self.children.get(name).map(|child| child.as_ref()) - } - - pub fn redirect(&self) -> Option<&dyn CommandNode> { - self.redirect.as_ref().map(|redirect| redirect.as_ref()) - } - - pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - self.modifier.as_ref().map(|modifier| modifier.as_ref()) - } - - pub fn can_use(&self, source: S) -> bool { + fn can_use(&self, source: S) -> bool { (self.requirement)(&source) } - // public void addChild(final CommandNode node) { - // if (node instanceof RootCommandNode) { - // throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode"); - // } - - // final CommandNode child = children.get(node.getName()); - // if (child != null) { - // // We've found something to merge onto - // if (node.getCommand() != null) { - // child.command = node.getCommand(); - // } - // for (final CommandNode grandchild : node.getChildren()) { - // child.addChild(grandchild); - // } - // } else { - // children.put(node.getName(), node); - // if (node instanceof LiteralCommandNode) { - // literals.put(node.getName(), (LiteralCommandNode) node); - // } else if (node instanceof ArgumentCommandNode) { - // arguments.put(node.getName(), (ArgumentCommandNode) node); - // } - // } - // } - - pub fn add_child(&self, node: &dyn CommandNode) -> Result<(), String> { - if (&node as &dyn Any).is::>() { + fn add_child(&self, node: &Box>) -> Result<(), String> { + let dynamic_node = node as &dyn Any; + if dynamic_node.is::>() { return Err(String::from( "Cannot add a RootCommandNode as a child to any other CommandNode", )); } - let child = self.children.get(node.name()); + let mut child = self.children.get(node.name()); if let Some(child) = child { // We've found something to merge onto - if let Some(command) = node.base.command() { - child.command = Some(command); + if let Some(command) = node.base().command() { + child.base_mut().command = Some(*command); } - for grandchild in node.children() { - child.add_child(grandchild)?; + for grandchild in node.base().children().values() { + child.base_mut().add_child(&*grandchild)?; } + Ok(()) } else { - self.children.insert(node.name().to_string(), node); - if let Some(literal) = - &node.clone_boxed() as &dyn Any as &dyn Any as &LiteralCommandNode - { - self.literals - .insert(node.name().to_string(), literal.clone_boxed()); - } else if let Some(argument) = - &node.clone_boxed() as &dyn Any as &dyn Any as &ArgumentCommandNode + self.children.insert(node.name().to_string(), *node); + + if let Some(dynamic_node) = dynamic_node.downcast_ref::>() { + self.literals.insert(node.name().to_string(), *dynamic_node); + } else if let Some(dynamic_node) = dynamic_node.downcast_ref::>() { self.arguments - .insert(node.name().to_string(), argument.clone_boxed()); + .insert(node.name().to_string(), *dynamic_node); } + Ok(()) } } } +pub struct BaseCommandNode<'a, S> { + children: HashMap>>, + literals: HashMap>, + arguments: HashMap>, + pub requirement: Box bool>, + redirect: Option>>, + modifier: Option>>, + forks: bool, + pub command: Option>>, +} -impl Clone for BaseCommandNode<'_, S> { - fn clone(&self) -> Self { - Self { - children: self.children.clone(), - literals: self.literals.clone(), - arguments: self.arguments.clone(), - requirement: self.requirement.clone(), - redirect: self.redirect.clone(), - modifier: self.modifier.clone(), - forks: self.forks.clone(), - command: self.command.clone(), - } - } -} +// impl Clone for BaseCommandNode<'_, S> { +// fn clone(&self) -> Self { +// Self { +// children: self.children.clone(), +// literals: self.literals.clone(), +// arguments: self.arguments.clone(), +// requirement: self.requirement.clone(), +// redirect: self.redirect.clone(), +// modifier: self.modifier.clone(), +// forks: self.forks.clone(), +// command: self.command.clone(), +// } +// } +// } impl Debug for BaseCommandNode<'_, S> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -153,7 +148,7 @@ impl Default for BaseCommandNode<'_, S> { } } -pub trait CommandNode { +pub trait CommandNodeTrait { fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( @@ -167,7 +162,6 @@ pub trait CommandNode { builder: &SuggestionsBuilder, ) -> Result; fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> dyn ArgumentBuilder; + fn create_builder(&self) -> Box>; fn get_examples(&self) -> Vec; - fn base(&self) -> &BaseCommandNode; } diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index a722121f..253b6dc5 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,17 +1,19 @@ use crate::{ arguments::argument_type::ArgumentType, - builder::literal_argument_builder::LiteralArgumentBuilder, - command::Command, + builder::{ + argument_builder::ArgumentBuilder, literal_argument_builder::LiteralArgumentBuilder, + }, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, - redirect_modifier::RedirectModifier, + immutable_string_reader::ImmutableStringReader, string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; +use std::fmt::Debug; -use super::command_node::{BaseCommandNode, CommandNode}; +use super::command_node::{BaseCommandNode, CommandNodeTrait}; #[derive(Debug, Clone)] pub struct LiteralCommandNode<'a, S> { @@ -22,7 +24,7 @@ pub struct LiteralCommandNode<'a, S> { } impl<'a, S> LiteralCommandNode<'a, S> { - pub fn new(literal: String, base: BaseCommandNode) -> Self { + pub fn new(literal: String, base: BaseCommandNode<'a, S>) -> Self { let literal_lowercase = literal.to_lowercase(); Self { literal, @@ -35,16 +37,16 @@ impl<'a, S> LiteralCommandNode<'a, S> { &self.literal } - pub fn parse(&self, reader: StringReader) -> i32 { - let start = reader.get_cursor(); - if reader.can_read(self.literal.len()) { + pub fn parse(&self, reader: &mut StringReader) -> i32 { + let start = reader.cursor(); + if reader.can_read_length(self.literal.len()) { let end = start + self.literal.len(); - if reader.get_string()[start..end].eq(&self.literal) { - reader.set_cursor(end); + if reader.string()[start..end].eq(&self.literal) { + reader.cursor = end; if !reader.can_read() || reader.peek() == ' ' { return end as i32; } else { - reader.set_cursor(start); + reader.cursor = start; } } } @@ -52,27 +54,24 @@ impl<'a, S> LiteralCommandNode<'a, S> { } } -impl CommandNode for LiteralCommandNode<'_, S> -where - S: Clone, -{ +impl CommandNodeTrait for LiteralCommandNode<'_, S> { fn name(&self) -> &str { &self.literal } fn parse( &self, - reader: StringReader, + reader: &mut StringReader<'_>, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { - let start = reader.get_cursor(); + let start = reader.cursor(); let end = self.parse(reader); if end > -1 { return Ok(()); } Err(BuiltInExceptions::LiteralIncorrect { - expected: self.literal(), + expected: self.literal().to_string(), } .create_with_context(reader)) } @@ -80,33 +79,41 @@ where fn list_suggestions( &self, context: CommandContext, - builder: SuggestionsBuilder, + builder: &SuggestionsBuilder, ) -> Result { if self .literal_lowercase .starts_with(&builder.remaining_lowercase()) { - builder.suggest(self.literal()) + Ok(builder.suggest(self.literal()).build()) } else { - Suggestions::empty() + Ok(Suggestions::default()) } } fn is_valid_input(&self, input: &str) -> bool { - self.parse(StringReader::from(input)) > -1 + self.parse(&mut StringReader::from(input)) > -1 } fn usage_text(&self) -> &str { - self.literal + &self.literal } - fn create_builder(&self) -> LiteralArgumentBuilder { - let builder = LiteralArgumentBuilder::literal(self.literal()); - builder.requires(self.requirement()); - builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); + fn create_builder(&self) -> Box> { + let mut builder = LiteralArgumentBuilder::literal(self.literal().to_string()); + builder.base.requires(&self.base().requirement); + builder.base.forward( + self.base.redirect(), + self.base.redirect_modifier(), + self.base.is_fork(), + ); if self.command().is_some() { builder.executes(self.command().unwrap()); } builder } + + fn get_examples(&self) -> Vec { + todo!() + } } diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index c3139a05..0fcb8d97 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -1,44 +1,49 @@ -use std::fmt::{Display, Formatter}; - +use super::{ + argument_command_node::ArgumentCommandNode, + command_node::{BaseCommandNode, CommandNodeTrait}, + literal_command_node::LiteralCommandNode, +}; use crate::{ arguments::argument_type::ArgumentType, + builder::argument_builder::ArgumentBuilder, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, + redirect_modifier::RedirectModifier, string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; +use std::{ + any::Any, + fmt::{Debug, Display, Formatter}, +}; -use super::command_node::{BaseCommandNode, CommandNode}; - -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] pub struct RootCommandNode<'a, S> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S>, } -impl CommandNode for RootCommandNode<'_, S> -where - S: Clone, -{ +impl CommandNodeTrait for RootCommandNode<'_, S> { fn name(&self) -> &str { "" } fn parse( &self, - reader: StringReader, + reader: &mut StringReader<'_>, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { + Ok(()) } fn list_suggestions( &self, context: CommandContext, - builder: SuggestionsBuilder, + builder: &SuggestionsBuilder, ) -> Result { - Suggestions::empty() + Ok(Suggestions::default()) } fn is_valid_input(&self, input: &str) -> bool { @@ -49,7 +54,7 @@ where "" } - fn create_builder(&self) -> () { + fn create_builder(&self) -> Box> { panic!("Cannot convert root into a builder"); } From 1b888881516c7553126e0c7fc2539d14b129e29e Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 2 Feb 2022 07:57:50 -0600 Subject: [PATCH 27/83] f --- azalea-brigadier/src/context/command_context_builder.rs | 7 ++----- azalea-brigadier/src/tree/command_node.rs | 8 ++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 969f9cfd..ba25849c 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -45,14 +45,11 @@ pub struct CommandContextBuilder<'a, S> { // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S> -where - , -{ +impl CommandContextBuilder<'_, S> { pub fn new( dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNodeTrait, + root_node: &dyn CommandNodeTrait, start: usize, ) -> Self { Self { diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index b7801363..30907163 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -21,14 +21,14 @@ enum CommandNodeEnum<'a, S> { Root(RootCommandNode<'a, S>), } -impl Deref for CommandNodeEnum<'_, S> { +impl<'a, S> Deref for CommandNodeEnum<'a, S> { type Target = dyn CommandNodeTrait; fn deref(&self) -> &Self::Target { match self { - CommandNodeEnum::Literal(node) => node, - CommandNodeEnum::Argument(node) => node, - CommandNodeEnum::Root(node) => node, + CommandNodeEnum::Literal(node) => *node as &Self::Target, + CommandNodeEnum::Argument(node) => *node as &Self::Target, + CommandNodeEnum::Root(node) => *node as &Self::Target, } } } From 4ff67d4917ce333232189e86aee09f2d82451fc6 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 3 Feb 2022 02:16:24 +0000 Subject: [PATCH 28/83] a --- Cargo.lock | 19 +++++++ azalea-brigadier/Cargo.toml | 1 + .../src/builder/literal_argument_builder.rs | 17 ++---- azalea-brigadier/src/command_dispatcher.rs | 6 +- azalea-brigadier/src/lib.rs | 3 + .../src/tree/argument_command_node.rs | 24 ++++---- azalea-brigadier/src/tree/command_node.rs | 56 ++++++------------- .../src/tree/literal_command_node.rs | 30 +++++++--- .../src/tree/root_command_node.rs | 19 +++++-- 9 files changed, 91 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0ca81fb..d52792ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,7 @@ name = "azalea-brigadier" version = "0.1.0" dependencies = [ "dyn-clonable", + "enum_dispatch", "lazy_static", ] @@ -382,6 +383,18 @@ dependencies = [ "syn", ] +[[package]] +name = "enum_dispatch" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd53b3fde38a39a06b2e66dc282f3e86191e53bd04cc499929c15742beae3df8" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "flate2" version = "1.0.22" @@ -717,6 +730,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + [[package]] name = "oorandom" version = "11.1.3" diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index 4e8968d7..6dead502 100644 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -8,3 +8,4 @@ version = "0.1.0" [dependencies] lazy_static = "^1.4" dyn-clonable = "^0.9" +enum_dispatch = "^0.3" diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index 8250d45d..4a95755c 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -10,11 +10,8 @@ use crate::{ }; use std::fmt::Debug; -pub struct LiteralArgumentBuilder<'a, S> -where - , -{ - arguments: RootCommandNode<'a, S>, +pub struct LiteralArgumentBuilder { + arguments: RootCommandNode, command: Option>>, requirement: Box bool>, target: Option>>, @@ -23,10 +20,7 @@ where literal: String, } -impl<'a, S> LiteralArgumentBuilder<'a, S> -where - , -{ +impl LiteralArgumentBuilder { pub fn new(literal: String) -> Self { Self { literal, @@ -44,10 +38,7 @@ where } } -impl<'a, S> ArgumentBuilder for LiteralArgumentBuilder<'a, S> -where - , -{ +impl ArgumentBuilder for LiteralArgumentBuilder { fn build(self) -> Box> { let result = LiteralCommandNode::new(self.literal, self.base.build()); diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index 98288a48..f8ffddff 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -4,11 +4,11 @@ use std::fmt::Debug; /// The core command dispatcher, for registering, parsing, and executing commands. /// The `S` generic is a custom "source" type, such as a user or originator of a command #[derive(Default, Clone)] -pub struct CommandDispatcher<'a, S> { - root: RootCommandNode<'a, S>, +pub struct CommandDispatcher { + root: RootCommandNode, } -impl CommandDispatcher<'_, S> { +impl CommandDispatcher { /// The string required to separate individual arguments in an input string /// /// See: [`ARGUMENT_SEPARATOR_CHAR`] diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index b2345abb..a1ac267c 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,6 +1,9 @@ #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate enum_dispatch; + mod ambiguity_consumer; mod arguments; mod builder; diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 0997ec17..9d2af14e 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -31,18 +31,14 @@ use super::{ const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; -#[derive(Clone, Debug)] -pub struct ArgumentCommandNode<'a, S> { +pub struct ArgumentCommandNode { name: String, type_: Box>, - custom_suggestions: Option<&'a dyn SuggestionProvider>, - // custom_suggestions: &'a dyn SuggestionProvider, - // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S>, + custom_suggestions: Option>>, children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, + literals: HashMap>, + arguments: HashMap>, pub requirement: Box bool>, redirect: Option>>, modifier: Option>>, @@ -50,17 +46,17 @@ pub struct ArgumentCommandNode<'a, S> { pub command: Option>>, } -impl ArgumentCommandNode<'_, S> { +impl ArgumentCommandNode { fn get_type(&self) -> &dyn ArgumentType { - self.type_ + &*self.type_ } - fn custom_suggestions(&self) -> Option<&dyn SuggestionProvider> { - self.custom_suggestions + fn custom_suggestions(&self) -> &Option>> { + &self.custom_suggestions } } -impl<'a, S> CommandNodeTrait for ArgumentCommandNode<'a, S> { +impl CommandNodeTrait for ArgumentCommandNode { fn name(&self) -> &str { &self.name } @@ -173,7 +169,7 @@ impl<'a, S> CommandNodeTrait for ArgumentCommandNode<'a, S> { } } -impl Display for ArgumentCommandNode<'_, S> { +impl Display for ArgumentCommandNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "", self.name, self.type_) } diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index 30907163..207e114e 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -15,43 +15,14 @@ use crate::{ use std::ops::Deref; use std::{any::Any, collections::HashMap, fmt::Debug}; -enum CommandNodeEnum<'a, S> { - Literal(LiteralCommandNode<'a, S>), - Argument(ArgumentCommandNode<'a, S>), - Root(RootCommandNode<'a, S>), +#[enum_dispatch(CommandNodeTrait)] +enum CommandNodeEnum { + Literal(LiteralCommandNode), + Argument(ArgumentCommandNode), + Root(RootCommandNode), } -impl<'a, S> Deref for CommandNodeEnum<'a, S> { - type Target = dyn CommandNodeTrait; - - fn deref(&self) -> &Self::Target { - match self { - CommandNodeEnum::Literal(node) => *node as &Self::Target, - CommandNodeEnum::Argument(node) => *node as &Self::Target, - CommandNodeEnum::Root(node) => *node as &Self::Target, - } - } -} - -impl From> for CommandNodeEnum<'_, S> { - fn from(node: LiteralCommandNode<'_, S>) -> Self { - CommandNodeEnum::Literal(node) - } -} - -impl From> for CommandNodeEnum<'_, S> { - fn from(node: ArgumentCommandNode<'_, S>) -> Self { - CommandNodeEnum::Argument(node) - } -} - -impl From> for CommandNodeEnum<'_, S> { - fn from(node: RootCommandNode<'_, S>) -> Self { - CommandNodeEnum::Root(node) - } -} - -impl CommandNodeEnum<'_, S> { +impl CommandNodeEnum { fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { (*self).modifier.as_ref().map(|modifier| modifier.as_ref()) } @@ -92,10 +63,10 @@ impl CommandNodeEnum<'_, S> { } } } -pub struct BaseCommandNode<'a, S> { +pub struct BaseCommandNode { children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, + literals: HashMap>, + arguments: HashMap>, pub requirement: Box bool>, redirect: Option>>, modifier: Option>>, @@ -118,7 +89,7 @@ pub struct BaseCommandNode<'a, S> { // } // } -impl Debug for BaseCommandNode<'_, S> { +impl Debug for BaseCommandNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("BaseCommandNode") .field("children", &self.children) @@ -133,7 +104,7 @@ impl Debug for BaseCommandNode<'_, S> { } } -impl Default for BaseCommandNode<'_, S> { +impl Default for BaseCommandNode { fn default() -> Self { Self { children: HashMap::new(), @@ -148,6 +119,7 @@ impl Default for BaseCommandNode<'_, S> { } } +#[enum_dispatch] pub trait CommandNodeTrait { fn name(&self) -> &str; fn usage_text(&self) -> &str; @@ -164,4 +136,8 @@ pub trait CommandNodeTrait { fn is_valid_input(&self, input: &str) -> bool; fn create_builder(&self) -> Box>; fn get_examples(&self) -> Vec; + + fn redirect_modifier(&self) -> Option<&dyn RedirectModifier>; + fn can_use(&self, source: S) -> bool; + fn add_child(&self, node: &Box>) -> Result<(), String>; } diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index 253b6dc5..2db31d97 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -3,33 +3,45 @@ use crate::{ builder::{ argument_builder::ArgumentBuilder, literal_argument_builder::LiteralArgumentBuilder, }, + command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, immutable_string_reader::ImmutableStringReader, + redirect_modifier::RedirectModifier, string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; -use super::command_node::{BaseCommandNode, CommandNodeTrait}; +use super::{ + argument_command_node::ArgumentCommandNode, + command_node::{BaseCommandNode, CommandNodeTrait}, +}; #[derive(Debug, Clone)] -pub struct LiteralCommandNode<'a, S> { +pub struct LiteralCommandNode { literal: String, literal_lowercase: String, - // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S>, + + children: HashMap>>, + literals: HashMap>, + arguments: HashMap>, + pub requirement: Box bool>, + redirect: Option>>, + modifier: Option>>, + forks: bool, + pub command: Option>>, } -impl<'a, S> LiteralCommandNode<'a, S> { - pub fn new(literal: String, base: BaseCommandNode<'a, S>) -> Self { +impl LiteralCommandNode { + pub fn new(literal: String) -> Self { let literal_lowercase = literal.to_lowercase(); Self { literal, literal_lowercase, - base, + ..Default::default() } } @@ -54,7 +66,7 @@ impl<'a, S> LiteralCommandNode<'a, S> { } } -impl CommandNodeTrait for LiteralCommandNode<'_, S> { +impl CommandNodeTrait for LiteralCommandNode { fn name(&self) -> &str { &self.literal } diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index 0fcb8d97..6b8bc157 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -6,6 +6,7 @@ use super::{ use crate::{ arguments::argument_type::ArgumentType, builder::argument_builder::ArgumentBuilder, + command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, @@ -16,16 +17,24 @@ use crate::{ }; use std::{ any::Any, + collections::HashMap, fmt::{Debug, Display, Formatter}, }; -#[derive(Clone, Default, Debug)] -pub struct RootCommandNode<'a, S> { +#[derive(Default)] +pub struct RootCommandNode { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S>, + children: HashMap>>, + literals: HashMap>, + arguments: HashMap>, + pub requirement: Box bool>, + redirect: Option>>, + modifier: Option>>, + forks: bool, + pub command: Option>>, } -impl CommandNodeTrait for RootCommandNode<'_, S> { +impl CommandNodeTrait for RootCommandNode { fn name(&self) -> &str { "" } @@ -63,7 +72,7 @@ impl CommandNodeTrait for RootCommandNode<'_, S> { } } -impl Display for RootCommandNode<'_, S> { +impl Display for RootCommandNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") } From 747bfa568cb1da8afdeb29005bdd2836963ed613 Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 16 Apr 2022 14:56:27 -0500 Subject: [PATCH 29/83] Add doc to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ea8c4bf7..4990fff7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/doc From a72a47ced76065caf739898954cd18edbc39174b Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 14:02:13 -0500 Subject: [PATCH 30/83] Rewrite brigadier --- azalea-brigadier/src/ambiguity_consumer.rs | 1 - .../src/arguments/argument_type.rs | 60 ---- .../src/arguments/bool_argument_type.rs | 59 ---- .../src/arguments/double_argument_type.rs | 1 - .../src/arguments/float_argument_type.rs | 1 - .../src/arguments/integer_argument_type.rs | 1 - .../src/arguments/long_argument_type.rs | 1 - azalea-brigadier/src/arguments/mod.rs | 7 - .../src/arguments/string_argument_type.rs | 1 - .../src/builder/argument_builder.rs | 297 ++++++++++-------- .../src/builder/literal_argument_builder.rs | 62 ++-- .../src/builder/required_argument_builder.rs | 121 +++---- azalea-brigadier/src/command.rs | 12 - azalea-brigadier/src/command_dispatcher.rs | 46 --- azalea-brigadier/src/context.rs | 145 +++++++++ .../src/context/command_context.rs | 93 ------ .../src/context/command_context_builder.rs | 176 ----------- azalea-brigadier/src/context/mod.rs | 6 - .../src/context/parsed_argument.rs | 24 -- .../src/context/parsed_command_node.rs | 30 -- .../src/context/suggestion_context.rs | 6 - azalea-brigadier/src/dispatcher.rs | 242 ++++++++++++++ .../exceptions/builtin_exception_provider.rs | 1 - .../src/exceptions/builtin_exceptions.rs | 16 +- .../exceptions/command_syntax_exception.rs | 6 +- azalea-brigadier/src/exceptions/mod.rs | 1 - .../src/immutable_string_reader.rs | 12 - azalea-brigadier/src/lib.rs | 73 +++-- azalea-brigadier/src/literal_message.rs | 1 - azalea-brigadier/src/main.rs | 38 +++ azalea-brigadier/src/message.rs | 4 +- azalea-brigadier/src/modifier.rs | 9 + azalea-brigadier/src/parse_results.rs | 20 ++ azalea-brigadier/src/parsers.rs | 21 ++ azalea-brigadier/src/redirect_modifier.rs | 11 - azalea-brigadier/src/result_consumer.rs | 1 - .../src/single_redirect_modifier.rs | 8 - .../src/{context => }/string_range.rs | 4 +- azalea-brigadier/src/string_reader.rs | 189 ++++++----- .../src/suggestion/integer_suggestion.rs | 1 - azalea-brigadier/src/suggestion/mod.rs | 5 - azalea-brigadier/src/suggestion/suggestion.rs | 90 ------ .../src/suggestion/suggestion_provider.rs | 14 - .../src/suggestion/suggestions.rs | 141 --------- .../src/suggestion/suggestions_builder.rs | 116 ------- azalea-brigadier/src/tree.rs | 269 ++++++++++++++++ .../src/tree/argument_command_node.rs | 176 ----------- azalea-brigadier/src/tree/command_node.rs | 143 --------- .../src/tree/literal_command_node.rs | 131 -------- azalea-brigadier/src/tree/mod.rs | 4 - .../src/tree/root_command_node.rs | 79 ----- 51 files changed, 1127 insertions(+), 1849 deletions(-) delete mode 100644 azalea-brigadier/src/ambiguity_consumer.rs delete mode 100644 azalea-brigadier/src/arguments/argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/bool_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/double_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/float_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/integer_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/long_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/mod.rs delete mode 100644 azalea-brigadier/src/arguments/string_argument_type.rs delete mode 100644 azalea-brigadier/src/command.rs delete mode 100644 azalea-brigadier/src/command_dispatcher.rs create mode 100644 azalea-brigadier/src/context.rs delete mode 100644 azalea-brigadier/src/context/command_context.rs delete mode 100644 azalea-brigadier/src/context/command_context_builder.rs delete mode 100644 azalea-brigadier/src/context/mod.rs delete mode 100644 azalea-brigadier/src/context/parsed_argument.rs delete mode 100644 azalea-brigadier/src/context/parsed_command_node.rs delete mode 100644 azalea-brigadier/src/context/suggestion_context.rs create mode 100644 azalea-brigadier/src/dispatcher.rs delete mode 100644 azalea-brigadier/src/exceptions/builtin_exception_provider.rs delete mode 100644 azalea-brigadier/src/immutable_string_reader.rs delete mode 100644 azalea-brigadier/src/literal_message.rs create mode 100644 azalea-brigadier/src/main.rs create mode 100644 azalea-brigadier/src/modifier.rs create mode 100644 azalea-brigadier/src/parsers.rs delete mode 100644 azalea-brigadier/src/redirect_modifier.rs delete mode 100644 azalea-brigadier/src/result_consumer.rs delete mode 100644 azalea-brigadier/src/single_redirect_modifier.rs rename azalea-brigadier/src/{context => }/string_range.rs (87%) delete mode 100644 azalea-brigadier/src/suggestion/integer_suggestion.rs delete mode 100644 azalea-brigadier/src/suggestion/mod.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestion.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestion_provider.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestions.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestions_builder.rs create mode 100644 azalea-brigadier/src/tree.rs delete mode 100644 azalea-brigadier/src/tree/argument_command_node.rs delete mode 100644 azalea-brigadier/src/tree/command_node.rs delete mode 100644 azalea-brigadier/src/tree/literal_command_node.rs delete mode 100644 azalea-brigadier/src/tree/mod.rs delete mode 100644 azalea-brigadier/src/tree/root_command_node.rs diff --git a/azalea-brigadier/src/ambiguity_consumer.rs b/azalea-brigadier/src/ambiguity_consumer.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/ambiguity_consumer.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs deleted file mode 100644 index 37cc9354..00000000 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::any::Any; - -use super::bool_argument_type::BoolArgumentType; -use crate::{ - context::command_context::CommandContext, - exceptions::command_syntax_exception::CommandSyntaxException, - string_reader::StringReader, - suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, -}; -use dyn_clonable::*; - -/* -#[derive(Types)] -enum BrigadierTypes { - Entity(EntityArgumentType) -} - -=== - -enum BrigadierTypes { - Bool(BoolArgumentType) - - Entity(EntityArgumentType) -} - -impl Types for BrigadierTypes { - fn inner(&self) -> dyn ArgumentType { - match self { - Bool(t) => t, - Entity(t) => t - } - } -} -*/ - -pub trait ArgumentType { - type Into; - // T parse(StringReader reader) throws CommandSyntaxException; - - // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { - // return Suggestions.empty(); - // } - - // default Collection getExamples() { - // return Collections.emptyList(); - // } - - fn parse(&self, reader: &mut StringReader) -> Result; - - fn list_suggestions( - &self, - context: &CommandContext, - builder: &SuggestionsBuilder, - ) -> Result - where - Self: Sized, - S: Sized; - - fn get_examples(&self) -> Vec; -} diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs deleted file mode 100644 index c06f40c1..00000000 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{ - context::command_context::CommandContext, - exceptions::command_syntax_exception::CommandSyntaxException, - string_reader::StringReader, - suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, -}; - -use super::argument_type::ArgumentType; - -#[derive(Clone)] -pub struct BoolArgumentType {} - -impl ArgumentType for BoolArgumentType { - type Into = bool; - - fn parse(&self, reader: &mut StringReader) -> Result { - Ok(reader.read_boolean()?) - } - - fn list_suggestions( - &self, - context: &CommandContext, - builder: &mut SuggestionsBuilder, - ) -> Result - where - S: Sized, - { - // if ("true".startsWith(builder.getRemainingLowerCase())) { - // builder.suggest("true"); - // } - // if ("false".startsWith(builder.getRemainingLowerCase())) { - // builder.suggest("false"); - // } - // return builder.buildFuture(); - if "true".starts_with(builder.remaining_lowercase()) { - builder.suggest("true"); - } - if "false".starts_with(builder.remaining_lowercase()) { - builder.suggest("false"); - } - Ok(builder.build()) - } - - fn get_examples(&self) -> Vec { - vec![] - } -} - -impl BoolArgumentType { - const EXAMPLES: &'static [&'static str] = &["true", "false"]; - - fn bool() -> Self { - Self {} - } - - fn get_bool(context: CommandContext, name: String) { - context.get_argument::(name) - } -} diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/arguments/double_argument_type.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/arguments/float_argument_type.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/arguments/integer_argument_type.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/arguments/long_argument_type.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs deleted file mode 100644 index 487c5db7..00000000 --- a/azalea-brigadier/src/arguments/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod argument_type; -pub mod bool_argument_type; -pub mod double_argument_type; -pub mod float_argument_type; -pub mod integer_argument_type; -pub mod long_argument_type; -pub mod string_argument_type; diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/arguments/string_argument_type.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index d0770be2..1fb775c2 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,137 +1,178 @@ -use crate::{ - arguments::argument_type::ArgumentType, - command::Command, - redirect_modifier::RedirectModifier, - single_redirect_modifier::SingleRedirectModifier, - tree::{ - command_node::{BaseCommandNode, CommandNodeTrait}, - root_command_node::RootCommandNode, - }, -}; -use std::fmt::Debug; +use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode}; -pub struct BaseArgumentBuilder<'a, S> { - arguments: RootCommandNode<'a, S>, - command: Option>>, - requirement: Box bool>, - target: Option>>, - modifier: Option>>, +use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; +use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, rc::Rc}; + +#[derive(Debug, Clone)] +pub enum ArgumentBuilderType { + Literal(Literal), + Argument(Argument), +} + +/// A node that hasn't yet been built. +#[derive(Clone)] +pub struct ArgumentBuilder { + value: ArgumentBuilderType, + + children: BTreeMap>>>, + literals: BTreeMap>>>, + arguments: BTreeMap>>>, + + executes: Option) -> i32>>, + requirement: Rc) -> bool>, forks: bool, + modifier: Option>>, } -pub trait ArgumentBuilder { - fn build(self) -> Box>; -} +// todo: maybe remake this to be based on a CommandNode like vanilla does? -impl<'a, S> BaseArgumentBuilder<'a, S> { - pub fn then(&mut self, argument: Box>) -> Result<&mut Self, String> { - if self.target.is_some() { - return Err("Cannot add children to a redirected node".to_string()); - } - self.arguments.add_child(argument.build()); - Ok(self) - } - - pub fn arguments(&self) -> &Vec<&dyn CommandNodeTrait> { - &self.arguments.get_children() - } - - pub fn executes(&mut self, command: Box>) -> &mut Self { - self.command = Some(command); - self - } - - pub fn command(&self) -> Option>> { - self.command - } - - pub fn requires(&mut self, requirement: Box bool>) -> &mut Self { - self.requirement = requirement; - self - } - - pub fn requirement(&self) -> Box bool> { - self.requirement - } - - pub fn redirect(&mut self, target: Box>) -> &mut Self { - self.forward(target, None, false) - } - - pub fn redirect_modifier( - &mut self, - target: &dyn CommandNodeTrait, - modifier: &dyn SingleRedirectModifier, - ) -> &mut Self { - // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false); - self.forward(target, modifier.map(|m| |o| vec![m.apply(o)]), false) - } - - pub fn fork( - &mut self, - target: &dyn CommandNodeTrait, - modifier: &dyn RedirectModifier, - ) -> &mut Self { - self.forward(target, Some(modifier), true) - } - - pub fn forward( - &mut self, - target: Option>>, - modifier: Option<&dyn RedirectModifier>, - fork: bool, - ) -> Result<&mut Self, String> { - if !self.arguments.get_children().is_empty() { - return Err("Cannot forward a node with children".to_string()); - } - self.target = target; - self.modifier = modifier; - self.forks = fork; - Ok(self) - } - - pub fn get_redirect(&self) -> Option<&dyn CommandNodeTrait> { - self.target.as_ref() - } - - pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - self.modifier.as_ref() - } - - pub fn is_fork(&self) -> bool { - self.forks - } - - pub fn build(self) -> BaseCommandNode<'a, S> { - let result: BaseCommandNode<'a, S> = BaseCommandNode { - command: self.command, - requirement: self.requirement, - redirect: self.target, - modifier: self.modifier, - forks: self.forks, - - arguments: Default::default(), - children: Default::default(), - literals: Default::default(), - }; - - for argument in self.arguments() { - result.add_child(argument); - } - - result - } -} - -impl Default for BaseArgumentBuilder<'_, S> { - fn default() -> Self { +/// A node that isn't yet built. +impl ArgumentBuilder { + pub fn new(value: ArgumentBuilderType) -> Self { Self { - arguments: Default::default(), - command: Default::default(), - requirement: Default::default(), - target: Default::default(), - modifier: Default::default(), - forks: Default::default(), + value, + children: BTreeMap::new(), + literals: BTreeMap::new(), + arguments: BTreeMap::new(), + executes: None, + requirement: Rc::new(|_| true), + forks: false, + modifier: None, + } + } + + pub fn then(&mut self, node: ArgumentBuilder) -> &mut Self { + let built_node = node.build(); + let name = built_node.name(); + let node_reference = Rc::new(RefCell::new(built_node.clone())); + self.children + .insert(name.to_string(), node_reference.clone()); + match &built_node.value { + ArgumentBuilderType::Literal(literal) => { + self.literals.insert(name.to_string(), node_reference); + } + ArgumentBuilderType::Argument(argument) => { + self.arguments.insert(name.to_string(), node_reference); + } + } + self + } + + pub fn executes(&mut self, f: F) -> Self + where + F: Fn(&CommandContext) -> i32 + 'static, + { + self.executes = Some(Rc::new(f)); + self.clone() + } + + pub fn build(self) -> CommandNode { + println!("building {:?}", self); + CommandNode { + value: self.value, + + children: self.children, + literals: self.literals, + arguments: self.arguments, + + command: self.executes.clone(), + requirement: self.requirement.clone(), + redirect: None, + forks: self.forks, + modifier: self.modifier, } } } + +impl Debug for ArgumentBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ArgumentBuilder") + .field("value", &self.value) + .field("children", &self.children) + .field("literals", &self.literals) + .field("arguments", &self.arguments) + .field("executes", &self.executes.is_some()) + // .field("requirement", &self.requirement) + .field("forks", &self.forks) + // .field("modifier", &self.modifier) + .finish() + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use crate::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + parsers::integer, + }; + + use super::ArgumentBuilder; + + // public class ArgumentBuilderTest { + // private TestableArgumentBuilder builder; + + // @Before + // public void setUp() throws Exception { + // builder = new TestableArgumentBuilder<>(); + // } + + // @Test + // public void testArguments() throws Exception { + // final RequiredArgumentBuilder argument = argument("bar", integer()); + + // builder.then(argument); + + // assertThat(builder.getArguments(), hasSize(1)); + // assertThat(builder.getArguments(), hasItem((CommandNode) argument.build())); + // } + + #[test] + fn test_arguments() { + let mut builder: ArgumentBuilder<()> = literal("foo"); + + let argument: ArgumentBuilder<()> = argument("bar", integer()); + builder.then(argument.clone()); + assert_eq!(builder.children.len(), 1); + let built_argument = Rc::new(argument.build()); + assert!(builder + .children + .values() + .any(|e| *e.borrow() == *built_argument)); + } + + // @Test + // public void testRedirect() throws Exception { + // final CommandNode target = mock(CommandNode.class); + // builder.redirect(target); + // assertThat(builder.getRedirect(), is(target)); + // } + + // @Test(expected = IllegalStateException.class) + // public void testRedirect_withChild() throws Exception { + // final CommandNode target = mock(CommandNode.class); + // builder.then(literal("foo")); + // builder.redirect(target); + // } + + // @Test(expected = IllegalStateException.class) + // public void testThen_withRedirect() throws Exception { + // final CommandNode target = mock(CommandNode.class); + // builder.redirect(target); + // builder.then(literal("foo")); + // } + + // private static class TestableArgumentBuilder extends ArgumentBuilder> { + // @Override + // protected TestableArgumentBuilder getThis() { + // return this; + // } + + // @Override + // public CommandNode build() { + // return null; + // } + // } + // } +} diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index 4a95755c..d8898540 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,51 +1,35 @@ -use super::argument_builder::{ArgumentBuilder, BaseArgumentBuilder}; +use std::any::Any; + use crate::{ - arguments::argument_type::ArgumentType, - command::Command, - redirect_modifier::RedirectModifier, - tree::{ - command_node::CommandNodeTrait, literal_command_node::LiteralCommandNode, - root_command_node::RootCommandNode, + context::CommandContextBuilder, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, + string_range::StringRange, + string_reader::StringReader, }; -use std::fmt::Debug; -pub struct LiteralArgumentBuilder { - arguments: RootCommandNode, - command: Option>>, - requirement: Box bool>, - target: Option>>, - modifier: Option>>, - forks: bool, - literal: String, +use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; + +#[derive(Debug, Clone, Default)] +pub struct Literal { + pub value: String, } - -impl LiteralArgumentBuilder { - pub fn new(literal: String) -> Self { +impl Literal { + pub fn new(value: &str) -> Self { Self { - literal, - arguments: RootCommandNode::new(), - command: None, - requirement: Box::new(|_| true), - target: None, - modifier: None, - forks: false, + value: value.to_string(), } } - - pub fn literal(name: String) -> Self { - Self::new(name) - } } -impl ArgumentBuilder for LiteralArgumentBuilder { - fn build(self) -> Box> { - let result = LiteralCommandNode::new(self.literal, self.base.build()); - - for argument in self.base.arguments() { - result.add_child(argument); - } - - Box::new(result) +impl From for ArgumentBuilderType { + fn from(literal: Literal) -> Self { + Self::Literal(literal) } } + +/// Shortcut for creating a new literal builder node. +pub fn literal(value: &str) -> ArgumentBuilder { + ArgumentBuilder::new(ArgumentBuilderType::Literal(Literal::new(value))) +} diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index fe6f2ecc..95f4da01 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,91 +1,46 @@ -use super::argument_builder::BaseArgumentBuilder; -use crate::{ - arguments::argument_type::ArgumentType, - command::Command, - redirect_modifier::RedirectModifier, - suggestion::suggestion_provider::SuggestionProvider, - tree::{ - argument_command_node::ArgumentCommandNode, - command_node::{BaseCommandNode, CommandNodeTrait}, - root_command_node::RootCommandNode, - }, -}; -use std::any::Any; -use std::fmt::Debug; +use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; +use crate::{parsers::Parser, string_reader::StringReader}; +use std::{any::Any, fmt::Debug, rc::Rc}; -pub struct RequiredArgumentBuilder<'a, S> { - arguments: RootCommandNode<'a, S>, - command: Option>>, - requirement: Box bool>, - target: Option>>, - modifier: Option>>, - forks: bool, - - name: String, - type_: Box>, - suggestions_provider: Option>>, +/// An argument node type. The `T` type parameter is the type of the argument, +/// which can be anything. +#[derive(Clone)] +pub struct Argument { + pub name: String, + parser: Rc, } - -impl<'a, S> RequiredArgumentBuilder<'a, S> { - pub fn new(name: String, type_: Box>) -> Self { +impl Argument { + pub fn new(name: &str, parser: Rc) -> Self { Self { - name, - type_: type_, - suggestions_provider: None, - arguments: RootCommandNode::new(), - command: None, - requirement: Box::new(|_| true), - target: None, - modifier: None, - forks: false, + name: name.to_string(), + parser: parser, } } - pub fn argument(name: String, type_: Box>) -> Self { - Self::new(name, type_) - } - - pub fn suggests(mut self, provider: Box>) -> Self { - self.suggestions_provider = Some(provider); - self - } - - pub fn suggestions_provider(&self) -> Option>> { - self.suggestions_provider - } - - pub fn get_type(&self) -> Box> { - self.type_ - } - - pub fn name(&self) -> &str { - &self.name - } - - // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); - - // for (final CommandNode argument : getArguments()) { - // result.addChild(argument); - // } - - // return result; - pub fn build(self) -> ArgumentCommandNode<'a, S> { - let result = ArgumentCommandNode { - name: self.name, - type_: self.type_, - command: self.base.command(), - requirement: self.base.requirement(), - redirect: self.base.get_redirect(), - modifier: self.base.get_redirect_modifier(), - forks: self.base.forks, - custom_suggestions: self.base.custom_suggestions, - ..ArgumentCommandNode::default() - }; - - for argument in self.base.arguments() { - result.add_child(argument); - } - - result + pub fn parse(&self, reader: &mut StringReader) -> Option> { + self.parser.parse(reader) } } + +impl From for ArgumentBuilderType { + fn from(argument: Argument) -> Self { + Self::Argument(argument) + } +} + +impl Debug for Argument { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Argument") + .field("name", &self.name) + // .field("parser", &self.parser) + .finish() + } +} + +/// Shortcut for creating a new argument builder node. +pub fn argument<'a, S: Any + Clone>( + name: &'a str, + parser: impl Parser + 'static, +) -> ArgumentBuilder { + ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) +} diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs deleted file mode 100644 index a529f23c..00000000 --- a/azalea-brigadier/src/command.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::{ - context::command_context::CommandContext, - exceptions::command_syntax_exception::CommandSyntaxException, -}; -use dyn_clonable::*; - -pub const SINGLE_SUCCESS: i32 = 1; - -#[clonable] -pub trait Command: Clone { - fn run(&self, context: &mut CommandContext) -> Result; -} diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs deleted file mode 100644 index f8ffddff..00000000 --- a/azalea-brigadier/src/command_dispatcher.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::{arguments::argument_type::ArgumentType, tree::root_command_node::RootCommandNode}; -use std::fmt::Debug; - -/// The core command dispatcher, for registering, parsing, and executing commands. -/// The `S` generic is a custom "source" type, such as a user or originator of a command -#[derive(Default, Clone)] -pub struct CommandDispatcher { - root: RootCommandNode, -} - -impl CommandDispatcher { - /// The string required to separate individual arguments in an input string - /// - /// See: [`ARGUMENT_SEPARATOR_CHAR`] - const ARGUMENT_SEPARATOR: &'static str = " "; - - /// The char required to separate individual arguments in an input string - /// - /// See: [`ARGUMENT_SEPARATOR`] - const ARGUMENT_SEPARATOR_CHAR: char = ' '; - - const USAGE_OPTIONAL_OPEN: &'static str = "["; - const USAGE_OPTIONAL_CLOSE: &'static str = "]"; - const USAGE_REQUIRED_OPEN: &'static str = "("; - const USAGE_REQUIRED_CLOSE: &'static str = ")"; - const USAGE_OR: &'static str = "|"; - - /// Create a new [`CommandDispatcher`] with the specified root node. - /// This is often useful to copy existing or pre-defined command trees. - /// # Example - /// ``` - /// use azalea_brigadier::{ - /// command_dispatcher::CommandDispatcher, - /// tree::root_command_node::RootCommandNode, - /// }; - /// - /// let mut dispatcher = CommandDispatcher::new(RootCommandNode::new()); - /// ``` - /// # Arguments - /// * `root` - the existing [`RootCommandNode`] to use as the basis for this tree - /// # Returns - /// A new [`CommandDispatcher`] with the specified root node. - fn new(root: RootCommandNode) -> Self { - Self { root } - } -} diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs new file mode 100644 index 00000000..6d4dec88 --- /dev/null +++ b/azalea-brigadier/src/context.rs @@ -0,0 +1,145 @@ +use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, ptr, rc::Rc}; + +use crate::{ + dispatcher::CommandDispatcher, modifier::RedirectModifier, string_range::StringRange, + tree::CommandNode, +}; + +#[derive(Clone)] +pub struct CommandContextBuilder { + pub arguments: HashMap, + pub root: Rc>>, + pub nodes: Vec>>, + pub dispatcher: Rc>, + pub source: Rc, + pub command: Option) -> i32>>, + pub child: Option>>, + pub range: StringRange, + pub modifier: Option>>, + pub forks: bool, +} + +impl CommandContextBuilder { + // CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start + pub fn new( + dispatcher: Rc>, + source: Rc, + root_node: Rc>>, + start: usize, + ) -> Self { + Self { + arguments: HashMap::new(), + root: root_node, + source, + range: StringRange::at(start), + command: None, + dispatcher, + nodes: vec![], + // rootNode, + // start, + child: None, + modifier: None, + forks: false, + } + } + + pub fn with_command( + &mut self, + command: &Option) -> i32>>, + ) -> &Self { + self.command = command.clone(); + self + } + pub fn with_child(&mut self, child: Rc>) -> &Self { + self.child = Some(child); + self + } + pub fn with_argument(&mut self, name: &str, argument: ParsedArgument) -> &Self { + self.arguments.insert(name.to_string(), argument); + self + } + pub fn with_node(&mut self, node: Rc>, range: StringRange) -> &Self { + self.nodes.push(node.clone()); + self.range = StringRange::encompassing(&self.range, &range); + self.modifier = node.modifier.clone(); + self.forks = node.forks; + self + } + + pub fn build(&self, input: &str) -> CommandContext { + CommandContext { + arguments: self.arguments.clone(), + root_node: self.root.clone(), + nodes: self.nodes.clone(), + source: self.source.clone(), + command: self.command.clone(), + child: self.child.clone().map(|c| Rc::new(c.build(&input))), + range: self.range.clone(), + forks: self.forks, + modifier: self.modifier.clone(), + input: input.to_string(), + } + } +} + +impl Debug for CommandContextBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandContextBuilder") + // .field("arguments", &self.arguments) + .field("root", &self.root) + .field("nodes", &self.nodes) + // .field("dispatcher", &self.dispatcher) + // .field("source", &self.source) + // .field("command", &self.command) + .field("child", &self.child) + .field("range", &self.range) + // .field("modifier", &self.modifier) + .field("forks", &self.forks) + .finish() + } +} + +#[derive(Clone)] +pub struct ParsedArgument { + pub range: StringRange, + pub result: Rc, +} + +#[derive(Clone)] +/// A built `CommandContextBuilder`. +pub struct CommandContext { + pub source: Rc, + pub input: String, + pub arguments: HashMap, + pub command: Option) -> i32>>, + pub root_node: Rc>>, + pub nodes: Vec>>, + pub range: StringRange, + pub child: Option>>, + pub modifier: Option>>, + pub forks: bool, +} + +impl CommandContext { + pub fn copy_for(&self, source: Rc) -> Self { + if Rc::ptr_eq(&source, &self.source) { + return self.clone(); + } + return CommandContext { + source, + input: self.input.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + root_node: self.root_node.clone(), + nodes: self.nodes.clone(), + range: self.range.clone(), + child: self.child.clone(), + modifier: self.modifier.clone(), + forks: self.forks, + }; + } + + pub fn has_nodes(&self) -> bool { + return !self.nodes.is_empty(); + } +} diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs deleted file mode 100644 index 8db1487f..00000000 --- a/azalea-brigadier/src/context/command_context.rs +++ /dev/null @@ -1,93 +0,0 @@ -use super::{ - parsed_argument::ParsedArgument, parsed_command_node::ParsedCommandNode, - string_range::StringRange, -}; -use crate::{ - arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, - tree::command_node::CommandNodeTrait, -}; -use std::{any::Any, collections::HashMap}; - -pub struct CommandContext<'a, S> { - source: S, - input: String, - command: &'a dyn Command, - arguments: HashMap>>, - root_node: &'a dyn CommandNodeTrait, - nodes: Vec>, - range: StringRange, - child: Option<&'a CommandContext<'a, S>>, - modifier: Option<&'a dyn RedirectModifier>, - forks: bool, -} - -impl CommandContext<'_, S> -where - S: PartialEq, -{ - pub fn clone_for(&self, source: S) -> Self { - if self.source == source { - return *self; - } - Self { - source, - input: self.input.clone(), - command: self.command.clone(), - arguments: self.arguments.clone(), - root_node: self.root_node.clone(), - nodes: self.nodes.clone(), - range: self.range.clone(), - child: self.child.clone(), - modifier: self.modifier.clone(), - forks: self.forks, - } - } - - fn child(&self) -> &Option> { - &self.child - } - - fn last_child(&self) -> &CommandContext { - let mut result = self; - while result.child.is_some() { - result = result.child.as_ref().unwrap(); - } - result - } - - fn command(&self) -> &dyn Command { - &self.command - } - - fn source(&self) -> &S { - &self.source - } - - // public V getArgument(final String name, final Class clazz) { - // final ParsedArgument argument = arguments.get(name); - - // if (argument == null) { - // throw new IllegalArgumentException("No such argument '" + name + "' exists on this command"); - // } - - // final Object result = argument.getResult(); - // if (PRIMITIVE_TO_WRAPPER.getOrDefault(clazz, clazz).isAssignableFrom(result.getClass())) { - // return (V) result; - // } else { - // throw new IllegalArgumentException("Argument '" + name + "' is defined as " + result.getClass().getSimpleName() + ", not " + clazz); - // } - // } - fn get_argument(&self, name: &str) -> Result { - let argument = self.arguments.get(name); - - if argument.is_none() { - return Err(format!( - "No such argument '{}' exists on this command", - name - )); - } - - let result = argument.unwrap().result(); - Ok(result) - } -} diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs deleted file mode 100644 index ba25849c..00000000 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::{ - arguments::argument_type::ArgumentType, command::Command, - command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, - tree::command_node::CommandNodeTrait, -}; -use std::fmt::Debug; -use std::{any::Any, collections::HashMap}; - -use super::{ - command_context::CommandContext, parsed_argument::ParsedArgument, - parsed_command_node::ParsedCommandNode, string_range::StringRange, - suggestion_context::SuggestionContext, -}; - -// public class CommandContextBuilder { -// private final Map> arguments = new LinkedHashMap<>(); -// private final CommandNode rootNode; -// private final List> nodes = new ArrayList<>(); -// private final CommandDispatcher dispatcher; -// private S source; -// private Command command; -// private CommandContextBuilder child; -// private StringRange range; -// private RedirectModifier modifier = null; -// private boolean forks; - -#[derive(Clone)] -pub struct CommandContextBuilder<'a, S> { - arguments: HashMap>>, - root_node: &'a dyn CommandNodeTrait, - nodes: Vec>, - dispatcher: CommandDispatcher<'a, S>, - source: S, - command: Box>, - child: Box>>, - range: StringRange, - modifier: Option>>, - forks: bool, -} - -// public CommandContextBuilder(final CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start) { -// this.rootNode = rootNode; -// this.dispatcher = dispatcher; -// this.source = source; -// this.range = StringRange.at(start); -// } - -impl CommandContextBuilder<'_, S> { - pub fn new( - dispatcher: CommandDispatcher, - source: S, - root_node: &dyn CommandNodeTrait, - start: usize, - ) -> Self { - Self { - root_node: &root_node, - dispatcher, - source, - range: StringRange::at(start), - ..Default::default() - } - } - - pub fn with_source(mut self, source: S) -> Self { - self.source = source; - self - } - - pub fn source(&self) -> &S { - &self.source - } - - pub fn root_node(&self) -> &dyn CommandNodeTrait { - &self.root_node - } - - pub fn with_argument(mut self, name: String, argument: ParsedArgument>) -> Self { - self.arguments.insert(name, argument); - self - } - - pub fn arguments(&self) -> &HashMap>> { - &self.arguments - } - - pub fn with_command(mut self, command: &dyn Command) -> Self { - self.command = command; - self - } - - pub fn with_node(mut self, node: dyn CommandNodeTrait, range: StringRange) -> Self { - self.nodes.push(ParsedCommandNode::new(node, range)); - self.range = StringRange::encompassing(&self.range, &range); - self.modifier = node.redirect_modifier(); - self.forks = node.is_fork(); - self - } - - pub fn with_child(mut self, child: CommandContextBuilder) -> Self { - self.child = Some(child); - self - } - - pub fn child(&self) -> Option<&CommandContextBuilder> { - self.child.as_ref() - } - - pub fn last_child(&self) -> Option<&CommandContextBuilder> { - let mut result = self; - while let Some(child) = result.child() { - result = child; - } - Some(result) - } - - pub fn command(&self) -> &dyn Command { - &*self.command - } - - pub fn nodes(&self) -> &Vec> { - &self.nodes - } - - pub fn build(self, input: &str) -> CommandContext { - CommandContext { - source: self.source, - input, - arguments: self.arguments, - command: self.command, - root_node: self.root_node, - nodes: self.nodes, - range: self.range, - child: self.child.map(|child| child.build(input)), - modifier: self.modifier, - forks: self.forks, - } - } - - pub fn dispatcher(&self) -> &CommandDispatcher { - &self.dispatcher - } - - pub fn range(&self) -> &StringRange { - &self.range - } - - pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { - if self.range.start() <= cursor { - if self.range.end() < cursor { - if let Some(child) = self.child() { - child.find_suggestion_context(cursor); - } else if !self.nodes.is_empty() { - let last = self.nodes.last().unwrap(); - let end = last.range().end() + 1; - return SuggestionContext::new(last.node(), end); - } else { - return SuggestionContext::new(self.root_node, self.range.start()); - } - } else { - let prev = self.root_node; - for node in &self.nodes { - let node_range = node.range(); - if node_range.start() <= cursor && cursor <= node_range.end() { - return SuggestionContext::new(prev, node_range.start()); - } - prev = node.node(); - } - if prev.is_none() { - return Err(String::from("Can't find node before cursor")); - } - return SuggestionContext::new(prev.unwrap(), self.range.start()); - } - } - Err(String::from("Can't find node before cursor")) - } -} diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs deleted file mode 100644 index 196d7c5b..00000000 --- a/azalea-brigadier/src/context/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod command_context; -pub mod command_context_builder; -pub mod parsed_argument; -pub mod parsed_command_node; -pub mod string_range; -pub mod suggestion_context; diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs deleted file mode 100644 index e0bdf97b..00000000 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::string_range::StringRange; - -#[derive(PartialEq, Eq, Hash, Clone)] -pub struct ParsedArgument { - range: StringRange, - result: T, -} - -impl ParsedArgument { - fn new(start: usize, end: usize, result: &T) -> Self { - Self { - range: StringRange::between(start, end), - result, - } - } - - fn range(&self) -> &StringRange { - &self.range - } - - fn result(&self) -> &T { - &self.result - } -} diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs deleted file mode 100644 index 21d1b2e9..00000000 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ /dev/null @@ -1,30 +0,0 @@ -use super::string_range::StringRange; -use crate::tree::command_node::CommandNodeTrait; - -pub struct ParsedCommandNode { - node: Box>, - range: StringRange, -} - -impl ParsedCommandNode { - fn new(node: dyn CommandNodeTrait, range: StringRange) -> Self { - Self { node, range } - } - - fn node(&self) -> &dyn CommandNodeTrait { - &self.node - } - - fn range(&self) -> &StringRange { - &self.range - } -} - -impl Clone for ParsedCommandNode { - fn clone_from(&mut self, source: &Self) { - Self { - node: self.node.clone(), - range: self.range.clone(), - } - } -} diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs deleted file mode 100644 index 51a053c1..00000000 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::tree::command_node::CommandNodeTrait; - -pub struct SuggestionContext<'a, S> { - parent: &'a dyn CommandNodeTrait, - start_pos: usize, -} diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs new file mode 100644 index 00000000..65fe5b0a --- /dev/null +++ b/azalea-brigadier/src/dispatcher.rs @@ -0,0 +1,242 @@ +use crate::{ + builder::argument_builder::ArgumentBuilder, + context::{CommandContext, CommandContextBuilder}, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + parse_results::ParseResults, + string_range::StringRange, + string_reader::StringReader, + tree::CommandNode, +}; +use std::{ + any::Any, cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc, +}; + +#[derive(Default)] +pub struct CommandDispatcher { + root: Rc>>, + _marker: PhantomData, +} + +impl CommandDispatcher { + pub fn new() -> Self { + Self { + root: Rc::new(RefCell::new(CommandNode::default())), + _marker: PhantomData, + } + } + + pub fn register(&mut self, node: ArgumentBuilder) { + println!("register {:#?}", node); + let build = Rc::new(RefCell::new(node.build())); + self.root.borrow_mut().add_child(&build); + // println!("build: {:#?}", build); + } + + pub fn parse(&self, command: StringReader, source: S) -> ParseResults { + let context = CommandContextBuilder::new( + Rc::new(self.clone()), + Rc::new(source), + self.root.clone(), + command.cursor(), + ); + self.parse_nodes(&self.root, &command, context).unwrap() + } + + fn parse_nodes( + &self, + node: &Rc>>, + original_reader: &StringReader, + context_so_far: CommandContextBuilder, + ) -> Result, CommandSyntaxException> { + let source = context_so_far.source.clone(); + let mut errors = HashMap::>, CommandSyntaxException>::new(); + let mut potentials: Vec> = vec![]; + let cursor = original_reader.cursor(); + + for child in node + .borrow() + .get_relevant_nodes(&mut original_reader.clone()) + { + if !child.borrow().can_use(source.clone()) { + continue; + } + let mut context = context_so_far.clone(); + let mut reader = original_reader.clone(); + + let parse_with_context_result = + child.borrow().parse_with_context(&mut reader, &mut context); + if let Err(ex) = parse_with_context_result { + errors.insert( + Rc::new((*child.borrow()).clone()), + BuiltInExceptions::DispatcherParseException { + message: ex.message(), + } + .create_with_context(&reader), + ); + reader.cursor = cursor; + continue; + } + if reader.can_read() { + if reader.peek() != ' ' { + errors.insert( + Rc::new((*child.borrow()).clone()), + BuiltInExceptions::DispatcherExpectedArgumentSeparator + .create_with_context(&reader), + ); + reader.cursor = cursor; + continue; + } + } + + context.with_command(&child.borrow().command); + if reader.can_read_length(if child.borrow().redirect.is_none() { + 2 + } else { + 1 + }) { + reader.skip(); + if let Some(redirect) = &child.borrow().redirect { + let child_context = CommandContextBuilder::new( + Rc::new(self.clone()), + source.clone(), + redirect.clone(), + reader.cursor, + ); + let parse = self + .parse_nodes(redirect, &reader, child_context) + .expect("Parsing nodes failed"); + context.with_child(Rc::new(parse.context)); + } else { + let parse = self + .parse_nodes(&child, &reader, context) + .expect("Parsing nodes failed"); + potentials.push(parse); + } + } else { + potentials.push(ParseResults { + context, + reader, + exceptions: HashMap::new(), + }); + } + } + + if potentials.len() > 0 { + if potentials.len() > 1 { + potentials.sort_by(|a, b| { + if !a.reader.can_read() && b.reader.can_read() { + return Ordering::Less; + }; + if a.reader.can_read() && !b.reader.can_read() { + return Ordering::Greater; + }; + if a.exceptions.is_empty() && !b.exceptions.is_empty() { + return Ordering::Less; + }; + if !a.exceptions.is_empty() && b.exceptions.is_empty() { + return Ordering::Greater; + }; + Ordering::Equal + }) + } + let best_potential = potentials.into_iter().next().unwrap(); + println!("chosen {:#?}", best_potential); + return Ok(best_potential); + } + + Ok(ParseResults { + context: context_so_far, + reader: original_reader.clone(), + exceptions: errors, + }) + } + + /// Executes a given pre-parsed command. + pub fn execute(parse: ParseResults) -> Result { + if parse.reader.can_read() { + println!("can read from reader {}", parse.reader.cursor); + if parse.exceptions.len() == 1 { + return Err(parse.exceptions.values().next().unwrap().clone()); + } + if parse.context.range.is_empty() { + return Err( + BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader) + ); + } + return Err( + BuiltInExceptions::DispatcherUnknownArgument.create_with_context(&parse.reader) + ); + } + println!("a"); + let mut result = 0i32; + let mut successful_forks = 0; + let mut forked = false; + let mut found_command = false; + let command = parse.reader.string(); + let original = parse.context.build(&command); + let mut contexts = vec![original]; + let mut next: Vec> = vec![]; + + while contexts.len() > 0 { + for context in contexts.iter() { + let child = &context.child; + if let Some(child) = child { + forked |= child.forks; + if child.has_nodes() { + found_command = true; + let modifier = &context.modifier; + if let Some(modifier) = modifier { + let results = modifier.apply(context); + if let Ok(results) = results { + if !results.is_empty() { + next.extend(results.iter().map(|s| child.copy_for(s.clone()))); + } + } else { + // TODO + // self.consumer.on_command_complete(context, false, 0); + if !forked { + return Err(results.err().unwrap()); + } + } + } else { + next.push(child.copy_for(context.source.clone())); + } + } + } else if let Some(context_command) = &context.command { + found_command = true; + + let value = context_command(context); + result += value; + // consumer.on_command_complete(context, true, value); + successful_forks += 1; + + // TODO: allow context_command to error and handle those errors + } + } + + // move next into contexts and clear next + mem::swap(&mut contexts, &mut next); + next.clear(); + } + + if !found_command { + // consumer.on_command_complete(original, false, 0); + return Err( + BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader) + ); + } + + Ok(if forked { successful_forks } else { result }) + } +} + +impl Clone for CommandDispatcher { + fn clone(&self) -> Self { + Self { + root: self.root.clone(), + _marker: PhantomData, + } + } +} diff --git a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs b/azalea-brigadier/src/exceptions/builtin_exception_provider.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index 1533364b..5f2e1605 100644 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::{immutable_string_reader::ImmutableStringReader, message::Message}; +use crate::{message::Message, string_reader::StringReader}; use super::command_syntax_exception::CommandSyntaxException; @@ -35,9 +35,9 @@ pub enum BuiltInExceptions { ReaderExpectedBool, ReaderExpectedSymbol { symbol: char }, - ReaderUnknownCommand, - ReaderUnknownArgument, - DusoatcgerExpectedArgumentSeparator, + DispatcherUnknownCommand, + DispatcherUnknownArgument, + DispatcherExpectedArgumentSeparator, DispatcherParseException { message: String }, } @@ -127,13 +127,13 @@ impl fmt::Debug for BuiltInExceptions { write!(f, "Expected '{}'", symbol) } - BuiltInExceptions::ReaderUnknownCommand => { + BuiltInExceptions::DispatcherUnknownCommand => { write!(f, "Unknown command") } - BuiltInExceptions::ReaderUnknownArgument => { + BuiltInExceptions::DispatcherUnknownArgument => { write!(f, "Incorrect argument for command") } - BuiltInExceptions::DusoatcgerExpectedArgumentSeparator => { + BuiltInExceptions::DispatcherExpectedArgumentSeparator => { write!( f, "Expected whitespace to end one argument, but found trailing data" @@ -152,7 +152,7 @@ impl BuiltInExceptions { CommandSyntaxException::create(self, message) } - pub fn create_with_context(self, reader: &dyn ImmutableStringReader) -> CommandSyntaxException { + pub fn create_with_context(self, reader: &StringReader) -> CommandSyntaxException { let message = Message::from(format!("{:?}", self)); CommandSyntaxException::new(self, message, reader.string(), reader.cursor()) } diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index 38aa1c3a..6cd4e53d 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -3,6 +3,7 @@ use std::{cmp, fmt, rc::Rc}; use super::builtin_exceptions::BuiltInExceptions; use crate::message::Message; +#[derive(Clone)] pub struct CommandSyntaxException { type_: BuiltInExceptions, message: Message, @@ -59,7 +60,10 @@ impl CommandSyntaxException { builder.push_str("..."); } - builder.push_str(&input[cmp::max(0, cursor - CONTEXT_AMOUNT)..cursor]); + builder.push_str( + &input + [(cmp::max(0, cursor as isize - CONTEXT_AMOUNT as isize) as usize)..cursor], + ); builder.push_str("<--[HERE]"); return Some(builder); diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs index 4a82b01e..0bca556e 100644 --- a/azalea-brigadier/src/exceptions/mod.rs +++ b/azalea-brigadier/src/exceptions/mod.rs @@ -1,3 +1,2 @@ -pub mod builtin_exception_provider; pub mod builtin_exceptions; pub mod command_syntax_exception; diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs deleted file mode 100644 index 53531c64..00000000 --- a/azalea-brigadier/src/immutable_string_reader.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub trait ImmutableStringReader { - fn string(&self) -> &str; - fn remaining_length(&self) -> usize; - fn total_length(&self) -> usize; - fn cursor(&self) -> usize; - fn get_read(&self) -> &str; - fn remaining(&self) -> &str; - fn can_read_length(&self, length: usize) -> bool; - fn can_read(&self) -> bool; - fn peek(&self) -> char; - fn peek_offset(&self, offset: usize) -> char; -} diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index a1ac267c..476764d0 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,32 +1,55 @@ -#[macro_use] -extern crate lazy_static; - -#[macro_use] -extern crate enum_dispatch; - -mod ambiguity_consumer; -mod arguments; -mod builder; -mod command; -mod command_dispatcher; -mod context; -mod exceptions; -mod immutable_string_reader; -mod literal_message; -mod message; -mod parse_results; -mod redirect_modifier; -mod result_consumer; -mod single_redirect_modifier; -mod string_reader; -mod suggestion; -mod tree; +pub mod builder; +pub mod context; +pub mod dispatcher; +pub mod exceptions; +pub mod message; +pub mod modifier; +pub mod parse_results; +pub mod parsers; +pub mod string_range; +pub mod string_reader; +pub mod tree; #[cfg(test)] mod tests { + + use std::rc::Rc; + + use crate::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + dispatcher::CommandDispatcher, + parsers::integer, + }; + + struct CommandSourceStack { + player: String, + } + #[test] fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); + let mut dispatcher = CommandDispatcher::>::new(); + + let source = Rc::new(CommandSourceStack { + player: "player".to_string(), + }); + + dispatcher.register( + literal("foo") + .then(argument("bar", integer()).executes(|c| { + // println!("Bar is {}", get_integer(c, "bar")); + 2 + })) + .executes(|c| { + println!("Called foo with no arguments"); + 1 + }), + ); + + let parse = dispatcher.parse("foo 123".to_string().into(), source); + println!( + "{}", + CommandDispatcher::>::execute(parse).unwrap() + ); + // assert_eq!(dispatcher.execute("foo bar", source), 2); } } diff --git a/azalea-brigadier/src/literal_message.rs b/azalea-brigadier/src/literal_message.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/literal_message.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/main.rs b/azalea-brigadier/src/main.rs new file mode 100644 index 00000000..53f2efa8 --- /dev/null +++ b/azalea-brigadier/src/main.rs @@ -0,0 +1,38 @@ +use std::rc::Rc; + +use rust_command_parser::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + dispatcher::CommandDispatcher, + parsers::integer, +}; + +struct CommandSourceStack { + player: String, +} + +pub fn main() { + let mut dispatcher = CommandDispatcher::>::new(); + + let source = Rc::new(CommandSourceStack { + player: "player".to_string(), + }); + + dispatcher.register( + literal("foo") + .then(argument("bar", integer()).executes(|c| { + // println!("Bar is {}", get_integer(c, "bar")); + 2 + })) + .executes(|c| { + println!("Called foo with no arguments"); + 1 + }), + ); + + let parse = dispatcher.parse("foo 123".to_string().into(), source); + println!("{:?}", parse); + println!( + "{}", + CommandDispatcher::>::execute(parse).unwrap() + ); +} diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index 42894d0e..9d133c7e 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -1,7 +1,7 @@ use std::rc::Rc; #[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct Message(Rc); +pub struct Message(String); impl Message { pub fn string(&self) -> String { @@ -11,6 +11,6 @@ impl Message { impl From for Message { fn from(s: String) -> Self { - Self(Rc::new(s)) + Self(s) } } diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs new file mode 100644 index 00000000..84528696 --- /dev/null +++ b/azalea-brigadier/src/modifier.rs @@ -0,0 +1,9 @@ +use std::{any::Any, rc::Rc}; + +use crate::{ + context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, +}; + +pub trait RedirectModifier { + fn apply(&self, context: &CommandContext) -> Result>, CommandSyntaxException>; +} diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index 8b137891..fb862dec 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -1 +1,21 @@ +use crate::{ + context::CommandContextBuilder, exceptions::command_syntax_exception::CommandSyntaxException, + string_reader::StringReader, tree::CommandNode, +}; +use std::{any::Any, collections::HashMap, fmt::Debug, rc::Rc}; +pub struct ParseResults { + pub context: CommandContextBuilder, + pub reader: StringReader, + pub exceptions: HashMap>, CommandSyntaxException>, +} + +impl Debug for ParseResults { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ParseResults") + .field("context", &self.context) + // .field("reader", &self.reader) + .field("exceptions", &self.exceptions) + .finish() + } +} diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs new file mode 100644 index 00000000..a497e0d1 --- /dev/null +++ b/azalea-brigadier/src/parsers.rs @@ -0,0 +1,21 @@ +use std::{any::Any, marker::PhantomData, rc::Rc}; + +use crate::string_reader::StringReader; + +pub trait Parser { + fn parse(&self, reader: &mut StringReader) -> Option>; +} + +struct Integer {} +impl Parser for Integer { + fn parse(&self, reader: &mut StringReader) -> Option> { + let start = reader.cursor; + let result = reader.read_int(); + // TODO: check min and max + Some(Rc::new(result)) + } +} + +pub fn integer() -> impl Parser { + Integer {} +} diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs deleted file mode 100644 index 7a6d4db5..00000000 --- a/azalea-brigadier/src/redirect_modifier.rs +++ /dev/null @@ -1,11 +0,0 @@ -use dyn_clonable::*; - -use crate::{ - context::command_context::CommandContext, - exceptions::command_syntax_exception::CommandSyntaxException, -}; - -#[clonable] -pub trait RedirectModifier: Clone { - fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; -} diff --git a/azalea-brigadier/src/result_consumer.rs b/azalea-brigadier/src/result_consumer.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/src/result_consumer.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs deleted file mode 100644 index dd63244d..00000000 --- a/azalea-brigadier/src/single_redirect_modifier.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::{ - context::command_context::CommandContext, - exceptions::command_syntax_exception::CommandSyntaxException, -}; - -pub trait SingleRedirectModifier { - fn apply(&self, context: CommandContext) -> Result; -} diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/string_range.rs similarity index 87% rename from azalea-brigadier/src/context/string_range.rs rename to azalea-brigadier/src/string_range.rs index 87098a1a..8ca88624 100644 --- a/azalea-brigadier/src/context/string_range.rs +++ b/azalea-brigadier/src/string_range.rs @@ -1,6 +1,6 @@ use std::cmp; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub struct StringRange { start: usize, end: usize, @@ -31,7 +31,7 @@ impl StringRange { self.end } - pub fn get(&self, reader: &str) -> &str { + pub fn get<'a>(&self, reader: &'a str) -> &'a str { &reader[self.start..self.end] } diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 694edccb..4b390155 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,14 +1,11 @@ -use crate::{ - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, - immutable_string_reader::ImmutableStringReader, +use crate::exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }; -use std::str::FromStr; +use std::{rc::Rc, str::FromStr}; #[derive(Clone)] -pub struct StringReader<'a> { - string: &'a str, +pub struct StringReader { + string: String, pub cursor: usize, } @@ -16,63 +13,53 @@ const SYNTAX_ESCAPE: char = '\\'; const SYNTAX_DOUBLE_QUOTE: char = '"'; const SYNTAX_SINGLE_QUOTE: char = '\''; -// impl<'a> From<&'a str> for &StringReader<'a> {} - -// impl StringReader<'_> { -// fn from(string: &str) -> StringReader { -// StringReader { string, cursor: 0 } -// } -// } - -impl<'a> From<&'a str> for StringReader<'a> { - fn from(string: &'a str) -> Self { +impl From for StringReader { + fn from(string: String) -> Self { Self { string, cursor: 0 } } } -impl ImmutableStringReader for StringReader<'_> { - fn string(&self) -> &str { - self.string +impl StringReader { + pub fn string(&self) -> &str { + &self.string } - fn remaining_length(&self) -> usize { + pub fn remaining_length(&self) -> usize { self.string.len() - self.cursor } - fn total_length(&self) -> usize { + pub fn total_length(&self) -> usize { self.string.len() } - fn get_read(&self) -> &str { + pub fn get_read(&self) -> &str { &self.string[..self.cursor] } - fn remaining(&self) -> &str { + pub fn remaining(&self) -> &str { &self.string[self.cursor..] } - fn can_read_length(&self, length: usize) -> bool { + pub fn can_read_length(&self, length: usize) -> bool { self.cursor + length <= self.string.len() } - fn can_read(&self) -> bool { + pub fn can_read(&self) -> bool { self.can_read_length(1) } - fn peek(&self) -> char { + pub fn peek(&self) -> char { self.string.chars().nth(self.cursor).unwrap() } - fn peek_offset(&self, offset: usize) -> char { + pub fn peek_offset(&self, offset: usize) -> char { self.string.chars().nth(self.cursor + offset).unwrap() } - fn cursor(&self) -> usize { + pub fn cursor(&self) -> usize { self.cursor } -} -impl StringReader<'_> { pub fn read(&mut self) -> char { let c = self.peek(); self.cursor += 1; @@ -99,7 +86,7 @@ impl StringReader<'_> { pub fn read_int(&mut self) -> Result { let start = self.cursor; - while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; @@ -120,7 +107,7 @@ impl StringReader<'_> { pub fn read_long(&mut self) -> Result { let start = self.cursor; - while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; @@ -162,7 +149,7 @@ impl StringReader<'_> { pub fn read_float(&mut self) -> Result { let start = self.cursor; - while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; @@ -204,7 +191,7 @@ impl StringReader<'_> { return Ok(String::new()); } let next = self.peek(); - if !StringReader::<'_>::is_quoted_string_start(next) { + if !StringReader::is_quoted_string_start(next) { return Err(BuiltInExceptions::ReaderExpectedStartOfQuote.create_with_context(self)); } self.skip(); @@ -245,7 +232,7 @@ impl StringReader<'_> { return Ok(String::new()); } let next = self.peek(); - if StringReader::<'_>::is_quoted_string_start(next) { + if StringReader::is_quoted_string_start(next) { self.skip(); return self.read_string_until(next); } @@ -286,7 +273,7 @@ mod test { #[test] fn can_read() { - let mut reader = StringReader::from("abc"); + let mut reader = StringReader::from("abc".to_string()); assert_eq!(reader.can_read(), true); reader.skip(); // 'a' assert_eq!(reader.can_read(), true); @@ -298,7 +285,7 @@ mod test { #[test] fn get_remaining_length() { - let mut reader = StringReader::from("abc"); + let mut reader = StringReader::from("abc".to_string()); assert_eq!(reader.remaining_length(), 3); reader.cursor = 1; assert_eq!(reader.remaining_length(), 2); @@ -310,7 +297,7 @@ mod test { #[test] fn can_read_length() { - let reader = StringReader::from("abc"); + let reader = StringReader::from("abc".to_string()); assert_eq!(reader.can_read_length(1), true); assert_eq!(reader.can_read_length(2), true); assert_eq!(reader.can_read_length(3), true); @@ -320,7 +307,7 @@ mod test { #[test] fn peek() { - let mut reader = StringReader::from("abc"); + let mut reader = StringReader::from("abc".to_string()); assert_eq!(reader.peek(), 'a'); assert_eq!(reader.cursor(), 0); reader.cursor = 2; @@ -330,7 +317,7 @@ mod test { #[test] fn peek_length() { - let mut reader = StringReader::from("abc"); + let mut reader = StringReader::from("abc".to_string()); assert_eq!(reader.peek_offset(0), 'a'); assert_eq!(reader.peek_offset(2), 'c'); assert_eq!(reader.cursor(), 0); @@ -341,7 +328,7 @@ mod test { #[test] fn read() { - let mut reader = StringReader::from("abc"); + let mut reader = StringReader::from("abc".to_string()); assert_eq!(reader.read(), 'a'); assert_eq!(reader.read(), 'b'); assert_eq!(reader.read(), 'c'); @@ -350,14 +337,14 @@ mod test { #[test] fn skip() { - let mut reader = StringReader::from("abc"); + let mut reader = StringReader::from("abc".to_string()); reader.skip(); assert_eq!(reader.cursor(), 1); } #[test] fn get_remaining() { - let mut reader = StringReader::from("Hello!"); + let mut reader = StringReader::from("Hello!".to_string()); assert_eq!(reader.remaining(), "Hello!"); reader.cursor = 3; assert_eq!(reader.remaining(), "lo!"); @@ -367,7 +354,7 @@ mod test { #[test] fn get_read() { - let mut reader = StringReader::from("Hello!"); + let mut reader = StringReader::from("Hello!".to_string()); assert_eq!(reader.get_read(), ""); reader.cursor = 3; assert_eq!(reader.get_read(), "Hel"); @@ -377,28 +364,28 @@ mod test { #[test] fn skip_whitespace_none() { - let mut reader = StringReader::from("Hello!"); + let mut reader = StringReader::from("Hello!".to_string()); reader.skip_whitespace(); assert_eq!(reader.cursor(), 0); } #[test] fn skip_whitespace_mixed() { - let mut reader = StringReader::from(" \t \t\nHello!"); + let mut reader = StringReader::from(" \t \t\nHello!".to_string()); reader.skip_whitespace(); assert_eq!(reader.cursor(), 5); } #[test] fn skip_whitespace_empty() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); reader.skip_whitespace(); assert_eq!(reader.cursor(), 0); } #[test] fn read_unquoted_string() { - let mut reader = StringReader::from("hello world"); + let mut reader = StringReader::from("hello world".to_string()); assert_eq!(reader.read_unquoted_string(), "hello"); assert_eq!(reader.get_read(), "hello"); assert_eq!(reader.remaining(), " world"); @@ -406,7 +393,7 @@ mod test { #[test] fn read_unquoted_string_empty() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); assert_eq!(reader.read_unquoted_string(), ""); assert_eq!(reader.get_read(), ""); assert_eq!(reader.remaining(), ""); @@ -414,7 +401,7 @@ mod test { #[test] fn read_unquoted_string_empty_with_remaining() { - let mut reader = StringReader::from(" hello world"); + let mut reader = StringReader::from(" hello world".to_string()); assert_eq!(reader.read_unquoted_string(), ""); assert_eq!(reader.get_read(), ""); assert_eq!(reader.remaining(), " hello world"); @@ -422,7 +409,7 @@ mod test { #[test] fn read_quoted_string() { - let mut reader = StringReader::from("\"hello world\""); + let mut reader = StringReader::from("\"hello world\"".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), ""); @@ -430,7 +417,7 @@ mod test { #[test] fn read_single_quoted_string() { - let mut reader = StringReader::from("'hello world'"); + let mut reader = StringReader::from("'hello world'".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "'hello world'"); assert_eq!(reader.remaining(), ""); @@ -438,7 +425,7 @@ mod test { #[test] fn read_mixed_quoted_string_double_inside_single() { - let mut reader = StringReader::from("'hello \"world\"'"); + let mut reader = StringReader::from("'hello \"world\"'".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); assert_eq!(reader.get_read(), "'hello \"world\"'"); assert_eq!(reader.remaining(), ""); @@ -446,7 +433,7 @@ mod test { #[test] fn read_mixed_quoted_string_single_inside_double() { - let mut reader = StringReader::from("\"hello 'world'\""); + let mut reader = StringReader::from("\"hello 'world'\"".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "hello 'world'"); assert_eq!(reader.get_read(), "\"hello 'world'\""); assert_eq!(reader.remaining(), ""); @@ -454,7 +441,7 @@ mod test { #[test] fn read_quoted_string_empty_quoted() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), ""); assert_eq!(reader.get_read(), ""); assert_eq!(reader.remaining(), ""); @@ -462,7 +449,7 @@ mod test { #[test] fn read_quoted_string_empty_quoted_with_remaining() { - let mut reader = StringReader::from("\"\" hello world"); + let mut reader = StringReader::from("\"\" hello world".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), ""); assert_eq!(reader.get_read(), "\"\""); assert_eq!(reader.remaining(), " hello world"); @@ -470,7 +457,7 @@ mod test { #[test] fn read_quoted_string_with_escaped_quote() { - let mut reader = StringReader::from("\"hello \\\"world\\\"\""); + let mut reader = StringReader::from("\"hello \\\"world\\\"\"".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); assert_eq!(reader.get_read(), "\"hello \\\"world\\\"\""); assert_eq!(reader.remaining(), ""); @@ -478,7 +465,7 @@ mod test { #[test] fn read_quoted_string_with_escaped_escapes() { - let mut reader = StringReader::from("\"\\\\o/\""); + let mut reader = StringReader::from("\"\\\\o/\"".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "\\o/"); assert_eq!(reader.get_read(), "\"\\\\o/\""); assert_eq!(reader.remaining(), ""); @@ -486,7 +473,7 @@ mod test { #[test] fn read_quoted_string_with_remaining() { - let mut reader = StringReader::from("\"hello world\" foo bar"); + let mut reader = StringReader::from("\"hello world\" foo bar".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), " foo bar"); @@ -494,7 +481,7 @@ mod test { #[test] fn read_quoted_string_with_immediate_remaining() { - let mut reader = StringReader::from("\"hello world\"foo bar"); + let mut reader = StringReader::from("\"hello world\"foo bar".to_string()); assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), "foo bar"); @@ -502,7 +489,7 @@ mod test { #[test] fn read_quoted_string_no_open() { - let mut reader = StringReader::from("hello world\""); + let mut reader = StringReader::from("hello world\"".to_string()); let result = reader.read_quoted_string(); assert!(result.is_err()); if let Err(e) = result { @@ -513,7 +500,7 @@ mod test { #[test] fn read_quoted_string_no_close() { - let mut reader = StringReader::from("\"hello world"); + let mut reader = StringReader::from("\"hello world".to_string()); let result = reader.read_quoted_string(); assert!(result.is_err()); if let Err(e) = result { @@ -524,7 +511,7 @@ mod test { #[test] fn read_quoted_string_invalid_escape() { - let mut reader = StringReader::from("\"hello\\nworld\""); + let mut reader = StringReader::from("\"hello\\nworld\"".to_string()); let result = reader.read_quoted_string(); assert!(result.is_err()); if let Err(e) = result { @@ -538,7 +525,7 @@ mod test { #[test] fn read_quoted_string_invalid_quote_escape() { - let mut reader = StringReader::from("'hello\\\"\'world"); + let mut reader = StringReader::from("'hello\\\"\'world".to_string()); let result = reader.read_quoted_string(); assert!(result.is_err()); if let Err(e) = result { @@ -552,7 +539,7 @@ mod test { #[test] fn read_string_no_quotes() { - let mut reader = StringReader::from("hello world"); + let mut reader = StringReader::from("hello world".to_string()); assert_eq!(reader.read_string().unwrap(), "hello"); assert_eq!(reader.get_read(), "hello"); assert_eq!(reader.remaining(), " world"); @@ -560,7 +547,7 @@ mod test { #[test] fn read_string_single_quotes() { - let mut reader = StringReader::from("'hello world'"); + let mut reader = StringReader::from("'hello world'".to_string()); assert_eq!(reader.read_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "'hello world'"); assert_eq!(reader.remaining(), ""); @@ -568,7 +555,7 @@ mod test { #[test] fn read_string_double_quotes() { - let mut reader = StringReader::from("\"hello world\""); + let mut reader = StringReader::from("\"hello world\"".to_string()); assert_eq!(reader.read_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), ""); @@ -576,7 +563,7 @@ mod test { #[test] fn read_int() { - let mut reader = StringReader::from("1234567890"); + let mut reader = StringReader::from("1234567890".to_string()); assert_eq!(reader.read_int().unwrap(), 1234567890); assert_eq!(reader.get_read(), "1234567890"); assert_eq!(reader.remaining(), ""); @@ -584,7 +571,7 @@ mod test { #[test] fn read_int_negative() { - let mut reader = StringReader::from("-1234567890"); + let mut reader = StringReader::from("-1234567890".to_string()); assert_eq!(reader.read_int().unwrap(), -1234567890); assert_eq!(reader.get_read(), "-1234567890"); assert_eq!(reader.remaining(), ""); @@ -592,7 +579,7 @@ mod test { #[test] fn read_int_invalid() { - let mut reader = StringReader::from("12.34"); + let mut reader = StringReader::from("12.34".to_string()); let result = reader.read_int(); assert!(result.is_err()); if let Err(e) = result { @@ -608,7 +595,7 @@ mod test { #[test] fn read_int_none() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); let result = reader.read_int(); assert!(result.is_err()); if let Err(e) = result { @@ -619,7 +606,7 @@ mod test { #[test] fn read_int_with_remaining() { - let mut reader = StringReader::from("1234567890 foo bar"); + let mut reader = StringReader::from("1234567890 foo bar".to_string()); assert_eq!(reader.read_int().unwrap(), 1234567890); assert_eq!(reader.get_read(), "1234567890"); assert_eq!(reader.remaining(), " foo bar"); @@ -627,7 +614,7 @@ mod test { #[test] fn read_int_with_remaining_immediate() { - let mut reader = StringReader::from("1234567890foo bar"); + let mut reader = StringReader::from("1234567890foo bar".to_string()); assert_eq!(reader.read_int().unwrap(), 1234567890); assert_eq!(reader.get_read(), "1234567890"); assert_eq!(reader.remaining(), "foo bar"); @@ -635,7 +622,7 @@ mod test { #[test] fn read_long() { - let mut reader = StringReader::from("1234567890"); + let mut reader = StringReader::from("1234567890".to_string()); assert_eq!(reader.read_long().unwrap(), 1234567890); assert_eq!(reader.get_read(), "1234567890"); assert_eq!(reader.remaining(), ""); @@ -643,7 +630,7 @@ mod test { #[test] fn read_long_negative() { - let mut reader = StringReader::from("-1234567890"); + let mut reader = StringReader::from("-1234567890".to_string()); assert_eq!(reader.read_long().unwrap(), -1234567890); assert_eq!(reader.get_read(), "-1234567890"); assert_eq!(reader.remaining(), ""); @@ -651,7 +638,7 @@ mod test { #[test] fn read_long_invalid() { - let mut reader = StringReader::from("12.34"); + let mut reader = StringReader::from("12.34".to_string()); let result = reader.read_long(); assert!(result.is_err()); if let Err(e) = result { @@ -667,7 +654,7 @@ mod test { #[test] fn read_long_none() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); let result = reader.read_long(); assert!(result.is_err()); if let Err(e) = result { @@ -678,7 +665,7 @@ mod test { #[test] fn read_long_with_remaining() { - let mut reader = StringReader::from("1234567890 foo bar"); + let mut reader = StringReader::from("1234567890 foo bar".to_string()); assert_eq!(reader.read_long().unwrap(), 1234567890); assert_eq!(reader.get_read(), "1234567890"); assert_eq!(reader.remaining(), " foo bar"); @@ -686,7 +673,7 @@ mod test { #[test] fn read_long_with_remaining_immediate() { - let mut reader = StringReader::from("1234567890foo bar"); + let mut reader = StringReader::from("1234567890foo bar".to_string()); assert_eq!(reader.read_long().unwrap(), 1234567890); assert_eq!(reader.get_read(), "1234567890"); assert_eq!(reader.remaining(), "foo bar"); @@ -694,7 +681,7 @@ mod test { #[test] fn read_double() { - let mut reader = StringReader::from("123"); + let mut reader = StringReader::from("123".to_string()); assert_eq!(reader.read_double().unwrap(), 123.0); assert_eq!(reader.get_read(), "123"); assert_eq!(reader.remaining(), ""); @@ -702,7 +689,7 @@ mod test { #[test] fn read_double_with_decimal() { - let mut reader = StringReader::from("12.34"); + let mut reader = StringReader::from("12.34".to_string()); assert_eq!(reader.read_double().unwrap(), 12.34); assert_eq!(reader.get_read(), "12.34"); assert_eq!(reader.remaining(), ""); @@ -710,7 +697,7 @@ mod test { #[test] fn read_double_negative() { - let mut reader = StringReader::from("-123"); + let mut reader = StringReader::from("-123".to_string()); assert_eq!(reader.read_double().unwrap(), -123.0); assert_eq!(reader.get_read(), "-123"); assert_eq!(reader.remaining(), ""); @@ -718,7 +705,7 @@ mod test { #[test] fn read_double_invalid() { - let mut reader = StringReader::from("12.34.56"); + let mut reader = StringReader::from("12.34.56".to_string()); let result = reader.read_double(); assert!(result.is_err()); if let Err(e) = result { @@ -734,7 +721,7 @@ mod test { #[test] fn read_double_none() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); let result = reader.read_double(); assert!(result.is_err()); if let Err(e) = result { @@ -745,7 +732,7 @@ mod test { #[test] fn read_double_with_remaining() { - let mut reader = StringReader::from("12.34 foo bar"); + let mut reader = StringReader::from("12.34 foo bar".to_string()); assert_eq!(reader.read_double().unwrap(), 12.34); assert_eq!(reader.get_read(), "12.34"); assert_eq!(reader.remaining(), " foo bar"); @@ -753,7 +740,7 @@ mod test { #[test] fn read_double_with_remaining_immediate() { - let mut reader = StringReader::from("12.34foo bar"); + let mut reader = StringReader::from("12.34foo bar".to_string()); assert_eq!(reader.read_double().unwrap(), 12.34); assert_eq!(reader.get_read(), "12.34"); assert_eq!(reader.remaining(), "foo bar"); @@ -761,7 +748,7 @@ mod test { #[test] fn read_float() { - let mut reader = StringReader::from("123"); + let mut reader = StringReader::from("123".to_string()); assert_eq!(reader.read_float().unwrap(), 123.0f32); assert_eq!(reader.get_read(), "123"); assert_eq!(reader.remaining(), ""); @@ -769,7 +756,7 @@ mod test { #[test] fn read_float_with_decimal() { - let mut reader = StringReader::from("12.34"); + let mut reader = StringReader::from("12.34".to_string()); assert_eq!(reader.read_float().unwrap(), 12.34f32); assert_eq!(reader.get_read(), "12.34"); assert_eq!(reader.remaining(), ""); @@ -777,7 +764,7 @@ mod test { #[test] fn read_float_negative() { - let mut reader = StringReader::from("-123"); + let mut reader = StringReader::from("-123".to_string()); assert_eq!(reader.read_float().unwrap(), -123.0f32); assert_eq!(reader.get_read(), "-123"); assert_eq!(reader.remaining(), ""); @@ -785,7 +772,7 @@ mod test { #[test] fn read_float_invalid() { - let mut reader = StringReader::from("12.34.56"); + let mut reader = StringReader::from("12.34.56".to_string()); let result = reader.read_float(); assert!(result.is_err()); if let Err(e) = result { @@ -801,7 +788,7 @@ mod test { #[test] fn read_float_none() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); let result = reader.read_float(); assert!(result.is_err()); if let Err(e) = result { @@ -812,7 +799,7 @@ mod test { #[test] fn read_float_with_remaining() { - let mut reader = StringReader::from("12.34 foo bar"); + let mut reader = StringReader::from("12.34 foo bar".to_string()); assert_eq!(reader.read_float().unwrap(), 12.34f32); assert_eq!(reader.get_read(), "12.34"); assert_eq!(reader.remaining(), " foo bar"); @@ -820,7 +807,7 @@ mod test { #[test] fn read_float_with_remaining_immediate() { - let mut reader = StringReader::from("12.34foo bar"); + let mut reader = StringReader::from("12.34foo bar".to_string()); assert_eq!(reader.read_float().unwrap(), 12.34f32); assert_eq!(reader.get_read(), "12.34"); assert_eq!(reader.remaining(), "foo bar"); @@ -828,14 +815,14 @@ mod test { #[test] fn expect_correct() { - let mut reader = StringReader::from("abc"); + let mut reader = StringReader::from("abc".to_string()); reader.expect('a'); assert_eq!(reader.cursor(), 1); } #[test] fn expect_incorrect() { - let mut reader = StringReader::from("bcd"); + let mut reader = StringReader::from("bcd".to_string()); let result = reader.expect('a'); assert!(result.is_err()); if let Err(e) = result { @@ -849,7 +836,7 @@ mod test { #[test] fn expect_none() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); let result = reader.expect('a'); assert!(result.is_err()); if let Err(e) = result { @@ -863,14 +850,14 @@ mod test { #[test] fn read_boolean_correct() { - let mut reader = StringReader::from("true"); + let mut reader = StringReader::from("true".to_string()); assert_eq!(reader.read_boolean().unwrap(), true); assert_eq!(reader.get_read(), "true"); } #[test] fn read_boolean_incorrect() { - let mut reader = StringReader::from("tuesday"); + let mut reader = StringReader::from("tuesday".to_string()); let result = reader.read_boolean(); assert!(result.is_err()); if let Err(e) = result { @@ -886,7 +873,7 @@ mod test { #[test] fn read_boolean_none() { - let mut reader = StringReader::from(""); + let mut reader = StringReader::from("".to_string()); let result = reader.read_boolean(); assert!(result.is_err()); if let Err(e) = result { diff --git a/azalea-brigadier/src/suggestion/integer_suggestion.rs b/azalea-brigadier/src/suggestion/integer_suggestion.rs deleted file mode 100644 index acee2329..00000000 --- a/azalea-brigadier/src/suggestion/integer_suggestion.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct IntegerSuggestion {} diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs deleted file mode 100644 index 050bae6c..00000000 --- a/azalea-brigadier/src/suggestion/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod integer_suggestion; -pub mod suggestion; -pub mod suggestion_provider; -pub mod suggestions; -pub mod suggestions_builder; diff --git a/azalea-brigadier/src/suggestion/suggestion.rs b/azalea-brigadier/src/suggestion/suggestion.rs deleted file mode 100644 index 4cbed7be..00000000 --- a/azalea-brigadier/src/suggestion/suggestion.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::cmp; - -use crate::{context::string_range::StringRange, message::Message}; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Suggestion { - range: StringRange, - text: String, - tooltip: Option, -} - -impl Suggestion { - pub fn new(range: StringRange, text: String) -> Suggestion { - Suggestion { - range, - text, - tooltip: None, - } - } - - pub fn new_with_tooltip(range: StringRange, text: String, tooltip: Message) -> Suggestion { - Suggestion { - range, - text, - tooltip: Some(tooltip), - } - } - - pub fn range(&self) -> &StringRange { - &self.range - } - - pub fn text(&self) -> &String { - &self.text - } - - pub fn tooltip(&self) -> Option<&Message> { - self.tooltip.as_ref() - } - - pub fn apply(&self, input: &str) -> String { - if self.range.start() == 0 && self.range.end() == input.len() { - return self.text.clone(); - } - let mut result = String::new(); - if self.range.start() > 0 { - result.push_str(&input[0..self.range.start()]); - } - result.push_str(&self.text); - if self.range.end() < input.len() { - result.push_str(&input[self.range.end()..]); - } - result - } - - pub fn expand(&self, command: &str, range: StringRange) -> Suggestion { - if range == self.range { - return self.clone(); - } - let mut result = String::new(); - if range.start() < self.range.start() { - result.push_str(&command[range.start()..self.range.start()]); - } - result.push_str(&self.text); - if range.end() > self.range.end() { - result.push_str(&command[self.range.end()..range.end()]); - } - Suggestion { - range, - text: result, - tooltip: self.tooltip.clone(), - } - } - - pub fn compare_ignore_case(&self, b: &Suggestion) -> cmp::Ordering { - self.text.to_lowercase().cmp(&b.text.to_lowercase()) - } -} - -impl PartialOrd for Suggestion { - fn partial_cmp(&self, other: &Suggestion) -> Option { - Some(self.text.cmp(&other.text)) - } -} - -impl Ord for Suggestion { - fn cmp(&self, other: &Suggestion) -> cmp::Ordering { - self.text.cmp(&other.text) - } -} diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs deleted file mode 100644 index 3027d460..00000000 --- a/azalea-brigadier/src/suggestion/suggestion_provider.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::{ - context::command_context::CommandContext, - exceptions::command_syntax_exception::CommandSyntaxException, -}; - -use super::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}; - -pub trait SuggestionProvider { - fn suggestions( - &self, - context: &CommandContext, - builder: &SuggestionsBuilder, - ) -> Result; -} diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs deleted file mode 100644 index 9f0ee06d..00000000 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::{cmp, collections::HashSet}; - -use crate::{context::string_range::StringRange, message::Message}; - -use super::suggestion::Suggestion; - -#[derive(PartialEq, Eq, Hash)] -pub struct Suggestions { - range: StringRange, - suggestions: Vec, -} - -impl Suggestions { - fn range(&self) -> &StringRange { - &self.range - } - - fn list(&self) -> &Vec { - &self.suggestions - } - - fn is_empty(&self) -> bool { - self.suggestions.is_empty() - } - - fn merge(command: &str, input: &Vec) { - if input.is_empty() { - return Self::default(); - } else if input.len() == 1 { - return input.iter().next(); - } - let texts = HashSet::new(); - for suggestions in input { - texts.extend(suggestions.list()) - } - Self::new(command, texts) - } - - // public static Suggestions create(final String command, final Collection suggestions) { - // if (suggestions.isEmpty()) { - // return EMPTY; - // } - // int start = Integer.MAX_VALUE; - // int end = Integer.MIN_VALUE; - // for (final Suggestion suggestion : suggestions) { - // start = Math.min(suggestion.getRange().getStart(), start); - // end = Math.max(suggestion.getRange().getEnd(), end); - // } - // final StringRange range = new StringRange(start, end); - // final Set texts = new HashSet<>(); - // for (final Suggestion suggestion : suggestions) { - // texts.add(suggestion.expand(command, range)); - // } - // final List sorted = new ArrayList<>(texts); - // sorted.sort((a, b) -> a.compareToIgnoreCase(b)); - // return new Suggestions(range, sorted); - pub fn new(command: String, suggestions: Vec) -> Self { - if suggestions.is_empty() { - return Self::default(); - } - let mut start = usize::MAX; - let mut end = usize::MIN; - for suggestion in suggestions { - let start = cmp::min(suggestion.range().start(), start); - let end = cmp::max(suggestion.range().end(), end); - } - let range = StringRange::new(start, end); - let texts = HashSet::new(); - for suggestion in suggestions { - texts.insert(suggestion.expand(command, range)); - } - let sorted = texts.sort_by(|a, b| a.compare_ignore_case(b)); - Suggestions { - range, - suggestions: sorted, - } - } -} - -impl Default for Suggestions { - fn default() -> Self { - Self { - range: StringRange::at(0), - suggestions: vec![], - } - } -} - -// #[cfg(test)] -// mod tests { -// use crate::suggestion::suggestion::Suggestion; - -// use super::*; - -// #[test] -// fn merge_empty() { -// let merged = Suggestions::merge("foo b", vec![]); -// assert_eq!(merged.is_empty(), true); -// } - -// #[test] -// fn merge_single() { -// let suggestions = Suggestions::new(StringRange::at(5), "ar".to_string()); -// let merged = Suggestions::merge("foo b", vec![suggestions]); -// assert_eq!(merged, suggestions); -// } - -// #[test] -// fn merge_multiple() { -// let a = Suggestions::new( -// StringRange::at(5), -// vec![ -// Suggestion::new(StringRange::at(5), "ar".to_string()), -// Suggestion::new(StringRange::at(5), "az".to_string()), -// Suggestion::new(StringRange::at(5), "Az".to_string()), -// ], -// ); -// let b = Suggestions::new( -// StringRange::between(4, 5), -// vec![ -// Suggestion::new(StringRange::between(4, 5), "foo".to_string()), -// Suggestion::new(StringRange::between(4, 5), "qux".to_string()), -// Suggestion::new(StringRange::between(4, 5), "apple".to_string()), -// Suggestion::new(StringRange::between(4, 5), "Bar".to_string()), -// ], -// ); -// let merged = Suggestions::merge("foo b", vec![a, b]); -// assert_eq!( -// merged.get_list(), -// vec![ -// Suggestion::new(StringRange::between(4, 5), "apple".to_string()), -// Suggestion::new(StringRange::between(4, 5), "bar".to_string()), -// Suggestion::new(StringRange::between(4, 5), "Bar".to_string()), -// Suggestion::new(StringRange::between(4, 5), "baz".to_string()), -// Suggestion::new(StringRange::between(4, 5), "bAz".to_string()), -// Suggestion::new(StringRange::between(4, 5), "foo".to_string()), -// Suggestion::new(StringRange::between(4, 5), "qux".to_string()), -// ] -// ); -// } -// } diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs deleted file mode 100644 index bc8f6f5d..00000000 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::context::string_range::StringRange; - -use super::{ - integer_suggestion::IntegerSuggestion, suggestion::Suggestion, suggestions::Suggestions, -}; - -pub struct SuggestionsBuilder { - input: String, - input_lowercase: String, - start: usize, - remaining: String, - remaining_lowercase: String, - result: Vec, -} - -impl SuggestionsBuilder { - pub fn new_with_lowercase( - input: String, - input_lowercase: String, - start: usize, - ) -> SuggestionsBuilder { - SuggestionsBuilder { - input, - input_lowercase, - start, - remaining: input.get(start..).unwrap().to_string(), - remaining_lowercase: input_lowercase.get(start..).unwrap().to_string(), - result: Vec::new(), - } - } - - pub fn new(input: String, start: usize) -> SuggestionsBuilder { - SuggestionsBuilder::new_with_lowercase(input, input.to_lowercase(), start) - } - - pub fn input(&self) -> &str { - &self.input - } - - pub fn start(&self) -> usize { - self.start - } - - pub fn remaining(&self) -> &str { - &self.remaining - } - - pub fn remaining_lowercase(&self) -> &str { - &self.remaining_lowercase - } - - pub fn build(&self) -> Suggestions { - Suggestions::create(self.input(), self.result) - } - - pub fn suggest(&mut self, text: &str) -> &mut SuggestionsBuilder { - if text == self.remaining { - return self; - } - self.result.push(Suggestion::new( - StringRange::between(self.start, self.input.len()), - text, - )); - self - } - - pub fn suggest_with_tooltip(&mut self, text: &str, tooltip: &str) -> &mut SuggestionsBuilder { - if text == self.remaining { - return self; - } - self.result.push(Suggestion::new_with_tooltip( - StringRange::between(self.start, self.input.len()), - text, - tooltip, - )); - self - } - - pub fn suggest_with_value(&mut self, value: i32) -> &mut SuggestionsBuilder { - self.result.push(IntegerSuggestion::new( - StringRange::between(self.start, self.input.len()), - value, - )); - self - } - - pub fn suggest_with_value_and_tooltip( - &mut self, - value: i32, - tooltip: &str, - ) -> &mut SuggestionsBuilder { - self.result.push(IntegerSuggestion::new_with_tooltip( - StringRange::between(self.start, self.input.len()), - value, - tooltip, - )); - self - } - - pub fn add(&mut self, other: &SuggestionsBuilder) -> &mut SuggestionsBuilder { - self.result.extend(other.result.iter().cloned()); - self - } - - pub fn create_offset(&self, start: usize) -> SuggestionsBuilder { - SuggestionsBuilder::new_with_lowercase( - self.input.clone(), - self.input_lowercase.clone(), - start, - ) - } - - pub fn restart(&self) -> SuggestionsBuilder { - self.create_offset(self.start) - } -} diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs new file mode 100644 index 00000000..2f023697 --- /dev/null +++ b/azalea-brigadier/src/tree.rs @@ -0,0 +1,269 @@ +use crate::{ + builder::{ + argument_builder::ArgumentBuilderType, literal_argument_builder::Literal, + required_argument_builder::Argument, + }, + context::{CommandContext, CommandContextBuilder, ParsedArgument}, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + modifier::RedirectModifier, + string_range::StringRange, + string_reader::StringReader, +}; +use std::{ + any::Any, + cell::RefCell, + collections::{BTreeMap, HashMap}, + fmt::Debug, + hash::Hash, + ptr, + rc::Rc, +}; + +/// An ArgumentBuilder that has been built. +#[derive(Clone)] +#[non_exhaustive] +pub struct CommandNode { + pub value: ArgumentBuilderType, + + // we use BTreeMap instead of HashMap because it can be hashed + pub children: BTreeMap>>>, + pub literals: BTreeMap>>>, + pub arguments: BTreeMap>>>, + + pub command: Option) -> i32>>, + pub requirement: Rc) -> bool>, + pub redirect: Option>>>, + pub forks: bool, + pub modifier: Option>>, +} + +impl CommandNode { + // pub fn new() + // TODO: precalculate `literals` and `arguments` and include them in CommandNode + fn literals(&self) -> &BTreeMap>>> { + &self.literals + } + fn arguments(&self) -> &BTreeMap>>> { + &self.arguments + } + + /// Gets the literal, or panics. You should use match if you're not certain about the type. + pub fn literal(&self) -> &Literal { + match self.value { + ArgumentBuilderType::Literal(ref literal) => literal, + _ => panic!("CommandNode::literal() called on non-literal node"), + } + } + /// Gets the argument, or panics. You should use match if you're not certain about the type. + pub fn argument(&self) -> &Argument { + match self.value { + ArgumentBuilderType::Argument(ref argument) => argument, + _ => panic!("CommandNode::argument() called on non-argument node"), + } + } + + pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { + let literals = self.literals(); + + println!("get relevant nodes {:?} literals={:?}", self, literals); + + if literals.len() > 0 { + let cursor = input.cursor(); + while input.can_read() && input.peek() != ' ' { + input.skip(); + } + let text: String = input + .string() + .chars() + .skip(cursor) + .take(input.cursor() - cursor) + .collect(); + input.cursor = cursor; + let literal = literals.get(&text); + if let Some(literal) = literal { + return vec![literal.clone()]; + } else { + return self + .arguments() + .values() + .map(|argument| argument.clone()) + .collect(); + } + } else { + return self + .arguments() + .values() + .map(|argument| argument.clone()) + .collect(); + } + } + + pub fn can_use(&self, source: Rc) -> bool { + (self.requirement)(source) + } + + pub fn add_child(&mut self, node: &Rc>>) { + let child = self.children.get(node.borrow().name()); + if let Some(child) = child { + // We've found something to merge onto + if let Some(command) = &node.borrow().command { + child.borrow_mut().command = Some(command.clone()); + } + for grandchild in node.borrow().children.values() { + child.borrow_mut().add_child(grandchild); + } + } else { + self.children + .insert(node.borrow().name().to_string(), node.clone()); + match &node.borrow().value { + ArgumentBuilderType::Literal(literal) => { + self.literals.insert(literal.value.clone(), node.clone()); + } + ArgumentBuilderType::Argument(argument) => { + self.arguments.insert(argument.name.clone(), node.clone()); + } + } + } + } + + pub fn name(&self) -> &str { + match &self.value { + ArgumentBuilderType::Argument(argument) => &argument.name, + ArgumentBuilderType::Literal(literal) => &literal.value, + } + } + + pub fn parse_with_context( + &self, + reader: &mut StringReader, + context_builder: &mut CommandContextBuilder, + ) -> Result<(), CommandSyntaxException> { + match self.value { + ArgumentBuilderType::Argument(ref argument) => { + let start = reader.cursor(); + // TODO: handle this better + let result = argument + .parse(reader) + .expect("Couldn't get result for some reason"); + let parsed = ParsedArgument { + range: StringRange::between(start, reader.cursor()), + result: result, + }; + + context_builder.with_argument(&argument.name, parsed.clone()); + context_builder.with_node(Rc::new(self.clone()), parsed.range); + + Ok(()) + } + ArgumentBuilderType::Literal(ref literal) => { + let start = reader.cursor(); + let end = self.parse(reader); + + if let Some(end) = end { + context_builder + .with_node(Rc::new(self.clone()), StringRange::between(start, end)); + return Ok(()); + } + + Err(BuiltInExceptions::LiteralIncorrect { + expected: literal.value.clone(), + } + .create_with_context(reader)) + } + } + } + + fn parse(&self, reader: &mut StringReader) -> Option { + match self.value { + ArgumentBuilderType::Argument(ref argument) => { + panic!("Can't parse argument.") + } + ArgumentBuilderType::Literal(ref literal) => { + let start = reader.cursor(); + if reader.can_read_length(literal.value.len()) { + let end = start + literal.value.len(); + if reader + .string() + .get(start..end) + .expect("Couldn't slice reader correctly?") + == literal.value + { + reader.cursor = end; + if !reader.can_read() || reader.peek() == ' ' { + return Some(end); + } else { + reader.cursor = start; + } + } + } + } + } + None + } +} + +impl Debug for CommandNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandNode") + .field("value", &self.value) + .field("children", &self.children) + .field("command", &self.command.is_some()) + // .field("requirement", &self.requirement) + .field("redirect", &self.redirect) + .field("forks", &self.forks) + // .field("modifier", &self.modifier) + .finish() + } +} + +impl Default for CommandNode { + fn default() -> Self { + println!("making default node"); + Self { + value: ArgumentBuilderType::Literal(Literal::default()), + + children: BTreeMap::new(), + literals: BTreeMap::new(), + arguments: BTreeMap::new(), + + command: None, + requirement: Rc::new(|_| true), + redirect: None, + forks: false, + modifier: None, + } + } +} + +impl Hash for CommandNode { + fn hash(&self, state: &mut H) { + // hash the children + for (k, v) in &self.children { + k.hash(state); + v.borrow().hash(state); + } + // i hope this works because if doesn't then that'll be a problem + ptr::hash(&self.command, state); + } +} + +impl PartialEq for CommandNode { + fn eq(&self, other: &Self) -> bool { + if self.children != other.children { + return false; + } + if let Some(selfexecutes) = &self.command { + if let Some(otherexecutes) = &other.command { + if !Rc::ptr_eq(selfexecutes, otherexecutes) { + return false; + } + } else { + return false; + } + } + true + } +} +impl Eq for CommandNode {} diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs deleted file mode 100644 index 9d2af14e..00000000 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::{ - any::Any, - collections::HashMap, - fmt::{Debug, Display, Formatter}, -}; - -use crate::{ - arguments::argument_type::ArgumentType, - builder::required_argument_builder::RequiredArgumentBuilder, - command::Command, - context::{ - command_context::CommandContext, command_context_builder::CommandContextBuilder, - parsed_argument::ParsedArgument, - }, - exceptions::command_syntax_exception::CommandSyntaxException, - immutable_string_reader::ImmutableStringReader, - redirect_modifier::RedirectModifier, - string_reader::StringReader, - suggestion::{ - suggestion_provider::SuggestionProvider, suggestions::Suggestions, - suggestions_builder::SuggestionsBuilder, - }, -}; - -use super::{ - command_node::{BaseCommandNode, CommandNodeTrait}, - literal_command_node::LiteralCommandNode, - root_command_node::RootCommandNode, -}; - -const USAGE_ARGUMENT_OPEN: &str = "<"; -const USAGE_ARGUMENT_CLOSE: &str = ">"; - -pub struct ArgumentCommandNode { - name: String, - type_: Box>, - custom_suggestions: Option>>, - - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -impl ArgumentCommandNode { - fn get_type(&self) -> &dyn ArgumentType { - &*self.type_ - } - - fn custom_suggestions(&self) -> &Option>> { - &self.custom_suggestions - } -} - -impl CommandNodeTrait for ArgumentCommandNode { - fn name(&self) -> &str { - &self.name - } - - fn parse( - &self, - reader: &mut StringReader, - context_builder: CommandContextBuilder, - ) -> Result<(), CommandSyntaxException> { - // final int start = reader.getCursor(); - // final T result = type.parse(reader); - // final ParsedArgument parsed = new ParsedArgument<>(start, reader.getCursor(), result); - - // contextBuilder.withArgument(name, parsed); - // contextBuilder.withNode(this, parsed.getRange()); - - let start = reader.cursor(); - let result = self.get_type().parse(reader)?; - let parsed = ParsedArgument::new(start, reader.get_cursor(), result); - - context_builder.with_argument(&self.name, parsed); - context_builder.with_node(self, parsed.get_range()); - - Ok(()) - } - - fn list_suggestions( - &self, - context: CommandContext, - builder: &SuggestionsBuilder, - ) -> Result { - if self.custom_suggestions.is_none() { - self.get_type().list_suggestions(context, builder) - } else { - self.custom_suggestions.get_suggestions(context, builder) - } - } - - fn is_valid_input(&self, input: &str) -> bool { - let reader = StringReader::new(input); - let result = self.get_type().parse(reader); - if result.is_ok() { - return !reader.can_read() || reader.peek() == ' '; - } else { - return false; - } - } - - fn usage_text(&self) -> &str { - USAGE_ARGUMENT_OPEN + self.name + USAGE_ARGUMENT_CLOSE - } - - fn create_builder(&self) -> RequiredArgumentBuilder { - let builder = RequiredArgumentBuilder::argument(&self.name, &self.type_); - builder.requires(self.base.get_requirement()); - builder.forward( - self.base.get_redirect(), - self.base.get_redirect_modifier(), - self.base.is_fork(), - ); - builder.suggests(self.custom_suggestions()); - if self.base.get_command() != None { - builder.executes(self.base.get_command().unwrap()); - } - builder - } - - fn get_examples(&self) -> Vec { - self.type_.get_examples() - } - - fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - self.modifier.as_ref().map(|modifier| modifier.as_ref()) - } - - fn can_use(&self, source: S) -> bool { - (self.requirement)(&source) - } - - fn add_child(&self, node: &Box>) -> Result<(), String> { - let dynamic_node = node as &dyn Any; - if dynamic_node.is::>() { - return Err(String::from( - "Cannot add a RootCommandNode as a child to any other CommandNode", - )); - } - - let mut child = self.children.get(node.name()); - if let Some(child) = child { - // We've found something to merge onto - if let Some(command) = node.base().command() { - child.base_mut().command = Some(*command); - } - for grandchild in node.base().children().values() { - child.base_mut().add_child(&*grandchild)?; - } - Ok(()) - } else { - self.children.insert(node.name().to_string(), *node); - - if let Some(dynamic_node) = dynamic_node.downcast_ref::>() { - self.literals.insert(node.name().to_string(), *dynamic_node); - } else if let Some(dynamic_node) = dynamic_node.downcast_ref::>() - { - self.arguments - .insert(node.name().to_string(), *dynamic_node); - } - Ok(()) - } - } -} - -impl Display for ArgumentCommandNode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "", self.name, self.type_) - } -} diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs deleted file mode 100644 index 207e114e..00000000 --- a/azalea-brigadier/src/tree/command_node.rs +++ /dev/null @@ -1,143 +0,0 @@ -use super::{ - argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode, - root_command_node::RootCommandNode, -}; -use crate::{ - arguments::argument_type::ArgumentType, - builder::argument_builder::ArgumentBuilder, - command::Command, - context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, - exceptions::command_syntax_exception::CommandSyntaxException, - redirect_modifier::RedirectModifier, - string_reader::StringReader, - suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, -}; -use std::ops::Deref; -use std::{any::Any, collections::HashMap, fmt::Debug}; - -#[enum_dispatch(CommandNodeTrait)] -enum CommandNodeEnum { - Literal(LiteralCommandNode), - Argument(ArgumentCommandNode), - Root(RootCommandNode), -} - -impl CommandNodeEnum { - fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - (*self).modifier.as_ref().map(|modifier| modifier.as_ref()) - } - - fn can_use(&self, source: S) -> bool { - (self.requirement)(&source) - } - - fn add_child(&self, node: &Box>) -> Result<(), String> { - let dynamic_node = node as &dyn Any; - if dynamic_node.is::>() { - return Err(String::from( - "Cannot add a RootCommandNode as a child to any other CommandNode", - )); - } - - let mut child = self.children.get(node.name()); - if let Some(child) = child { - // We've found something to merge onto - if let Some(command) = node.base().command() { - child.base_mut().command = Some(*command); - } - for grandchild in node.base().children().values() { - child.base_mut().add_child(&*grandchild)?; - } - Ok(()) - } else { - self.children.insert(node.name().to_string(), *node); - - if let Some(dynamic_node) = dynamic_node.downcast_ref::>() { - self.literals.insert(node.name().to_string(), *dynamic_node); - } else if let Some(dynamic_node) = dynamic_node.downcast_ref::>() - { - self.arguments - .insert(node.name().to_string(), *dynamic_node); - } - Ok(()) - } - } -} -pub struct BaseCommandNode { - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -// impl Clone for BaseCommandNode<'_, S> { -// fn clone(&self) -> Self { -// Self { -// children: self.children.clone(), -// literals: self.literals.clone(), -// arguments: self.arguments.clone(), -// requirement: self.requirement.clone(), -// redirect: self.redirect.clone(), -// modifier: self.modifier.clone(), -// forks: self.forks.clone(), -// command: self.command.clone(), -// } -// } -// } - -impl Debug for BaseCommandNode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BaseCommandNode") - .field("children", &self.children) - .field("literals", &self.literals) - .field("arguments", &self.arguments) - .field("requirement", &self.requirement) - .field("redirect", &self.redirect) - .field("modifier", &self.modifier) - .field("forks", &self.forks) - .field("command", &self.command) - .finish() - } -} - -impl Default for BaseCommandNode { - fn default() -> Self { - Self { - children: HashMap::new(), - literals: HashMap::new(), - arguments: HashMap::new(), - requirement: Box::new(|_| true), - redirect: None, - modifier: None, - forks: false, - command: None, - } - } -} - -#[enum_dispatch] -pub trait CommandNodeTrait { - fn name(&self) -> &str; - fn usage_text(&self) -> &str; - fn parse( - &self, - reader: &mut StringReader, - context_builder: CommandContextBuilder, - ) -> Result<(), CommandSyntaxException>; - fn list_suggestions( - &self, - context: CommandContext, - builder: &SuggestionsBuilder, - ) -> Result; - fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> Box>; - fn get_examples(&self) -> Vec; - - fn redirect_modifier(&self) -> Option<&dyn RedirectModifier>; - fn can_use(&self, source: S) -> bool; - fn add_child(&self, node: &Box>) -> Result<(), String>; -} diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs deleted file mode 100644 index 2db31d97..00000000 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::{ - arguments::argument_type::ArgumentType, - builder::{ - argument_builder::ArgumentBuilder, literal_argument_builder::LiteralArgumentBuilder, - }, - command::Command, - context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, - immutable_string_reader::ImmutableStringReader, - redirect_modifier::RedirectModifier, - string_reader::StringReader, - suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, -}; -use std::{collections::HashMap, fmt::Debug}; - -use super::{ - argument_command_node::ArgumentCommandNode, - command_node::{BaseCommandNode, CommandNodeTrait}, -}; - -#[derive(Debug, Clone)] -pub struct LiteralCommandNode { - literal: String, - literal_lowercase: String, - - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -impl LiteralCommandNode { - pub fn new(literal: String) -> Self { - let literal_lowercase = literal.to_lowercase(); - Self { - literal, - literal_lowercase, - ..Default::default() - } - } - - pub fn literal(&self) -> &String { - &self.literal - } - - pub fn parse(&self, reader: &mut StringReader) -> i32 { - let start = reader.cursor(); - if reader.can_read_length(self.literal.len()) { - let end = start + self.literal.len(); - if reader.string()[start..end].eq(&self.literal) { - reader.cursor = end; - if !reader.can_read() || reader.peek() == ' ' { - return end as i32; - } else { - reader.cursor = start; - } - } - } - -1 - } -} - -impl CommandNodeTrait for LiteralCommandNode { - fn name(&self) -> &str { - &self.literal - } - - fn parse( - &self, - reader: &mut StringReader<'_>, - context_builder: CommandContextBuilder, - ) -> Result<(), CommandSyntaxException> { - let start = reader.cursor(); - let end = self.parse(reader); - if end > -1 { - return Ok(()); - } - - Err(BuiltInExceptions::LiteralIncorrect { - expected: self.literal().to_string(), - } - .create_with_context(reader)) - } - - fn list_suggestions( - &self, - context: CommandContext, - builder: &SuggestionsBuilder, - ) -> Result { - if self - .literal_lowercase - .starts_with(&builder.remaining_lowercase()) - { - Ok(builder.suggest(self.literal()).build()) - } else { - Ok(Suggestions::default()) - } - } - - fn is_valid_input(&self, input: &str) -> bool { - self.parse(&mut StringReader::from(input)) > -1 - } - - fn usage_text(&self) -> &str { - &self.literal - } - - fn create_builder(&self) -> Box> { - let mut builder = LiteralArgumentBuilder::literal(self.literal().to_string()); - builder.base.requires(&self.base().requirement); - builder.base.forward( - self.base.redirect(), - self.base.redirect_modifier(), - self.base.is_fork(), - ); - if self.command().is_some() { - builder.executes(self.command().unwrap()); - } - builder - } - - fn get_examples(&self) -> Vec { - todo!() - } -} diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs deleted file mode 100644 index 3dc22583..00000000 --- a/azalea-brigadier/src/tree/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod argument_command_node; -pub mod command_node; -pub mod literal_command_node; -pub mod root_command_node; diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs deleted file mode 100644 index 6b8bc157..00000000 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ /dev/null @@ -1,79 +0,0 @@ -use super::{ - argument_command_node::ArgumentCommandNode, - command_node::{BaseCommandNode, CommandNodeTrait}, - literal_command_node::LiteralCommandNode, -}; -use crate::{ - arguments::argument_type::ArgumentType, - builder::argument_builder::ArgumentBuilder, - command::Command, - context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, - redirect_modifier::RedirectModifier, - string_reader::StringReader, - suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, -}; -use std::{ - any::Any, - collections::HashMap, - fmt::{Debug, Display, Formatter}, -}; - -#[derive(Default)] -pub struct RootCommandNode { - // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -impl CommandNodeTrait for RootCommandNode { - fn name(&self) -> &str { - "" - } - - fn parse( - &self, - reader: &mut StringReader<'_>, - context_builder: CommandContextBuilder, - ) -> Result<(), CommandSyntaxException> { - Ok(()) - } - - fn list_suggestions( - &self, - context: CommandContext, - builder: &SuggestionsBuilder, - ) -> Result { - Ok(Suggestions::default()) - } - - fn is_valid_input(&self, input: &str) -> bool { - false - } - - fn usage_text(&self) -> &str { - "" - } - - fn create_builder(&self) -> Box> { - panic!("Cannot convert root into a builder"); - } - - fn get_examples(&self) -> Vec { - vec![] - } -} - -impl Display for RootCommandNode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "") - } -} From 82ed6baea5c4e0d00f5fc2bbeb45cbb2838a3784 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 14:03:21 -0500 Subject: [PATCH 31/83] Clean up some old stuff --- Cargo.lock | 50 ------------------------------------ azalea-brigadier/Cargo.toml | 3 --- azalea-brigadier/src/main.rs | 38 --------------------------- 3 files changed, 91 deletions(-) delete mode 100644 azalea-brigadier/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index d52792ce..67259fef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,11 +70,6 @@ dependencies = [ [[package]] name = "azalea-brigadier" version = "0.1.0" -dependencies = [ - "dyn-clonable", - "enum_dispatch", - "lazy_static", -] [[package]] name = "azalea-chat" @@ -338,33 +333,6 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" -[[package]] -name = "dyn-clonable" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" -dependencies = [ - "dyn-clonable-impl", - "dyn-clone", -] - -[[package]] -name = "dyn-clonable-impl" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dyn-clone" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" - [[package]] name = "either" version = "1.6.1" @@ -383,18 +351,6 @@ dependencies = [ "syn", ] -[[package]] -name = "enum_dispatch" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd53b3fde38a39a06b2e66dc282f3e86191e53bd04cc499929c15742beae3df8" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "flate2" version = "1.0.22" @@ -730,12 +686,6 @@ dependencies = [ "libc", ] -[[package]] -name = "once_cell" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" - [[package]] name = "oorandom" version = "11.1.3" diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index 6dead502..a7ebf618 100644 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -6,6 +6,3 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lazy_static = "^1.4" -dyn-clonable = "^0.9" -enum_dispatch = "^0.3" diff --git a/azalea-brigadier/src/main.rs b/azalea-brigadier/src/main.rs deleted file mode 100644 index 53f2efa8..00000000 --- a/azalea-brigadier/src/main.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::rc::Rc; - -use rust_command_parser::{ - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - dispatcher::CommandDispatcher, - parsers::integer, -}; - -struct CommandSourceStack { - player: String, -} - -pub fn main() { - let mut dispatcher = CommandDispatcher::>::new(); - - let source = Rc::new(CommandSourceStack { - player: "player".to_string(), - }); - - dispatcher.register( - literal("foo") - .then(argument("bar", integer()).executes(|c| { - // println!("Bar is {}", get_integer(c, "bar")); - 2 - })) - .executes(|c| { - println!("Called foo with no arguments"); - 1 - }), - ); - - let parse = dispatcher.parse("foo 123".to_string().into(), source); - println!("{:?}", parse); - println!( - "{}", - CommandDispatcher::>::execute(parse).unwrap() - ); -} From 10cd1733cbba5c637fa0130a0cd7a7ab6e618226 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 14:40:26 -0500 Subject: [PATCH 32/83] add execute & get_integer --- .../src/builder/argument_builder.rs | 1 - .../src/builder/required_argument_builder.rs | 7 ++- azalea-brigadier/src/context.rs | 5 ++ azalea-brigadier/src/dispatcher.rs | 13 +++-- .../src/exceptions/builtin_exceptions.rs | 20 ++++---- azalea-brigadier/src/lib.rs | 13 ++--- azalea-brigadier/src/parsers.rs | 51 ++++++++++++++++--- azalea-brigadier/src/string_reader.rs | 8 +++ azalea-brigadier/src/tree.rs | 3 -- 9 files changed, 82 insertions(+), 39 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 1fb775c2..17e9d625 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -67,7 +67,6 @@ impl ArgumentBuilder { } pub fn build(self) -> CommandNode { - println!("building {:?}", self); CommandNode { value: self.value, diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 95f4da01..9cd089de 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,5 +1,8 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; -use crate::{parsers::Parser, string_reader::StringReader}; +use crate::{ + exceptions::command_syntax_exception::CommandSyntaxException, parsers::Parser, + string_reader::StringReader, +}; use std::{any::Any, fmt::Debug, rc::Rc}; /// An argument node type. The `T` type parameter is the type of the argument, @@ -17,7 +20,7 @@ impl Argument { } } - pub fn parse(&self, reader: &mut StringReader) -> Option> { + pub fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { self.parser.parse(reader) } } diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index 6d4dec88..a68c1da5 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -142,4 +142,9 @@ impl CommandContext { pub fn has_nodes(&self) -> bool { return !self.nodes.is_empty(); } + + pub fn argument(&self, name: &str) -> Option> { + let argument = self.arguments.get(name); + argument.map(|a| a.result.clone()) + } } diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 65fe5b0a..b2004859 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -5,7 +5,6 @@ use crate::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, parse_results::ParseResults, - string_range::StringRange, string_reader::StringReader, tree::CommandNode, }; @@ -28,10 +27,8 @@ impl CommandDispatcher { } pub fn register(&mut self, node: ArgumentBuilder) { - println!("register {:#?}", node); let build = Rc::new(RefCell::new(node.build())); self.root.borrow_mut().add_child(&build); - // println!("build: {:#?}", build); } pub fn parse(&self, command: StringReader, source: S) -> ParseResults { @@ -142,7 +139,6 @@ impl CommandDispatcher { }) } let best_potential = potentials.into_iter().next().unwrap(); - println!("chosen {:#?}", best_potential); return Ok(best_potential); } @@ -153,10 +149,14 @@ impl CommandDispatcher { }) } + pub fn execute(&self, input: StringReader, source: S) -> Result { + let parse = self.parse(input, source); + Self::execute_parsed(parse) + } + /// Executes a given pre-parsed command. - pub fn execute(parse: ParseResults) -> Result { + pub fn execute_parsed(parse: ParseResults) -> Result { if parse.reader.can_read() { - println!("can read from reader {}", parse.reader.cursor); if parse.exceptions.len() == 1 { return Err(parse.exceptions.values().next().unwrap().clone()); } @@ -169,7 +169,6 @@ impl CommandDispatcher { BuiltInExceptions::DispatcherUnknownArgument.create_with_context(&parse.reader) ); } - println!("a"); let mut result = 0i32; let mut successful_forks = 0; let mut forked = false; diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index 5f2e1605..09951a03 100644 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -6,17 +6,17 @@ use super::command_syntax_exception::CommandSyntaxException; #[derive(Clone, PartialEq)] pub enum BuiltInExceptions { - DoubleTooSmall { found: usize, min: usize }, - DoubleTooBig { found: usize, max: usize }, + DoubleTooSmall { found: f64, min: f64 }, + DoubleTooBig { found: f64, max: f64 }, - FloatTooSmall { found: usize, min: usize }, - FloatTooBig { found: usize, max: usize }, + FloatTooSmall { found: f32, min: f32 }, + FloatTooBig { found: f32, max: f32 }, - IntegerTooSmall { found: usize, min: usize }, - IntegerTooBig { found: usize, max: usize }, + IntegerTooSmall { found: i32, min: i32 }, + IntegerTooBig { found: i32, max: i32 }, - LONGTooSmall { found: usize, min: usize }, - LONGTooBig { found: usize, max: usize }, + LongTooSmall { found: i64, min: i64 }, + LongTooBig { found: i64, max: i64 }, LiteralIncorrect { expected: String }, @@ -65,10 +65,10 @@ impl fmt::Debug for BuiltInExceptions { write!(f, "Integer must not be more than {}, found {}", max, found) } - BuiltInExceptions::LONGTooSmall { found, min } => { + BuiltInExceptions::LongTooSmall { found, min } => { write!(f, "Long must not be less than {}, found {}", min, found) } - BuiltInExceptions::LONGTooBig { found, max } => { + BuiltInExceptions::LongTooBig { found, max } => { write!(f, "Long must not be more than {}, found {}", max, found) } diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 476764d0..2f4fffe9 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -18,7 +18,7 @@ mod tests { use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, dispatcher::CommandDispatcher, - parsers::integer, + parsers::{get_integer, integer}, }; struct CommandSourceStack { @@ -36,7 +36,7 @@ mod tests { dispatcher.register( literal("foo") .then(argument("bar", integer()).executes(|c| { - // println!("Bar is {}", get_integer(c, "bar")); + println!("Bar is {:?}", get_integer(c, "bar")); 2 })) .executes(|c| { @@ -45,11 +45,8 @@ mod tests { }), ); - let parse = dispatcher.parse("foo 123".to_string().into(), source); - println!( - "{}", - CommandDispatcher::>::execute(parse).unwrap() - ); - // assert_eq!(dispatcher.execute("foo bar", source), 2); + let parse = dispatcher.parse("foo 123".into(), source.clone()); + assert_eq!(CommandDispatcher::<_>::execute_parsed(parse).unwrap(), 2); + assert_eq!(dispatcher.execute("foo".into(), source).unwrap(), 1); } } diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index a497e0d1..bdf34438 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -1,21 +1,56 @@ use std::{any::Any, marker::PhantomData, rc::Rc}; -use crate::string_reader::StringReader; +use crate::{ + context::CommandContext, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + string_reader::StringReader, +}; pub trait Parser { - fn parse(&self, reader: &mut StringReader) -> Option>; + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; +} + +#[derive(Default)] +struct Integer { + pub minimum: Option, + pub maximum: Option, } -struct Integer {} impl Parser for Integer { - fn parse(&self, reader: &mut StringReader) -> Option> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { let start = reader.cursor; - let result = reader.read_int(); - // TODO: check min and max - Some(Rc::new(result)) + let result = reader.read_int()?; + if let Some(minimum) = self.minimum { + if result < minimum { + return Err(BuiltInExceptions::IntegerTooSmall { + found: result, + min: minimum, + } + .create_with_context(reader)); + } + } + if let Some(maximum) = self.maximum { + if result > maximum { + return Err(BuiltInExceptions::IntegerTooBig { + found: result, + max: maximum, + } + .create_with_context(reader)); + } + } + Ok(Rc::new(result)) } } pub fn integer() -> impl Parser { - Integer {} + Integer::default() +} +pub fn get_integer(context: &CommandContext, name: &str) -> Option { + context + .argument(name) + .unwrap() + .downcast_ref::() + .map(|x| *x) } diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 4b390155..403b8e99 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -18,6 +18,14 @@ impl From for StringReader { Self { string, cursor: 0 } } } +impl From<&str> for StringReader { + fn from(string: &str) -> Self { + Self { + string: string.to_string(), + cursor: 0, + } + } +} impl StringReader { pub fn string(&self) -> &str { diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index 2f023697..c81c599f 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -67,8 +67,6 @@ impl CommandNode { pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { let literals = self.literals(); - println!("get relevant nodes {:?} literals={:?}", self, literals); - if literals.len() > 0 { let cursor = input.cursor(); while input.can_read() && input.peek() != ' ' { @@ -220,7 +218,6 @@ impl Debug for CommandNode { impl Default for CommandNode { fn default() -> Self { - println!("making default node"); Self { value: ArgumentBuilderType::Literal(Literal::default()), From 2e904225611b66fa72b082e4f5e188b55b333fcd Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 15:57:28 -0500 Subject: [PATCH 33/83] Fix clippy issues and add a couple tests to dispatcher --- .../src/builder/argument_builder.rs | 8 +- .../src/builder/literal_argument_builder.rs | 9 -- .../src/builder/required_argument_builder.rs | 7 +- azalea-brigadier/src/context.rs | 10 +- azalea-brigadier/src/dispatcher.rs | 132 ++++++++++++++++-- .../exceptions/command_syntax_exception.rs | 9 +- azalea-brigadier/src/message.rs | 2 - azalea-brigadier/src/parsers.rs | 6 +- azalea-brigadier/src/string_reader.rs | 18 +-- azalea-brigadier/src/tree.rs | 33 ++--- .../arguments/bool_argument_type_test.rs | 0 .../arguments/double_argument_type_test.rs | 0 .../arguments/float_argument_type_test.rs | 0 .../arguments/integer_argument_type_test.rs | 0 .../arguments/long_argument_type_test.rs | 0 .../arguments/string_argument_type_test.rs | 0 .../tests/builder/argument_builder_test.rs | 0 .../builder/literal_argument_builder_test.rs | 0 .../builder/required_argument_builder_test.rs | 0 .../tests/command_dispatcher_test.rs | 1 - .../tests/command_dispatcher_usages_test.rs | 1 - .../tests/command_suggestions_test.rs | 1 - .../tests/context/command_context_test.rs | 0 .../tests/context/parsed_argument_test.rs | 0 ...amic_command_syntax_exception_type_test.rs | 0 ...mple_command_syntax_exception_type_test.rs | 0 .../tests/suggestion/suggestion_test.rs | 0 .../suggestion/suggestions_builder_test.rs | 0 .../tests/suggestion/suggestions_test.rs | 0 .../tests/tree/abstract_command_node_test.rs | 0 .../tests/tree/argument_command_node_test.rs | 0 .../tests/tree/literal_command_node_test.rs | 0 .../tests/tree/root_command_node_test.rs | 0 33 files changed, 158 insertions(+), 79 deletions(-) delete mode 100644 azalea-brigadier/tests/arguments/bool_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/double_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/float_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/integer_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/long_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/string_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/builder/argument_builder_test.rs delete mode 100644 azalea-brigadier/tests/builder/literal_argument_builder_test.rs delete mode 100644 azalea-brigadier/tests/builder/required_argument_builder_test.rs delete mode 100644 azalea-brigadier/tests/command_dispatcher_test.rs delete mode 100644 azalea-brigadier/tests/command_dispatcher_usages_test.rs delete mode 100644 azalea-brigadier/tests/command_suggestions_test.rs delete mode 100644 azalea-brigadier/tests/context/command_context_test.rs delete mode 100644 azalea-brigadier/tests/context/parsed_argument_test.rs delete mode 100644 azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs delete mode 100644 azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs delete mode 100644 azalea-brigadier/tests/suggestion/suggestion_test.rs delete mode 100644 azalea-brigadier/tests/suggestion/suggestions_builder_test.rs delete mode 100644 azalea-brigadier/tests/suggestion/suggestions_test.rs delete mode 100644 azalea-brigadier/tests/tree/abstract_command_node_test.rs delete mode 100644 azalea-brigadier/tests/tree/argument_command_node_test.rs delete mode 100644 azalea-brigadier/tests/tree/literal_command_node_test.rs delete mode 100644 azalea-brigadier/tests/tree/root_command_node_test.rs diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 17e9d625..6f23457a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -41,21 +41,21 @@ impl ArgumentBuilder { } } - pub fn then(&mut self, node: ArgumentBuilder) -> &mut Self { + pub fn then(&mut self, node: ArgumentBuilder) -> Self { let built_node = node.build(); let name = built_node.name(); let node_reference = Rc::new(RefCell::new(built_node.clone())); self.children .insert(name.to_string(), node_reference.clone()); match &built_node.value { - ArgumentBuilderType::Literal(literal) => { + ArgumentBuilderType::Literal(_) => { self.literals.insert(name.to_string(), node_reference); } - ArgumentBuilderType::Argument(argument) => { + ArgumentBuilderType::Argument(_) => { self.arguments.insert(name.to_string(), node_reference); } } - self + self.clone() } pub fn executes(&mut self, f: F) -> Self diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index d8898540..e5e165d8 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,14 +1,5 @@ use std::any::Any; -use crate::{ - context::CommandContextBuilder, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, - string_range::StringRange, - string_reader::StringReader, -}; - use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; #[derive(Debug, Clone, Default)] diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 9cd089de..0eb5d11a 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -16,7 +16,7 @@ impl Argument { pub fn new(name: &str, parser: Rc) -> Self { Self { name: name.to_string(), - parser: parser, + parser, } } @@ -41,9 +41,6 @@ impl Debug for Argument { } /// Shortcut for creating a new argument builder node. -pub fn argument<'a, S: Any + Clone>( - name: &'a str, - parser: impl Parser + 'static, -) -> ArgumentBuilder { +pub fn argument(name: &str, parser: impl Parser + 'static) -> ArgumentBuilder { ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) } diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index a68c1da5..5ffe2028 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -1,4 +1,4 @@ -use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, ptr, rc::Rc}; +use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; use crate::{ dispatcher::CommandDispatcher, modifier::RedirectModifier, string_range::StringRange, @@ -73,7 +73,7 @@ impl CommandContextBuilder { nodes: self.nodes.clone(), source: self.source.clone(), command: self.command.clone(), - child: self.child.clone().map(|c| Rc::new(c.build(&input))), + child: self.child.clone().map(|c| Rc::new(c.build(input))), range: self.range.clone(), forks: self.forks, modifier: self.modifier.clone(), @@ -125,7 +125,7 @@ impl CommandContext { if Rc::ptr_eq(&source, &self.source) { return self.clone(); } - return CommandContext { + CommandContext { source, input: self.input.clone(), arguments: self.arguments.clone(), @@ -136,11 +136,11 @@ impl CommandContext { child: self.child.clone(), modifier: self.modifier.clone(), forks: self.forks, - }; + } } pub fn has_nodes(&self) -> bool { - return !self.nodes.is_empty(); + !self.nodes.is_empty() } pub fn argument(&self, name: &str) -> Option> { diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index b2004859..029f0ed0 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -75,16 +75,14 @@ impl CommandDispatcher { reader.cursor = cursor; continue; } - if reader.can_read() { - if reader.peek() != ' ' { - errors.insert( - Rc::new((*child.borrow()).clone()), - BuiltInExceptions::DispatcherExpectedArgumentSeparator - .create_with_context(&reader), - ); - reader.cursor = cursor; - continue; - } + if reader.can_read() && reader.peek() != ' ' { + errors.insert( + Rc::new((*child.borrow()).clone()), + BuiltInExceptions::DispatcherExpectedArgumentSeparator + .create_with_context(&reader), + ); + reader.cursor = cursor; + continue; } context.with_command(&child.borrow().command); @@ -120,7 +118,7 @@ impl CommandDispatcher { } } - if potentials.len() > 0 { + if !potentials.is_empty() { if potentials.len() > 1 { potentials.sort_by(|a, b| { if !a.reader.can_read() && b.reader.can_read() { @@ -174,11 +172,11 @@ impl CommandDispatcher { let mut forked = false; let mut found_command = false; let command = parse.reader.string(); - let original = parse.context.build(&command); + let original = parse.context.build(command); let mut contexts = vec![original]; let mut next: Vec> = vec![]; - while contexts.len() > 0 { + while !contexts.is_empty() { for context in contexts.iter() { let child = &context.child; if let Some(child) = child { @@ -239,3 +237,111 @@ impl Clone for CommandDispatcher { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::builder::literal_argument_builder::literal; + + struct CommandSource {} + + fn input_with_offset(input: &str, offset: usize) -> StringReader { + let mut result: StringReader = input.into(); + result.cursor = offset; + result + } + + // @Test + // public void testCreateAndExecuteCommand() throws Exception { + // subject.register(literal("foo").executes(command)); + + // assertThat(subject.execute("foo", source), is(42)); + // verify(command).run(any(CommandContext.class)); + // } + #[test] + fn create_and_execute_command() { + let mut subject = CommandDispatcher::>::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute("foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + // @Test + // public void testCreateAndExecuteOffsetCommand() throws Exception { + // subject.register(literal("foo").executes(command)); + + // assertThat(subject.execute(inputWithOffset("/foo", 1), source), is(42)); + // verify(command).run(any(CommandContext.class)); + // } + #[test] + fn create_and_execute_offset_command() { + let mut subject = CommandDispatcher::>::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute(input_with_offset("/foo", 1), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + // @Test + // public void testCreateAndMergeCommands() throws Exception { + // subject.register(literal("base").then(literal("foo").executes(command))); + // subject.register(literal("base").then(literal("bar").executes(command))); + + // assertThat(subject.execute("base foo", source), is(42)); + // assertThat(subject.execute("base bar", source), is(42)); + // verify(command, times(2)).run(any(CommandContext.class)); + // } + #[test] + fn create_and_merge_commands() { + let mut subject = CommandDispatcher::>::new(); + subject.register(literal("base").then(literal("foo").executes(|_| 42))); + subject.register(literal("base").then(literal("bar").executes(|_| 42))); + + assert_eq!( + subject + .execute("base foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + assert_eq!( + subject + .execute("base bar".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + // @Test + // public void testExecuteUnknownCommand() throws Exception { + // subject.register(literal("bar")); + // subject.register(literal("baz")); + + // try { + // subject.execute("foo", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); + // assertThat(ex.getCursor(), is(0)); + // } + // } + // #[test] + // fn execute_unknown_command() { + // let mut subject = CommandDispatcher::>::new(); + // subject.register(literal("bar")); + // subject.register(literal("baz")); + + // assert_eq!( + // subject + // .execute("foo".into(), Rc::new(CommandSource {})) + // .err() + // .unwrap(), + // BuiltInExceptions::DispatcherUnknownCommand.create() + // ); + // } +} diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index 6cd4e53d..93ac788c 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -1,9 +1,9 @@ -use std::{cmp, fmt, rc::Rc}; +use std::{cmp, fmt}; use super::builtin_exceptions::BuiltInExceptions; use crate::message::Message; -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct CommandSyntaxException { type_: BuiltInExceptions, message: Message, @@ -12,7 +12,6 @@ pub struct CommandSyntaxException { } const CONTEXT_AMOUNT: usize = 10; -const ENABLE_COMMAND_STACK_TRACES: bool = true; impl CommandSyntaxException { pub fn new(type_: BuiltInExceptions, message: Message, input: &str, cursor: usize) -> Self { @@ -76,8 +75,8 @@ impl CommandSyntaxException { &self.type_ } - pub fn input(&self) -> String { - self.input() + pub fn input(&self) -> &Option { + &self.input } pub fn cursor(&self) -> Option { diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index 9d133c7e..75e07d4e 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Message(String); diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index bdf34438..77e57ace 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -1,4 +1,4 @@ -use std::{any::Any, marker::PhantomData, rc::Rc}; +use std::{any::Any, rc::Rc}; use crate::{ context::CommandContext, @@ -24,6 +24,7 @@ impl Parser for Integer { let result = reader.read_int()?; if let Some(minimum) = self.minimum { if result < minimum { + reader.cursor = start; return Err(BuiltInExceptions::IntegerTooSmall { found: result, min: minimum, @@ -33,6 +34,7 @@ impl Parser for Integer { } if let Some(maximum) = self.maximum { if result > maximum { + reader.cursor = start; return Err(BuiltInExceptions::IntegerTooBig { found: result, max: maximum, @@ -52,5 +54,5 @@ pub fn get_integer(context: &CommandContext, name: &str) -> O .argument(name) .unwrap() .downcast_ref::() - .map(|x| *x) + .copied() } diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 403b8e99..5825871f 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,7 +1,7 @@ use crate::exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }; -use std::{rc::Rc, str::FromStr}; +use std::str::FromStr; #[derive(Clone)] pub struct StringReader { @@ -79,7 +79,7 @@ impl StringReader { } pub fn is_allowed_number(c: char) -> bool { - c >= '0' && c <= '9' || c == '.' || c == '-' + ('0'..='9').contains(&c) || c == '.' || c == '-' } pub fn is_quoted_string_start(c: char) -> bool { @@ -177,9 +177,9 @@ impl StringReader { } pub fn is_allowed_in_unquoted_string(c: char) -> bool { - c >= '0' && c <= '9' - || c >= 'A' && c <= 'Z' - || c >= 'a' && c <= 'z' + ('0'..='9').contains(&c) + || ('A'..='Z').contains(&c) + || ('a'..='z').contains(&c) || c == '_' || c == '-' || c == '.' @@ -232,7 +232,7 @@ impl StringReader { } } - return Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self)); + Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self)) } pub fn read_string(&mut self) -> Result { @@ -255,12 +255,12 @@ impl StringReader { } if value == "true" { - return Ok(true); + Ok(true) } else if value == "false" { - return Ok(false); + Ok(false) } else { self.cursor = start; - return Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self)); + Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self)) } } diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index c81c599f..5c33a879 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -11,15 +11,7 @@ use crate::{ string_range::StringRange, string_reader::StringReader, }; -use std::{ - any::Any, - cell::RefCell, - collections::{BTreeMap, HashMap}, - fmt::Debug, - hash::Hash, - ptr, - rc::Rc, -}; +use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; /// An ArgumentBuilder that has been built. #[derive(Clone)] @@ -67,7 +59,7 @@ impl CommandNode { pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { let literals = self.literals(); - if literals.len() > 0 { + if !literals.is_empty() { let cursor = input.cursor(); while input.can_read() && input.peek() != ' ' { input.skip(); @@ -83,18 +75,10 @@ impl CommandNode { if let Some(literal) = literal { return vec![literal.clone()]; } else { - return self - .arguments() - .values() - .map(|argument| argument.clone()) - .collect(); + return self.arguments().values().cloned().collect(); } } else { - return self - .arguments() - .values() - .map(|argument| argument.clone()) - .collect(); + self.arguments().values().cloned().collect() } } @@ -147,7 +131,7 @@ impl CommandNode { .expect("Couldn't get result for some reason"); let parsed = ParsedArgument { range: StringRange::between(start, reader.cursor()), - result: result, + result, }; context_builder.with_argument(&argument.name, parsed.clone()); @@ -175,7 +159,7 @@ impl CommandNode { fn parse(&self, reader: &mut StringReader) -> Option { match self.value { - ArgumentBuilderType::Argument(ref argument) => { + ArgumentBuilderType::Argument(_) => { panic!("Can't parse argument.") } ArgumentBuilderType::Literal(ref literal) => { @@ -252,7 +236,9 @@ impl PartialEq for CommandNode { return false; } if let Some(selfexecutes) = &self.command { + // idk how to do this better since we can't compare `dyn Fn`s if let Some(otherexecutes) = &other.command { + #[allow(clippy::vtable_address_comparisons)] if !Rc::ptr_eq(selfexecutes, otherexecutes) { return false; } @@ -260,6 +246,9 @@ impl PartialEq for CommandNode { return false; } } + else if other.command.is_some() { + return false; + } true } } diff --git a/azalea-brigadier/tests/arguments/bool_argument_type_test.rs b/azalea-brigadier/tests/arguments/bool_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/double_argument_type_test.rs b/azalea-brigadier/tests/arguments/double_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/float_argument_type_test.rs b/azalea-brigadier/tests/arguments/float_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/integer_argument_type_test.rs b/azalea-brigadier/tests/arguments/integer_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/long_argument_type_test.rs b/azalea-brigadier/tests/arguments/long_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/string_argument_type_test.rs b/azalea-brigadier/tests/arguments/string_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/builder/argument_builder_test.rs b/azalea-brigadier/tests/builder/argument_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/builder/literal_argument_builder_test.rs b/azalea-brigadier/tests/builder/literal_argument_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/builder/required_argument_builder_test.rs b/azalea-brigadier/tests/builder/required_argument_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/tests/command_dispatcher_test.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/tests/command_dispatcher_usages_test.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/tests/command_suggestions_test.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/tests/context/command_context_test.rs b/azalea-brigadier/tests/context/command_context_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/context/parsed_argument_test.rs b/azalea-brigadier/tests/context/parsed_argument_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs b/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/abstract_command_node_test.rs b/azalea-brigadier/tests/tree/abstract_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/argument_command_node_test.rs b/azalea-brigadier/tests/tree/argument_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/literal_command_node_test.rs b/azalea-brigadier/tests/tree/literal_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/root_command_node_test.rs b/azalea-brigadier/tests/tree/root_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 From 6f3c41e01c466e4ab6abd1268149dd88686bbbc9 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 16:52:34 -0500 Subject: [PATCH 34/83] add .requires --- .../src/builder/argument_builder.rs | 8 +++ azalea-brigadier/src/dispatcher.rs | 51 ++++++++++++++----- .../exceptions/command_syntax_exception.rs | 2 +- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 6f23457a..11c0062c 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -66,6 +66,14 @@ impl ArgumentBuilder { self.clone() } + pub fn requires(&mut self, requirement: F) -> Self + where + F: Fn(Rc) -> bool + 'static, + { + self.requirement = Rc::new(requirement); + self.clone() + } + pub fn build(self) -> CommandNode { CommandNode { value: self.value, diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 029f0ed0..683348e3 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -330,18 +330,45 @@ mod tests { // assertThat(ex.getCursor(), is(0)); // } // } - // #[test] - // fn execute_unknown_command() { - // let mut subject = CommandDispatcher::>::new(); - // subject.register(literal("bar")); - // subject.register(literal("baz")); + #[test] + fn execute_unknown_command() { + let mut subject = CommandDispatcher::>::new(); + subject.register(literal("bar")); + subject.register(literal("baz")); - // assert_eq!( - // subject - // .execute("foo".into(), Rc::new(CommandSource {})) - // .err() - // .unwrap(), - // BuiltInExceptions::DispatcherUnknownCommand.create() - // ); + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + // @Test + // public void testExecuteImpermissibleCommand() throws Exception { + // subject.register(literal("foo").requires(s -> false)); + + // try { + // subject.execute("foo", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); + // assertThat(ex.getCursor(), is(0)); + // } // } + #[test] + fn execute_impermissible_command() { + let mut subject = CommandDispatcher::>::new(); + subject.register(literal("foo").requires(|_| false)); + + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } } diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index 93ac788c..4bfe9cda 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -5,7 +5,7 @@ use crate::message::Message; #[derive(Clone, PartialEq)] pub struct CommandSyntaxException { - type_: BuiltInExceptions, + pub type_: BuiltInExceptions, message: Message, input: Option, cursor: Option, From e945bfac77ba84497559a46cb869345225579cbe Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 18:08:19 -0500 Subject: [PATCH 35/83] more tests and start adding redirects --- .../src/builder/argument_builder.rs | 39 ++- azalea-brigadier/src/dispatcher.rs | 293 +++++++++++++++++- 2 files changed, 316 insertions(+), 16 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 11c0062c..ee6682dd 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -12,14 +12,15 @@ pub enum ArgumentBuilderType { /// A node that hasn't yet been built. #[derive(Clone)] pub struct ArgumentBuilder { - value: ArgumentBuilderType, - + arguments: BTreeMap>>>, children: BTreeMap>>>, literals: BTreeMap>>>, - arguments: BTreeMap>>>, - executes: Option) -> i32>>, + command: Option) -> i32>>, requirement: Rc) -> bool>, + target: Option>>>, + + value: ArgumentBuilderType, forks: bool, modifier: Option>>, } @@ -34,10 +35,11 @@ impl ArgumentBuilder { children: BTreeMap::new(), literals: BTreeMap::new(), arguments: BTreeMap::new(), - executes: None, + command: None, requirement: Rc::new(|_| true), forks: false, modifier: None, + target: None, } } @@ -62,7 +64,7 @@ impl ArgumentBuilder { where F: Fn(&CommandContext) -> i32 + 'static, { - self.executes = Some(Rc::new(f)); + self.command = Some(Rc::new(f)); self.clone() } @@ -74,6 +76,25 @@ impl ArgumentBuilder { self.clone() } + pub fn redirect(&mut self, target: Rc>>) -> Self { + self.forward(target, None, false) + } + + pub fn forward( + &mut self, + target: Rc>>, + modifier: Option>>, + fork: bool, + ) -> Self { + if !self.arguments.is_empty() { + panic!("Cannot forward a node with children"); + } + self.target = Some(target); + self.modifier = modifier; + self.forks = fork; + self.clone() + } + pub fn build(self) -> CommandNode { CommandNode { value: self.value, @@ -82,9 +103,9 @@ impl ArgumentBuilder { literals: self.literals, arguments: self.arguments, - command: self.executes.clone(), + command: self.command.clone(), requirement: self.requirement.clone(), - redirect: None, + redirect: self.target, forks: self.forks, modifier: self.modifier, } @@ -98,7 +119,7 @@ impl Debug for ArgumentBuilder { .field("children", &self.children) .field("literals", &self.literals) .field("arguments", &self.arguments) - .field("executes", &self.executes.is_some()) + .field("executes", &self.command.is_some()) // .field("requirement", &self.requirement) .field("forks", &self.forks) // .field("modifier", &self.modifier) diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 683348e3..a8ca2ec9 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -26,9 +26,10 @@ impl CommandDispatcher { } } - pub fn register(&mut self, node: ArgumentBuilder) { + pub fn register(&mut self, node: ArgumentBuilder) -> Rc>> { let build = Rc::new(RefCell::new(node.build())); self.root.borrow_mut().add_child(&build); + build } pub fn parse(&self, command: StringReader, source: S) -> ParseResults { @@ -241,7 +242,10 @@ impl Clone for CommandDispatcher { #[cfg(test)] mod tests { use super::*; - use crate::builder::literal_argument_builder::literal; + use crate::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + parsers::integer, + }; struct CommandSource {} @@ -260,7 +264,7 @@ mod tests { // } #[test] fn create_and_execute_command() { - let mut subject = CommandDispatcher::>::new(); + let mut subject = CommandDispatcher::new(); subject.register(literal("foo").executes(|_| 42)); assert_eq!( @@ -279,7 +283,7 @@ mod tests { // } #[test] fn create_and_execute_offset_command() { - let mut subject = CommandDispatcher::>::new(); + let mut subject = CommandDispatcher::new(); subject.register(literal("foo").executes(|_| 42)); assert_eq!( @@ -300,7 +304,7 @@ mod tests { // } #[test] fn create_and_merge_commands() { - let mut subject = CommandDispatcher::>::new(); + let mut subject = CommandDispatcher::new(); subject.register(literal("base").then(literal("foo").executes(|_| 42))); subject.register(literal("base").then(literal("bar").executes(|_| 42))); @@ -332,7 +336,7 @@ mod tests { // } #[test] fn execute_unknown_command() { - let mut subject = CommandDispatcher::>::new(); + let mut subject = CommandDispatcher::new(); subject.register(literal("bar")); subject.register(literal("baz")); @@ -359,7 +363,7 @@ mod tests { // } #[test] fn execute_impermissible_command() { - let mut subject = CommandDispatcher::>::new(); + let mut subject = CommandDispatcher::new(); subject.register(literal("foo").requires(|_| false)); let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); @@ -371,4 +375,279 @@ mod tests { } assert_eq!(err.cursor().unwrap(), 0); } + // @Test + // public void testExecuteEmptyCommand() throws Exception { + // subject.register(literal("")); + + // try { + // subject.execute("", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); + // assertThat(ex.getCursor(), is(0)); + // } + // } + #[test] + fn execute_empty_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("")); + + let execute_result = subject.execute("".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + // @Test + // public void testExecuteUnknownSubcommand() throws Exception { + // subject.register(literal("foo").executes(command)); + + // try { + // subject.execute("foo bar", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument())); + // assertThat(ex.getCursor(), is(4)); + // } + // } + #[test] + fn execute_unknown_subcommand() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + let execute_result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + // @Test + // public void testExecuteIncorrectLiteral() throws Exception { + // subject.register(literal("foo").executes(command).then(literal("bar"))); + + // try { + // subject.execute("foo baz", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument())); + // assertThat(ex.getCursor(), is(4)); + // } + // } + #[test] + fn execute_incorrect_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42).then(literal("bar"))); + + let execute_result = subject.execute("foo baz".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + // @Test + // public void testExecuteAmbiguousIncorrectArgument() throws Exception { + // subject.register( + // literal("foo").executes(command) + // .then(literal("bar")) + // .then(literal("baz")) + // ); + + // try { + // subject.execute("foo unknown", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument())); + // assertThat(ex.getCursor(), is(4)); + // } + // } + #[test] + fn execute_ambiguous_incorrect_argument() { + let mut subject = CommandDispatcher::new(); + subject.register( + literal("foo") + .executes(|_| 42) + .then(literal("bar")) + .then(literal("baz")), + ); + + let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + + // @Test + // public void testExecuteSubcommand() throws Exception { + // final Command subCommand = mock(Command.class); + // when(subCommand.run(any())).thenReturn(100); + + // subject.register(literal("foo").then( + // literal("a") + // ).then( + // literal("=").executes(subCommand) + // ).then( + // literal("c") + // ).executes(command)); + + // assertThat(subject.execute("foo =", source), is(100)); + // verify(subCommand).run(any(CommandContext.class)); + // } + #[test] + fn test_execute_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(literal("a")) + .then(literal("=").executes(|_| 100)) + .then(literal("c")) + .executes(|_| 42), + ); + + assert_eq!( + subject + .execute("foo =".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + // @Test + // public void testParseIncompleteLiteral() throws Exception { + // subject.register(literal("foo").then(literal("bar").executes(command))); + + // final ParseResults parse = subject.parse("foo ", source); + // assertThat(parse.getReader().getRemaining(), equalTo(" ")); + // assertThat(parse.getContext().getNodes().size(), is(1)); + // } + #[test] + fn test_parse_incomplete_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(literal("bar").executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); + } + // @Test + // public void testParseIncompleteArgument() throws Exception { + // subject.register(literal("foo").then(argument("bar", integer()).executes(command))); + + // final ParseResults parse = subject.parse("foo ", source); + // assertThat(parse.getReader().getRemaining(), equalTo(" ")); + // assertThat(parse.getContext().getNodes().size(), is(1)); + // } + #[test] + fn test_parse_incomplete_argument() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); + } + + // @Test + // public void testExecuteAmbiguiousParentSubcommand() throws Exception { + // final Command subCommand = mock(Command.class); + // when(subCommand.run(any())).thenReturn(100); + + // subject.register( + // literal("test") + // .then( + // argument("incorrect", integer()) + // .executes(command) + // ) + // .then( + // argument("right", integer()) + // .then( + // argument("sub", integer()) + // .executes(subCommand) + // ) + // ) + // ); + + // assertThat(subject.execute("test 1 2", source), is(100)); + // verify(subCommand).run(any(CommandContext.class)); + // verify(command, never()).run(any()); + // } + #[test] + fn test_execute_ambiguious_parent_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then( + argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), + ), + ); + + assert_eq!( + subject + .execute("test 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + + // @Test + // public void testExecuteAmbiguiousParentSubcommandViaRedirect() throws Exception { + // final Command subCommand = mock(Command.class); + // when(subCommand.run(any())).thenReturn(100); + + // final LiteralCommandNode real = subject.register( + // literal("test") + // .then( + // argument("incorrect", integer()) + // .executes(command) + // ) + // .then( + // argument("right", integer()) + // .then( + // argument("sub", integer()) + // .executes(subCommand) + // ) + // ) + // ); + + // subject.register(literal("redirect").redirect(real)); + + // assertThat(subject.execute("redirect 1 2", source), is(100)); + // verify(subCommand).run(any(CommandContext.class)); + // verify(command, never()).run(any()); + // } + // #[test] + // fn test_execute_ambiguious_parent_subcommand_via_redirect() { + // let mut subject = CommandDispatcher::new(); + + // let real = subject.register( + // literal("test") + // .then(argument("incorrect", integer()).executes(|_| 42)) + // .then( + // argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), + // ), + // ); + + // subject.register(literal("redirect").redirect(real)); + + // assert_eq!( + // subject + // .execute("redirect 1 2".into(), Rc::new(CommandSource {})) + // .unwrap(), + // 100 + // ); + // } } From 14625e2bce15eeef35d2ae7fcce1e21a491f32aa Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 18:17:20 -0500 Subject: [PATCH 36/83] Change ArgumentBuilder to have a CommandNode --- .../src/builder/argument_builder.rs | 63 +++++++------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index ee6682dd..f17fe4fa 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -12,29 +12,24 @@ pub enum ArgumentBuilderType { /// A node that hasn't yet been built. #[derive(Clone)] pub struct ArgumentBuilder { - arguments: BTreeMap>>>, - children: BTreeMap>>>, - literals: BTreeMap>>>, + arguments: CommandNode, command: Option) -> i32>>, requirement: Rc) -> bool>, target: Option>>>, - value: ArgumentBuilderType, forks: bool, modifier: Option>>, } -// todo: maybe remake this to be based on a CommandNode like vanilla does? - /// A node that isn't yet built. impl ArgumentBuilder { pub fn new(value: ArgumentBuilderType) -> Self { Self { - value, - children: BTreeMap::new(), - literals: BTreeMap::new(), - arguments: BTreeMap::new(), + arguments: CommandNode { + value, + ..Default::default() + }, command: None, requirement: Rc::new(|_| true), forks: false, @@ -43,20 +38,9 @@ impl ArgumentBuilder { } } - pub fn then(&mut self, node: ArgumentBuilder) -> Self { - let built_node = node.build(); - let name = built_node.name(); - let node_reference = Rc::new(RefCell::new(built_node.clone())); - self.children - .insert(name.to_string(), node_reference.clone()); - match &built_node.value { - ArgumentBuilderType::Literal(_) => { - self.literals.insert(name.to_string(), node_reference); - } - ArgumentBuilderType::Argument(_) => { - self.arguments.insert(name.to_string(), node_reference); - } - } + pub fn then(&mut self, argument: ArgumentBuilder) -> Self { + self.arguments + .add_child(&Rc::new(RefCell::new(argument.build()))); self.clone() } @@ -86,7 +70,7 @@ impl ArgumentBuilder { modifier: Option>>, fork: bool, ) -> Self { - if !self.arguments.is_empty() { + if !self.arguments.children.is_empty() { panic!("Cannot forward a node with children"); } self.target = Some(target); @@ -96,31 +80,31 @@ impl ArgumentBuilder { } pub fn build(self) -> CommandNode { - CommandNode { - value: self.value, - - children: self.children, - literals: self.literals, - arguments: self.arguments, - + let mut result = CommandNode { + value: self.arguments.value, command: self.command.clone(), requirement: self.requirement.clone(), - redirect: self.target, + redirect: self.target.clone(), + modifier: self.modifier.clone(), forks: self.forks, - modifier: self.modifier, + ..Default::default() + }; + + for (_, argument) in &self.arguments.children { + result.add_child(argument); } + + result } } impl Debug for ArgumentBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ArgumentBuilder") - .field("value", &self.value) - .field("children", &self.children) - .field("literals", &self.literals) .field("arguments", &self.arguments) - .field("executes", &self.command.is_some()) + // .field("command", &self.command) // .field("requirement", &self.requirement) + .field("target", &self.target) .field("forks", &self.forks) // .field("modifier", &self.modifier) .finish() @@ -162,9 +146,10 @@ mod tests { let argument: ArgumentBuilder<()> = argument("bar", integer()); builder.then(argument.clone()); - assert_eq!(builder.children.len(), 1); + assert_eq!(builder.arguments.children.len(), 1); let built_argument = Rc::new(argument.build()); assert!(builder + .arguments .children .values() .any(|e| *e.borrow() == *built_argument)); From af4b0d0add23188ab00e89a216cd3db62074cf7e Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 18:36:14 -0500 Subject: [PATCH 37/83] Implement working redirects --- .../src/builder/argument_builder.rs | 11 ++--- azalea-brigadier/src/dispatcher.rs | 41 +++++++++++-------- azalea-brigadier/src/tree.rs | 18 ++------ 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index f17fe4fa..3c9ae3ed 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,7 +1,7 @@ use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode}; use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; -use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, rc::Rc}; +use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc}; #[derive(Debug, Clone)] pub enum ArgumentBuilderType { @@ -38,6 +38,7 @@ impl ArgumentBuilder { } } + // do we need to be cloning here? maybe we could return a ref to self? pub fn then(&mut self, argument: ArgumentBuilder) -> Self { self.arguments .add_child(&Rc::new(RefCell::new(argument.build()))); @@ -82,10 +83,10 @@ impl ArgumentBuilder { pub fn build(self) -> CommandNode { let mut result = CommandNode { value: self.arguments.value, - command: self.command.clone(), - requirement: self.requirement.clone(), - redirect: self.target.clone(), - modifier: self.modifier.clone(), + command: self.command, + requirement: self.requirement, + redirect: self.target, + modifier: self.modifier, forks: self.forks, ..Default::default() }; diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index a8ca2ec9..b62455f5 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -104,6 +104,11 @@ impl CommandDispatcher { .parse_nodes(redirect, &reader, child_context) .expect("Parsing nodes failed"); context.with_child(Rc::new(parse.context)); + return Ok(ParseResults { + context, + reader: parse.reader, + exceptions: parse.exceptions, + }); } else { let parse = self .parse_nodes(&child, &reader, context) @@ -629,25 +634,25 @@ mod tests { // verify(subCommand).run(any(CommandContext.class)); // verify(command, never()).run(any()); // } - // #[test] - // fn test_execute_ambiguious_parent_subcommand_via_redirect() { - // let mut subject = CommandDispatcher::new(); + #[test] + fn test_execute_ambiguious_parent_subcommand_via_redirect() { + let mut subject = CommandDispatcher::new(); - // let real = subject.register( - // literal("test") - // .then(argument("incorrect", integer()).executes(|_| 42)) - // .then( - // argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), - // ), - // ); + let real = subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then( + argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), + ), + ); - // subject.register(literal("redirect").redirect(real)); + subject.register(literal("redirect").redirect(real)); - // assert_eq!( - // subject - // .execute("redirect 1 2".into(), Rc::new(CommandSource {})) - // .unwrap(), - // 100 - // ); - // } + assert_eq!( + subject + .execute("redirect 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } } diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index 5c33a879..b68fbdcf 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -32,15 +32,6 @@ pub struct CommandNode { } impl CommandNode { - // pub fn new() - // TODO: precalculate `literals` and `arguments` and include them in CommandNode - fn literals(&self) -> &BTreeMap>>> { - &self.literals - } - fn arguments(&self) -> &BTreeMap>>> { - &self.arguments - } - /// Gets the literal, or panics. You should use match if you're not certain about the type. pub fn literal(&self) -> &Literal { match self.value { @@ -57,7 +48,7 @@ impl CommandNode { } pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { - let literals = self.literals(); + let literals = &self.literals; if !literals.is_empty() { let cursor = input.cursor(); @@ -75,10 +66,10 @@ impl CommandNode { if let Some(literal) = literal { return vec![literal.clone()]; } else { - return self.arguments().values().cloned().collect(); + return self.arguments.values().cloned().collect(); } } else { - self.arguments().values().cloned().collect() + self.arguments.values().cloned().collect() } } @@ -245,8 +236,7 @@ impl PartialEq for CommandNode { } else { return false; } - } - else if other.command.is_some() { + } else if other.command.is_some() { return false; } true From d68233e0b1ff61d09ab2c4e22a42f3326054539b Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 20:46:43 -0500 Subject: [PATCH 38/83] simplify the generic so it's not an Rc --- .../src/builder/argument_builder.rs | 37 +++- .../src/builder/literal_argument_builder.rs | 2 +- .../src/builder/required_argument_builder.rs | 2 +- azalea-brigadier/src/context.rs | 73 ++++++-- azalea-brigadier/src/dispatcher.rs | 161 +++++++++++++++++- azalea-brigadier/src/lib.rs | 2 +- azalea-brigadier/src/modifier.rs | 9 +- azalea-brigadier/src/parse_results.rs | 4 +- azalea-brigadier/src/parsers.rs | 2 +- azalea-brigadier/src/tree.rs | 56 ++++-- 10 files changed, 295 insertions(+), 53 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 3c9ae3ed..39edfbd3 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,4 +1,7 @@ -use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode}; +use crate::{ + context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, + modifier::RedirectModifier, tree::CommandNode, +}; use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc}; @@ -10,8 +13,7 @@ pub enum ArgumentBuilderType { } /// A node that hasn't yet been built. -#[derive(Clone)] -pub struct ArgumentBuilder { +pub struct ArgumentBuilder { arguments: CommandNode, command: Option) -> i32>>, @@ -19,11 +21,24 @@ pub struct ArgumentBuilder { target: Option>>>, forks: bool, - modifier: Option>>, + modifier: Option>>, +} + +impl Clone for ArgumentBuilder { + fn clone(&self) -> Self { + Self { + arguments: self.arguments.clone(), + command: self.command.clone(), + requirement: self.requirement.clone(), + target: self.target.clone(), + forks: self.forks.clone(), + modifier: self.modifier.clone(), + } + } } /// A node that isn't yet built. -impl ArgumentBuilder { +impl ArgumentBuilder { pub fn new(value: ArgumentBuilderType) -> Self { Self { arguments: CommandNode { @@ -65,10 +80,18 @@ impl ArgumentBuilder { self.forward(target, None, false) } + pub fn fork( + &mut self, + target: Rc>>, + modifier: Rc>, + ) -> Self { + self.forward(target, Some(modifier), true) + } + pub fn forward( &mut self, target: Rc>>, - modifier: Option>>, + modifier: Option>>, fork: bool, ) -> Self { if !self.arguments.children.is_empty() { @@ -99,7 +122,7 @@ impl ArgumentBuilder { } } -impl Debug for ArgumentBuilder { +impl Debug for ArgumentBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ArgumentBuilder") .field("arguments", &self.arguments) diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index e5e165d8..65a5644e 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -21,6 +21,6 @@ impl From for ArgumentBuilderType { } /// Shortcut for creating a new literal builder node. -pub fn literal(value: &str) -> ArgumentBuilder { +pub fn literal(value: &str) -> ArgumentBuilder { ArgumentBuilder::new(ArgumentBuilderType::Literal(Literal::new(value))) } diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 0eb5d11a..cae0cddb 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -41,6 +41,6 @@ impl Debug for Argument { } /// Shortcut for creating a new argument builder node. -pub fn argument(name: &str, parser: impl Parser + 'static) -> ArgumentBuilder { +pub fn argument(name: &str, parser: impl Parser + 'static) -> ArgumentBuilder { ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) } diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index 5ffe2028..b798397b 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -1,25 +1,43 @@ use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; use crate::{ - dispatcher::CommandDispatcher, modifier::RedirectModifier, string_range::StringRange, - tree::CommandNode, + dispatcher::CommandDispatcher, + modifier::RedirectModifier, + string_range::StringRange, + tree::{CommandNode, ParsedCommandNode}, }; -#[derive(Clone)] -pub struct CommandContextBuilder { +pub struct CommandContextBuilder { pub arguments: HashMap, pub root: Rc>>, - pub nodes: Vec>>, + pub nodes: Vec>, pub dispatcher: Rc>, pub source: Rc, pub command: Option) -> i32>>, pub child: Option>>, pub range: StringRange, - pub modifier: Option>>, + pub modifier: Option>>, pub forks: bool, } -impl CommandContextBuilder { +impl Clone for CommandContextBuilder { + fn clone(&self) -> Self { + Self { + arguments: self.arguments.clone(), + root: self.root.clone(), + nodes: self.nodes.clone(), + dispatcher: self.dispatcher.clone(), + source: self.source.clone(), + command: self.command.clone(), + child: self.child.clone(), + range: self.range.clone(), + modifier: self.modifier.clone(), + forks: self.forks.clone(), + } + } +} + +impl CommandContextBuilder { // CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start pub fn new( dispatcher: Rc>, @@ -58,11 +76,14 @@ impl CommandContextBuilder { self.arguments.insert(name.to_string(), argument); self } - pub fn with_node(&mut self, node: Rc>, range: StringRange) -> &Self { - self.nodes.push(node.clone()); + pub fn with_node(&mut self, node: Rc>>, range: StringRange) -> &Self { + self.nodes.push(ParsedCommandNode { + node: node.clone(), + range: range.clone(), + }); self.range = StringRange::encompassing(&self.range, &range); - self.modifier = node.modifier.clone(); - self.forks = node.forks; + self.modifier = node.borrow().modifier.clone(); + self.forks = node.borrow().forks; self } @@ -82,12 +103,12 @@ impl CommandContextBuilder { } } -impl Debug for CommandContextBuilder { +impl Debug for CommandContextBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CommandContextBuilder") // .field("arguments", &self.arguments) .field("root", &self.root) - .field("nodes", &self.nodes) + // .field("nodes", &self.nodes) // .field("dispatcher", &self.dispatcher) // .field("source", &self.source) // .field("command", &self.command) @@ -105,22 +126,38 @@ pub struct ParsedArgument { pub result: Rc, } -#[derive(Clone)] /// A built `CommandContextBuilder`. -pub struct CommandContext { +pub struct CommandContext { pub source: Rc, pub input: String, pub arguments: HashMap, pub command: Option) -> i32>>, pub root_node: Rc>>, - pub nodes: Vec>>, + pub nodes: Vec>, pub range: StringRange, pub child: Option>>, - pub modifier: Option>>, + pub modifier: Option>>, pub forks: bool, } -impl CommandContext { +impl Clone for CommandContext { + fn clone(&self) -> Self { + Self { + source: self.source.clone(), + input: self.input.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + root_node: self.root_node.clone(), + nodes: self.nodes.clone(), + range: self.range.clone(), + child: self.child.clone(), + modifier: self.modifier.clone(), + forks: self.forks.clone(), + } + } +} + +impl CommandContext { pub fn copy_for(&self, source: Rc) -> Self { if Rc::ptr_eq(&source, &self.source) { return self.clone(); diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index b62455f5..6031452e 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -13,12 +13,12 @@ use std::{ }; #[derive(Default)] -pub struct CommandDispatcher { +pub struct CommandDispatcher { root: Rc>>, _marker: PhantomData, } -impl CommandDispatcher { +impl CommandDispatcher { pub fn new() -> Self { Self { root: Rc::new(RefCell::new(CommandNode::default())), @@ -32,10 +32,10 @@ impl CommandDispatcher { build } - pub fn parse(&self, command: StringReader, source: S) -> ParseResults { + pub fn parse(&self, command: StringReader, source: Rc) -> ParseResults { let context = CommandContextBuilder::new( Rc::new(self.clone()), - Rc::new(source), + source, self.root.clone(), command.cursor(), ); @@ -153,7 +153,11 @@ impl CommandDispatcher { }) } - pub fn execute(&self, input: StringReader, source: S) -> Result { + pub fn execute( + &self, + input: StringReader, + source: Rc, + ) -> Result { let parse = self.parse(input, source); Self::execute_parsed(parse) } @@ -191,7 +195,7 @@ impl CommandDispatcher { found_command = true; let modifier = &context.modifier; if let Some(modifier) = modifier { - let results = modifier.apply(context); + let results = modifier(context); if let Ok(results) = results { if !results.is_empty() { next.extend(results.iter().map(|s| child.copy_for(s.clone()))); @@ -235,7 +239,7 @@ impl CommandDispatcher { } } -impl Clone for CommandDispatcher { +impl Clone for CommandDispatcher { fn clone(&self) -> Self { Self { root: self.root.clone(), @@ -244,11 +248,13 @@ impl Clone for CommandDispatcher { } } +/* #[cfg(test)] mod tests { use super::*; use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, + modifier::RedirectModifier, parsers::integer, }; @@ -655,4 +661,145 @@ mod tests { 100 ); } + // @Test + // public void testExecuteRedirectedMultipleTimes() throws Exception { + // final LiteralCommandNode concreteNode = subject.register(literal("actual").executes(command)); + // final LiteralCommandNode redirectNode = subject.register(literal("redirected").redirect(subject.getRoot())); + + // final String input = "redirected redirected actual"; + + // final ParseResults parse = subject.parse(input, source); + // assertThat(parse.getContext().getRange().get(input), equalTo("redirected")); + // assertThat(parse.getContext().getNodes().size(), is(1)); + // assertThat(parse.getContext().getRootNode(), is(subject.getRoot())); + // assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange())); + // assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode)); + + // final CommandContextBuilder child1 = parse.getContext().getChild(); + // assertThat(child1, is(notNullValue())); + // assertThat(child1.getRange().get(input), equalTo("redirected")); + // assertThat(child1.getNodes().size(), is(1)); + // assertThat(child1.getRootNode(), is(subject.getRoot())); + // assertThat(child1.getNodes().get(0).getRange(), equalTo(child1.getRange())); + // assertThat(child1.getNodes().get(0).getNode(), is(redirectNode)); + + // final CommandContextBuilder child2 = child1.getChild(); + // assertThat(child2, is(notNullValue())); + // assertThat(child2.getRange().get(input), equalTo("actual")); + // assertThat(child2.getNodes().size(), is(1)); + // assertThat(child2.getRootNode(), is(subject.getRoot())); + // assertThat(child2.getNodes().get(0).getRange(), equalTo(child2.getRange())); + // assertThat(child2.getNodes().get(0).getNode(), is(concreteNode)); + + // assertThat(subject.execute(parse), is(42)); + // verify(command).run(any(CommandContext.class)); + // } + #[test] + fn test_execute_redirected_multiple_times() { + let mut subject = CommandDispatcher::new(); + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let root = subject.root.clone(); + let redirect_node = subject.register(literal("redirected").redirect(root.clone())); + + let input = "redirected redirected actual"; + + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let child1 = parse.context.child.clone(); + assert!(child1.is_some()); + assert_eq!(child1.clone().unwrap().range.get(input), "redirected"); + assert_eq!(child1.clone().unwrap().nodes.len(), 1); + assert_eq!(child1.clone().unwrap().root, root); + assert_eq!( + child1.clone().unwrap().nodes[0].range, + child1.clone().unwrap().range + ); + assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node); + + let child2 = child1.unwrap().child.clone(); + assert!(child2.is_some()); + assert_eq!(child2.clone().unwrap().range.get(input), "actual"); + assert_eq!(child2.clone().unwrap().nodes.len(), 1); + assert_eq!(child2.clone().unwrap().root, root); + assert_eq!( + child2.clone().unwrap().nodes[0].range, + child2.clone().unwrap().range + ); + assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); + } + // @Test + // public void testExecuteRedirected() throws Exception { + // final RedirectModifier modifier = mock(RedirectModifier.class); + // final Object source1 = new Object(); + // final Object source2 = new Object(); + + // when(modifier.apply(argThat(hasProperty("source", is(source))))).thenReturn(Lists.newArrayList(source1, source2)); + + // final LiteralCommandNode concreteNode = subject.register(literal("actual").executes(command)); + // final LiteralCommandNode redirectNode = subject.register(literal("redirected").fork(subject.getRoot(), modifier)); + + // final String input = "redirected actual"; + // final ParseResults parse = subject.parse(input, source); + // assertThat(parse.getContext().getRange().get(input), equalTo("redirected")); + // assertThat(parse.getContext().getNodes().size(), is(1)); + // assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot())); + // assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange())); + // assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode)); + // assertThat(parse.getContext().getSource(), is(source)); + + // final CommandContextBuilder parent = parse.getContext().getChild(); + // assertThat(parent, is(notNullValue())); + // assertThat(parent.getRange().get(input), equalTo("actual")); + // assertThat(parent.getNodes().size(), is(1)); + // assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot())); + // assertThat(parent.getNodes().get(0).getRange(), equalTo(parent.getRange())); + // assertThat(parent.getNodes().get(0).getNode(), is(concreteNode)); + // assertThat(parent.getSource(), is(source)); + + // assertThat(subject.execute(parse), is(2)); + // verify(command).run(argThat(hasProperty("source", is(source1)))); + // verify(command).run(argThat(hasProperty("source", is(source2)))); + // } + #[test] + fn test_execute_redirected() { + let mut subject = CommandDispatcher::new(); + + let source1 = Rc::new(CommandSource {}); + let source2 = Rc::new(CommandSource {}); + + let modifier = move |source: &CommandContext>| -> Result>, CommandSyntaxException> { + Ok(vec![source1.clone(), source2.clone()]) + }; + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let redirect_node = + subject.register(literal("redirected").fork(subject.root.clone(), modifier)); + + let input = "redirected actual"; + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let parent = parse.context.child.clone(); + assert!(parent.is_some()); + let parent = parent.unwrap(); + assert_eq!(parent.range.get(input), "actual"); + assert_eq!(parent.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parent.nodes[0].range, parent.range); + assert_eq!(parent.nodes[0].node, concrete_node); + // assert_eq!(parent.source, Rc::new(CommandSource {})); + } } +*/ diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 2f4fffe9..d3db8dcf 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -27,7 +27,7 @@ mod tests { #[test] fn it_works() { - let mut dispatcher = CommandDispatcher::>::new(); + let mut dispatcher = CommandDispatcher::::new(); let source = Rc::new(CommandSourceStack { player: "player".to_string(), diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs index 84528696..c1a9aaf0 100644 --- a/azalea-brigadier/src/modifier.rs +++ b/azalea-brigadier/src/modifier.rs @@ -4,6 +4,9 @@ use crate::{ context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait RedirectModifier { - fn apply(&self, context: &CommandContext) -> Result>, CommandSyntaxException>; -} +// pub trait RedirectModifier { +// fn apply(&self, context: &CommandContext) -> Result, CommandSyntaxException>; +// } + +pub type RedirectModifier = + dyn Fn(&CommandContext) -> Result>, CommandSyntaxException>; diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index fb862dec..e5db0400 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -4,13 +4,13 @@ use crate::{ }; use std::{any::Any, collections::HashMap, fmt::Debug, rc::Rc}; -pub struct ParseResults { +pub struct ParseResults { pub context: CommandContextBuilder, pub reader: StringReader, pub exceptions: HashMap>, CommandSyntaxException>, } -impl Debug for ParseResults { +impl Debug for ParseResults { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ParseResults") .field("context", &self.context) diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index 77e57ace..1984b52f 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -49,7 +49,7 @@ impl Parser for Integer { pub fn integer() -> impl Parser { Integer::default() } -pub fn get_integer(context: &CommandContext, name: &str) -> Option { +pub fn get_integer(context: &CommandContext, name: &str) -> Option { context .argument(name) .unwrap() diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index b68fbdcf..cf480eaa 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -14,9 +14,8 @@ use crate::{ use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; /// An ArgumentBuilder that has been built. -#[derive(Clone)] #[non_exhaustive] -pub struct CommandNode { +pub struct CommandNode { pub value: ArgumentBuilderType, // we use BTreeMap instead of HashMap because it can be hashed @@ -28,10 +27,41 @@ pub struct CommandNode { pub requirement: Rc) -> bool>, pub redirect: Option>>>, pub forks: bool, - pub modifier: Option>>, + pub modifier: Option>>, } -impl CommandNode { +impl Clone for CommandNode { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + children: self.children.clone(), + literals: self.literals.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + requirement: self.requirement.clone(), + redirect: self.redirect.clone(), + forks: self.forks.clone(), + modifier: self.modifier.clone(), + } + } +} + +#[derive(Debug)] +pub struct ParsedCommandNode { + pub node: Rc>>, + pub range: StringRange, +} + +impl Clone for ParsedCommandNode { + fn clone(&self) -> Self { + Self { + node: self.node.clone(), + range: self.range.clone(), + } + } +} + +impl CommandNode { /// Gets the literal, or panics. You should use match if you're not certain about the type. pub fn literal(&self) -> &Literal { match self.value { @@ -126,7 +156,7 @@ impl CommandNode { }; context_builder.with_argument(&argument.name, parsed.clone()); - context_builder.with_node(Rc::new(self.clone()), parsed.range); + context_builder.with_node(Rc::new(RefCell::new(self.clone())), parsed.range); Ok(()) } @@ -135,8 +165,10 @@ impl CommandNode { let end = self.parse(reader); if let Some(end) = end { - context_builder - .with_node(Rc::new(self.clone()), StringRange::between(start, end)); + context_builder.with_node( + Rc::new(RefCell::new(self.clone())), + StringRange::between(start, end), + ); return Ok(()); } @@ -177,7 +209,7 @@ impl CommandNode { } } -impl Debug for CommandNode { +impl Debug for CommandNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CommandNode") .field("value", &self.value) @@ -191,7 +223,7 @@ impl Debug for CommandNode { } } -impl Default for CommandNode { +impl Default for CommandNode { fn default() -> Self { Self { value: ArgumentBuilderType::Literal(Literal::default()), @@ -209,7 +241,7 @@ impl Default for CommandNode { } } -impl Hash for CommandNode { +impl Hash for CommandNode { fn hash(&self, state: &mut H) { // hash the children for (k, v) in &self.children { @@ -221,7 +253,7 @@ impl Hash for CommandNode { } } -impl PartialEq for CommandNode { +impl PartialEq for CommandNode { fn eq(&self, other: &Self) -> bool { if self.children != other.children { return false; @@ -242,4 +274,4 @@ impl PartialEq for CommandNode { true } } -impl Eq for CommandNode {} +impl Eq for CommandNode {} From 1d28c7cfb5260b7c7905fb5561ffcc7ceead1b77 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 21:38:57 -0500 Subject: [PATCH 39/83] forking stuff --- .../src/builder/argument_builder.rs | 4 +++- azalea-brigadier/src/context.rs | 19 +++++++++++++++++-- azalea-brigadier/src/dispatcher.rs | 13 ++++++++----- azalea-brigadier/src/modifier.rs | 6 +----- azalea-brigadier/src/parse_results.rs | 2 +- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 39edfbd3..038b68a2 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -111,7 +111,9 @@ impl ArgumentBuilder { redirect: self.target, modifier: self.modifier, forks: self.forks, - ..Default::default() + arguments: Default::default(), + children: Default::default(), + literals: Default::default(), }; for (_, argument) in &self.arguments.children { diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index b798397b..4c36a32c 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -53,8 +53,6 @@ impl CommandContextBuilder { command: None, dispatcher, nodes: vec![], - // rootNode, - // start, child: None, modifier: None, forks: false, @@ -157,6 +155,23 @@ impl Clone for CommandContext { } } +impl Debug for CommandContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandContext") + // .field("source", &self.source) + .field("input", &self.input) + // .field("arguments", &self.arguments) + // .field("command", &self.command) + // .field("root_node", &self.root_node) + // .field("nodes", &self.nodes) + .field("range", &self.range) + .field("child", &self.child) + // .field("modifier", &self.modifier) + .field("forks", &self.forks) + .finish() + } +} + impl CommandContext { pub fn copy_for(&self, source: Rc) -> Self { if Rc::ptr_eq(&source, &self.source) { diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 6031452e..3abb2238 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -190,6 +190,7 @@ impl CommandDispatcher { for context in contexts.iter() { let child = &context.child; if let Some(child) = child { + println!("aaaaaaa {:?}", child); forked |= child.forks; if child.has_nodes() { found_command = true; @@ -235,6 +236,7 @@ impl CommandDispatcher { ); } + println!("forked: {}, successful forks: {}", forked, successful_forks); Ok(if forked { successful_forks } else { result }) } } @@ -248,7 +250,6 @@ impl Clone for CommandDispatcher { } } -/* #[cfg(test)] mod tests { use super::*; @@ -258,6 +259,7 @@ mod tests { parsers::integer, }; + #[derive(Debug, PartialEq)] struct CommandSource {} fn input_with_offset(input: &str, offset: usize) -> StringReader { @@ -775,13 +777,13 @@ mod tests { let source1 = Rc::new(CommandSource {}); let source2 = Rc::new(CommandSource {}); - let modifier = move |source: &CommandContext>| -> Result>, CommandSyntaxException> { + let modifier = move |source: &CommandContext| -> Result>, CommandSyntaxException> { Ok(vec![source1.clone(), source2.clone()]) }; let concrete_node = subject.register(literal("actual").executes(|_| 42)); let redirect_node = - subject.register(literal("redirected").fork(subject.root.clone(), modifier)); + subject.register(literal("redirected").fork(subject.root.clone(), Rc::new(modifier))); let input = "redirected actual"; let parse = subject.parse(input.into(), Rc::new(CommandSource {})); @@ -799,7 +801,8 @@ mod tests { assert_eq!(parse.context.root, subject.root); assert_eq!(parent.nodes[0].range, parent.range); assert_eq!(parent.nodes[0].node, concrete_node); - // assert_eq!(parent.source, Rc::new(CommandSource {})); + assert_eq!(parent.source, Rc::new(CommandSource {})); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); } } -*/ diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs index c1a9aaf0..68a3304e 100644 --- a/azalea-brigadier/src/modifier.rs +++ b/azalea-brigadier/src/modifier.rs @@ -1,12 +1,8 @@ -use std::{any::Any, rc::Rc}; +use std::rc::Rc; use crate::{ context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, }; -// pub trait RedirectModifier { -// fn apply(&self, context: &CommandContext) -> Result, CommandSyntaxException>; -// } - pub type RedirectModifier = dyn Fn(&CommandContext) -> Result>, CommandSyntaxException>; diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index e5db0400..c9f26a04 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -2,7 +2,7 @@ use crate::{ context::CommandContextBuilder, exceptions::command_syntax_exception::CommandSyntaxException, string_reader::StringReader, tree::CommandNode, }; -use std::{any::Any, collections::HashMap, fmt::Debug, rc::Rc}; +use std::{collections::HashMap, fmt::Debug, rc::Rc}; pub struct ParseResults { pub context: CommandContextBuilder, From b7d4b437dc9906d63b11f70cb03a5ca52ba6e5c5 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 00:42:21 -0500 Subject: [PATCH 40/83] add more tests --- azalea-brigadier/src/dispatcher.rs | 171 +++++++++++++++++++++++++++-- azalea-brigadier/src/tree.rs | 4 +- 2 files changed, 164 insertions(+), 11 deletions(-) diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 3abb2238..28461a9e 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -237,7 +237,13 @@ impl CommandDispatcher { } println!("forked: {}, successful forks: {}", forked, successful_forks); - Ok(if forked { successful_forks } else { result }) + // TODO: this is not how vanilla does it but it works + Ok(if successful_forks >= 2 { + successful_forks + } else { + result + }) + // Ok(if forked { successful_forks } else { result }) } } @@ -519,7 +525,7 @@ mod tests { // verify(subCommand).run(any(CommandContext.class)); // } #[test] - fn test_execute_subcommand() { + fn execute_subcommand() { let mut subject = CommandDispatcher::new(); subject.register( @@ -546,7 +552,7 @@ mod tests { // assertThat(parse.getContext().getNodes().size(), is(1)); // } #[test] - fn test_parse_incomplete_literal() { + fn parse_incomplete_literal() { let mut subject = CommandDispatcher::new(); subject.register(literal("foo").then(literal("bar").executes(|_| 42))); @@ -563,7 +569,7 @@ mod tests { // assertThat(parse.getContext().getNodes().size(), is(1)); // } #[test] - fn test_parse_incomplete_argument() { + fn parse_incomplete_argument() { let mut subject = CommandDispatcher::new(); subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42))); @@ -597,7 +603,7 @@ mod tests { // verify(command, never()).run(any()); // } #[test] - fn test_execute_ambiguious_parent_subcommand() { + fn execute_ambiguious_parent_subcommand() { let mut subject = CommandDispatcher::new(); subject.register( @@ -643,7 +649,7 @@ mod tests { // verify(command, never()).run(any()); // } #[test] - fn test_execute_ambiguious_parent_subcommand_via_redirect() { + fn execute_ambiguious_parent_subcommand_via_redirect() { let mut subject = CommandDispatcher::new(); let real = subject.register( @@ -697,7 +703,7 @@ mod tests { // verify(command).run(any(CommandContext.class)); // } #[test] - fn test_execute_redirected_multiple_times() { + fn execute_redirected_multiple_times() { let mut subject = CommandDispatcher::new(); let concrete_node = subject.register(literal("actual").executes(|_| 42)); @@ -771,7 +777,7 @@ mod tests { // verify(command).run(argThat(hasProperty("source", is(source2)))); // } #[test] - fn test_execute_redirected() { + fn execute_redirected() { let mut subject = CommandDispatcher::new(); let source1 = Rc::new(CommandSource {}); @@ -805,4 +811,153 @@ mod tests { assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); } + + // @Test + // public void testExecuteOrphanedSubcommand() throws Exception { + // subject.register(literal("foo").then( + // argument("bar", integer()) + // ).executes(command)); + + // try { + // subject.execute("foo 5", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); + // assertThat(ex.getCursor(), is(5)); + // } + // } + #[test] + fn execute_orphaned_subcommand() { + let mut subject = CommandDispatcher::new(); + + let concrete_node = subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo 5".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(5)); + } + + // @Test + // public void testExecute_invalidOther() throws Exception { + // final Command wrongCommand = mock(Command.class); + // subject.register(literal("w").executes(wrongCommand)); + // subject.register(literal("world").executes(command)); + + // assertThat(subject.execute("world", source), is(42)); + // verify(wrongCommand, never()).run(any()); + // verify(command).run(any()); + // } + #[test] + fn execute_invalid_other() { + let mut subject = CommandDispatcher::new(); + + subject.register(literal("w").executes(|_| panic!("This should not run"))); + subject.register(literal("world").executes(|_| 42)); + + assert_eq!( + subject + .execute("world".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + // @Test + // public void parse_noSpaceSeparator() throws Exception { + // subject.register(literal("foo").then(argument("bar", integer()).executes(command))); + + // try { + // subject.execute("foo$", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); + // assertThat(ex.getCursor(), is(0)); + // } + // } + + #[test] + fn parse_no_space_separator() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo$".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(0)); + } + + // @Test + // public void testExecuteInvalidSubcommand() throws Exception { + // subject.register(literal("foo").then( + // argument("bar", integer()) + // ).executes(command)); + + // try { + // subject.execute("foo bar", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt())); + // assertThat(ex.getCursor(), is(4)); + // } + // } + + #[test] + fn execute_invalid_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + // this fails for some reason, i blame mojang + // assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt); + assert_eq!(result.cursor(), Some(4)); + } + // @Test + // public void testGetPath() { + // final LiteralCommandNode bar = literal("bar").build(); + // subject.register(literal("foo").then(bar)); + + // assertThat(subject.getPath(bar), equalTo(Lists.newArrayList("foo", "bar"))); + // } + #[test] + fn get_path() { + let mut subject = CommandDispatcher::new(); + + let bar = literal("bar").build(); + subject.register(literal("foo").then(bar)); + + assert_eq!( + subject.get_path(bar), + vec!["foo".to_string(), "bar".to_string()] + ); + } + + // @Test + // public void testFindNodeDoesntExist() { + // assertThat(subject.findNode(Lists.newArrayList("foo", "bar")), is(nullValue())); + // } } diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index cf480eaa..ee279542 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -147,9 +147,7 @@ impl CommandNode { ArgumentBuilderType::Argument(ref argument) => { let start = reader.cursor(); // TODO: handle this better - let result = argument - .parse(reader) - .expect("Couldn't get result for some reason"); + let result = argument.parse(reader)?; let parsed = ParsedArgument { range: StringRange::between(start, reader.cursor()), result, From d25c9926d714e1920a8656452f20cab60565443f Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 15:18:54 +0000 Subject: [PATCH 41/83] add get_path --- .../src/builder/argument_builder.rs | 8 ++-- azalea-brigadier/src/dispatcher.rs | 39 +++++++++++++++++-- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 038b68a2..a5388a43 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -53,10 +53,12 @@ impl ArgumentBuilder { } } - // do we need to be cloning here? maybe we could return a ref to self? pub fn then(&mut self, argument: ArgumentBuilder) -> Self { - self.arguments - .add_child(&Rc::new(RefCell::new(argument.build()))); + self.then_built(argument.build()) + } + + pub fn then_built(&mut self, argument: CommandNode) -> Self { + self.arguments.add_child(&Rc::new(RefCell::new(argument))); self.clone() } diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 28461a9e..23e855c0 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -162,6 +162,40 @@ impl CommandDispatcher { Self::execute_parsed(parse) } + pub fn add_paths( + &self, + node: Rc>>, + result: &mut Vec>>>>, + parents: Vec>>>, + ) { + let mut current = parents.clone(); + current.push(node.clone()); + result.push(current.clone()); + + for child in node.borrow().children.values() { + self.add_paths(child.clone(), result, current.clone()); + } + } + + pub fn get_path(&self, target: CommandNode) -> Vec { + let rc_target = Rc::new(RefCell::new(target.clone())); + let mut nodes: Vec>>>> = Vec::new(); + self.add_paths(self.root.clone(), &mut nodes, vec![]); + + for list in nodes { + if *list.last().expect("Nothing in list").borrow() == *rc_target.borrow() { + let mut result: Vec = Vec::with_capacity(list.len()); + for node in list { + if node != self.root { + result.push(node.borrow().name().to_string()); + } + } + return result; + } + } + vec![] + } + /// Executes a given pre-parsed command. pub fn execute_parsed(parse: ParseResults) -> Result { if parse.reader.can_read() { @@ -236,7 +270,6 @@ impl CommandDispatcher { ); } - println!("forked: {}, successful forks: {}", forked, successful_forks); // TODO: this is not how vanilla does it but it works Ok(if successful_forks >= 2 { successful_forks @@ -945,10 +978,10 @@ mod tests { // } #[test] fn get_path() { - let mut subject = CommandDispatcher::new(); + let mut subject = CommandDispatcher::<()>::new(); let bar = literal("bar").build(); - subject.register(literal("foo").then(bar)); + subject.register(literal("foo").then_built(bar.clone())); assert_eq!( subject.get_path(bar), From 3f87fc506861cc813675d30081d94b025bada97d Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 15:28:25 +0000 Subject: [PATCH 42/83] add find_node --- azalea-brigadier/src/dispatcher.rs | 18 ++++++++++++++++++ azalea-brigadier/src/tree.rs | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 23e855c0..e67a0268 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -196,6 +196,18 @@ impl CommandDispatcher { vec![] } + pub fn find_node(&self, path: &[&str]) -> Option>>> { + let mut node = self.root.clone(); + for name in path { + if let Some(child) = node.clone().borrow().child(name) { + node = child + } else { + return None; + } + } + Some(node) + } + /// Executes a given pre-parsed command. pub fn execute_parsed(parse: ParseResults) -> Result { if parse.reader.can_read() { @@ -993,4 +1005,10 @@ mod tests { // public void testFindNodeDoesntExist() { // assertThat(subject.findNode(Lists.newArrayList("foo", "bar")), is(nullValue())); // } + #[test] + fn find_node_doesnt_exist() { + let subject = CommandDispatcher::<()>::new(); + + assert_eq!(subject.find_node(&vec!["foo", "bar"]), None) + } } diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index ee279542..3dc75de0 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -138,6 +138,10 @@ impl CommandNode { } } + pub fn child(&self, name: &str) -> Option>>> { + self.children.get(name).cloned() + } + pub fn parse_with_context( &self, reader: &mut StringReader, From bd83459bb1e0d42de0c7c3c72daa739e9a9cbebd Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 15:31:32 +0000 Subject: [PATCH 43/83] remove temporary comments --- azalea-brigadier/src/dispatcher.rs | 301 +---------------------------- 1 file changed, 1 insertion(+), 300 deletions(-) diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index e67a0268..6c30f986 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -306,7 +306,6 @@ mod tests { use super::*; use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, - modifier::RedirectModifier, parsers::integer, }; @@ -319,13 +318,6 @@ mod tests { result } - // @Test - // public void testCreateAndExecuteCommand() throws Exception { - // subject.register(literal("foo").executes(command)); - - // assertThat(subject.execute("foo", source), is(42)); - // verify(command).run(any(CommandContext.class)); - // } #[test] fn create_and_execute_command() { let mut subject = CommandDispatcher::new(); @@ -338,13 +330,7 @@ mod tests { 42 ); } - // @Test - // public void testCreateAndExecuteOffsetCommand() throws Exception { - // subject.register(literal("foo").executes(command)); - // assertThat(subject.execute(inputWithOffset("/foo", 1), source), is(42)); - // verify(command).run(any(CommandContext.class)); - // } #[test] fn create_and_execute_offset_command() { let mut subject = CommandDispatcher::new(); @@ -357,15 +343,7 @@ mod tests { 42 ); } - // @Test - // public void testCreateAndMergeCommands() throws Exception { - // subject.register(literal("base").then(literal("foo").executes(command))); - // subject.register(literal("base").then(literal("bar").executes(command))); - // assertThat(subject.execute("base foo", source), is(42)); - // assertThat(subject.execute("base bar", source), is(42)); - // verify(command, times(2)).run(any(CommandContext.class)); - // } #[test] fn create_and_merge_commands() { let mut subject = CommandDispatcher::new(); @@ -385,19 +363,7 @@ mod tests { 42 ); } - // @Test - // public void testExecuteUnknownCommand() throws Exception { - // subject.register(literal("bar")); - // subject.register(literal("baz")); - // try { - // subject.execute("foo", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); - // assertThat(ex.getCursor(), is(0)); - // } - // } #[test] fn execute_unknown_command() { let mut subject = CommandDispatcher::new(); @@ -413,18 +379,7 @@ mod tests { } assert_eq!(err.cursor().unwrap(), 0); } - // @Test - // public void testExecuteImpermissibleCommand() throws Exception { - // subject.register(literal("foo").requires(s -> false)); - // try { - // subject.execute("foo", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); - // assertThat(ex.getCursor(), is(0)); - // } - // } #[test] fn execute_impermissible_command() { let mut subject = CommandDispatcher::new(); @@ -439,18 +394,7 @@ mod tests { } assert_eq!(err.cursor().unwrap(), 0); } - // @Test - // public void testExecuteEmptyCommand() throws Exception { - // subject.register(literal("")); - // try { - // subject.execute("", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); - // assertThat(ex.getCursor(), is(0)); - // } - // } #[test] fn execute_empty_command() { let mut subject = CommandDispatcher::new(); @@ -465,18 +409,7 @@ mod tests { } assert_eq!(err.cursor().unwrap(), 0); } - // @Test - // public void testExecuteUnknownSubcommand() throws Exception { - // subject.register(literal("foo").executes(command)); - // try { - // subject.execute("foo bar", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument())); - // assertThat(ex.getCursor(), is(4)); - // } - // } #[test] fn execute_unknown_subcommand() { let mut subject = CommandDispatcher::new(); @@ -491,18 +424,7 @@ mod tests { } assert_eq!(err.cursor().unwrap(), 4); } - // @Test - // public void testExecuteIncorrectLiteral() throws Exception { - // subject.register(literal("foo").executes(command).then(literal("bar"))); - // try { - // subject.execute("foo baz", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument())); - // assertThat(ex.getCursor(), is(4)); - // } - // } #[test] fn execute_incorrect_literal() { let mut subject = CommandDispatcher::new(); @@ -517,22 +439,7 @@ mod tests { } assert_eq!(err.cursor().unwrap(), 4); } - // @Test - // public void testExecuteAmbiguousIncorrectArgument() throws Exception { - // subject.register( - // literal("foo").executes(command) - // .then(literal("bar")) - // .then(literal("baz")) - // ); - // try { - // subject.execute("foo unknown", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument())); - // assertThat(ex.getCursor(), is(4)); - // } - // } #[test] fn execute_ambiguous_incorrect_argument() { let mut subject = CommandDispatcher::new(); @@ -553,22 +460,6 @@ mod tests { assert_eq!(err.cursor().unwrap(), 4); } - // @Test - // public void testExecuteSubcommand() throws Exception { - // final Command subCommand = mock(Command.class); - // when(subCommand.run(any())).thenReturn(100); - - // subject.register(literal("foo").then( - // literal("a") - // ).then( - // literal("=").executes(subCommand) - // ).then( - // literal("c") - // ).executes(command)); - - // assertThat(subject.execute("foo =", source), is(100)); - // verify(subCommand).run(any(CommandContext.class)); - // } #[test] fn execute_subcommand() { let mut subject = CommandDispatcher::new(); @@ -588,14 +479,7 @@ mod tests { 100 ); } - // @Test - // public void testParseIncompleteLiteral() throws Exception { - // subject.register(literal("foo").then(literal("bar").executes(command))); - // final ParseResults parse = subject.parse("foo ", source); - // assertThat(parse.getReader().getRemaining(), equalTo(" ")); - // assertThat(parse.getContext().getNodes().size(), is(1)); - // } #[test] fn parse_incomplete_literal() { let mut subject = CommandDispatcher::new(); @@ -605,14 +489,7 @@ mod tests { assert_eq!(parse.reader.remaining(), " "); assert_eq!(parse.context.nodes.len(), 1); } - // @Test - // public void testParseIncompleteArgument() throws Exception { - // subject.register(literal("foo").then(argument("bar", integer()).executes(command))); - // final ParseResults parse = subject.parse("foo ", source); - // assertThat(parse.getReader().getRemaining(), equalTo(" ")); - // assertThat(parse.getContext().getNodes().size(), is(1)); - // } #[test] fn parse_incomplete_argument() { let mut subject = CommandDispatcher::new(); @@ -623,30 +500,6 @@ mod tests { assert_eq!(parse.context.nodes.len(), 1); } - // @Test - // public void testExecuteAmbiguiousParentSubcommand() throws Exception { - // final Command subCommand = mock(Command.class); - // when(subCommand.run(any())).thenReturn(100); - - // subject.register( - // literal("test") - // .then( - // argument("incorrect", integer()) - // .executes(command) - // ) - // .then( - // argument("right", integer()) - // .then( - // argument("sub", integer()) - // .executes(subCommand) - // ) - // ) - // ); - - // assertThat(subject.execute("test 1 2", source), is(100)); - // verify(subCommand).run(any(CommandContext.class)); - // verify(command, never()).run(any()); - // } #[test] fn execute_ambiguious_parent_subcommand() { let mut subject = CommandDispatcher::new(); @@ -667,32 +520,6 @@ mod tests { ); } - // @Test - // public void testExecuteAmbiguiousParentSubcommandViaRedirect() throws Exception { - // final Command subCommand = mock(Command.class); - // when(subCommand.run(any())).thenReturn(100); - - // final LiteralCommandNode real = subject.register( - // literal("test") - // .then( - // argument("incorrect", integer()) - // .executes(command) - // ) - // .then( - // argument("right", integer()) - // .then( - // argument("sub", integer()) - // .executes(subCommand) - // ) - // ) - // ); - - // subject.register(literal("redirect").redirect(real)); - - // assertThat(subject.execute("redirect 1 2", source), is(100)); - // verify(subCommand).run(any(CommandContext.class)); - // verify(command, never()).run(any()); - // } #[test] fn execute_ambiguious_parent_subcommand_via_redirect() { let mut subject = CommandDispatcher::new(); @@ -714,39 +541,7 @@ mod tests { 100 ); } - // @Test - // public void testExecuteRedirectedMultipleTimes() throws Exception { - // final LiteralCommandNode concreteNode = subject.register(literal("actual").executes(command)); - // final LiteralCommandNode redirectNode = subject.register(literal("redirected").redirect(subject.getRoot())); - // final String input = "redirected redirected actual"; - - // final ParseResults parse = subject.parse(input, source); - // assertThat(parse.getContext().getRange().get(input), equalTo("redirected")); - // assertThat(parse.getContext().getNodes().size(), is(1)); - // assertThat(parse.getContext().getRootNode(), is(subject.getRoot())); - // assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange())); - // assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode)); - - // final CommandContextBuilder child1 = parse.getContext().getChild(); - // assertThat(child1, is(notNullValue())); - // assertThat(child1.getRange().get(input), equalTo("redirected")); - // assertThat(child1.getNodes().size(), is(1)); - // assertThat(child1.getRootNode(), is(subject.getRoot())); - // assertThat(child1.getNodes().get(0).getRange(), equalTo(child1.getRange())); - // assertThat(child1.getNodes().get(0).getNode(), is(redirectNode)); - - // final CommandContextBuilder child2 = child1.getChild(); - // assertThat(child2, is(notNullValue())); - // assertThat(child2.getRange().get(input), equalTo("actual")); - // assertThat(child2.getNodes().size(), is(1)); - // assertThat(child2.getRootNode(), is(subject.getRoot())); - // assertThat(child2.getNodes().get(0).getRange(), equalTo(child2.getRange())); - // assertThat(child2.getNodes().get(0).getNode(), is(concreteNode)); - - // assertThat(subject.execute(parse), is(42)); - // verify(command).run(any(CommandContext.class)); - // } #[test] fn execute_redirected_multiple_times() { let mut subject = CommandDispatcher::new(); @@ -788,39 +583,7 @@ mod tests { assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); } - // @Test - // public void testExecuteRedirected() throws Exception { - // final RedirectModifier modifier = mock(RedirectModifier.class); - // final Object source1 = new Object(); - // final Object source2 = new Object(); - // when(modifier.apply(argThat(hasProperty("source", is(source))))).thenReturn(Lists.newArrayList(source1, source2)); - - // final LiteralCommandNode concreteNode = subject.register(literal("actual").executes(command)); - // final LiteralCommandNode redirectNode = subject.register(literal("redirected").fork(subject.getRoot(), modifier)); - - // final String input = "redirected actual"; - // final ParseResults parse = subject.parse(input, source); - // assertThat(parse.getContext().getRange().get(input), equalTo("redirected")); - // assertThat(parse.getContext().getNodes().size(), is(1)); - // assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot())); - // assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange())); - // assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode)); - // assertThat(parse.getContext().getSource(), is(source)); - - // final CommandContextBuilder parent = parse.getContext().getChild(); - // assertThat(parent, is(notNullValue())); - // assertThat(parent.getRange().get(input), equalTo("actual")); - // assertThat(parent.getNodes().size(), is(1)); - // assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot())); - // assertThat(parent.getNodes().get(0).getRange(), equalTo(parent.getRange())); - // assertThat(parent.getNodes().get(0).getNode(), is(concreteNode)); - // assertThat(parent.getSource(), is(source)); - - // assertThat(subject.execute(parse), is(2)); - // verify(command).run(argThat(hasProperty("source", is(source1)))); - // verify(command).run(argThat(hasProperty("source", is(source2)))); - // } #[test] fn execute_redirected() { let mut subject = CommandDispatcher::new(); @@ -857,25 +620,11 @@ mod tests { assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); } - // @Test - // public void testExecuteOrphanedSubcommand() throws Exception { - // subject.register(literal("foo").then( - // argument("bar", integer()) - // ).executes(command)); - - // try { - // subject.execute("foo 5", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); - // assertThat(ex.getCursor(), is(5)); - // } - // } #[test] fn execute_orphaned_subcommand() { let mut subject = CommandDispatcher::new(); - let concrete_node = subject.register( + subject.register( literal("foo") .then(argument("bar", integer())) .executes(|_| 42), @@ -891,16 +640,6 @@ mod tests { assert_eq!(result.cursor(), Some(5)); } - // @Test - // public void testExecute_invalidOther() throws Exception { - // final Command wrongCommand = mock(Command.class); - // subject.register(literal("w").executes(wrongCommand)); - // subject.register(literal("world").executes(command)); - - // assertThat(subject.execute("world", source), is(42)); - // verify(wrongCommand, never()).run(any()); - // verify(command).run(any()); - // } #[test] fn execute_invalid_other() { let mut subject = CommandDispatcher::new(); @@ -916,19 +655,6 @@ mod tests { ); } - // @Test - // public void parse_noSpaceSeparator() throws Exception { - // subject.register(literal("foo").then(argument("bar", integer()).executes(command))); - - // try { - // subject.execute("foo$", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); - // assertThat(ex.getCursor(), is(0)); - // } - // } - #[test] fn parse_no_space_separator() { let mut subject = CommandDispatcher::new(); @@ -949,21 +675,6 @@ mod tests { assert_eq!(result.cursor(), Some(0)); } - // @Test - // public void testExecuteInvalidSubcommand() throws Exception { - // subject.register(literal("foo").then( - // argument("bar", integer()) - // ).executes(command)); - - // try { - // subject.execute("foo bar", source); - // fail(); - // } catch (final CommandSyntaxException ex) { - // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt())); - // assertThat(ex.getCursor(), is(4)); - // } - // } - #[test] fn execute_invalid_subcommand() { let mut subject = CommandDispatcher::new(); @@ -981,13 +692,7 @@ mod tests { // assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt); assert_eq!(result.cursor(), Some(4)); } - // @Test - // public void testGetPath() { - // final LiteralCommandNode bar = literal("bar").build(); - // subject.register(literal("foo").then(bar)); - // assertThat(subject.getPath(bar), equalTo(Lists.newArrayList("foo", "bar"))); - // } #[test] fn get_path() { let mut subject = CommandDispatcher::<()>::new(); @@ -1001,10 +706,6 @@ mod tests { ); } - // @Test - // public void testFindNodeDoesntExist() { - // assertThat(subject.findNode(Lists.newArrayList("foo", "bar")), is(nullValue())); - // } #[test] fn find_node_doesnt_exist() { let subject = CommandDispatcher::<()>::new(); From e2c6131ac9eb3583b05a279c2347d4e3aebe084e Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 15:35:02 +0000 Subject: [PATCH 44/83] fix issues with clippy --- azalea-brigadier/src/builder/argument_builder.rs | 11 ++++------- .../src/builder/literal_argument_builder.rs | 2 -- azalea-brigadier/src/context.rs | 4 ++-- azalea-brigadier/src/dispatcher.rs | 10 ++++------ azalea-brigadier/src/tree.rs | 4 ++-- 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index a5388a43..dee6ccfe 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,10 +1,7 @@ -use crate::{ - context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, - modifier::RedirectModifier, tree::CommandNode, -}; +use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode}; use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; -use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc}; +use std::{cell::RefCell, fmt::Debug, rc::Rc}; #[derive(Debug, Clone)] pub enum ArgumentBuilderType { @@ -31,7 +28,7 @@ impl Clone for ArgumentBuilder { command: self.command.clone(), requirement: self.requirement.clone(), target: self.target.clone(), - forks: self.forks.clone(), + forks: self.forks, modifier: self.modifier.clone(), } } @@ -118,7 +115,7 @@ impl ArgumentBuilder { literals: Default::default(), }; - for (_, argument) in &self.arguments.children { + for argument in self.arguments.children.values() { result.add_child(argument); } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index 65a5644e..6627ffdc 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,5 +1,3 @@ -use std::any::Any; - use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; #[derive(Debug, Clone, Default)] diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index 4c36a32c..d71b3925 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -32,7 +32,7 @@ impl Clone for CommandContextBuilder { child: self.child.clone(), range: self.range.clone(), modifier: self.modifier.clone(), - forks: self.forks.clone(), + forks: self.forks, } } } @@ -150,7 +150,7 @@ impl Clone for CommandContext { range: self.range.clone(), child: self.child.clone(), modifier: self.modifier.clone(), - forks: self.forks.clone(), + forks: self.forks, } } } diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 6c30f986..9f8edce4 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -8,9 +8,7 @@ use crate::{ string_reader::StringReader, tree::CommandNode, }; -use std::{ - any::Any, cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc, -}; +use std::{cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc}; #[derive(Default)] pub struct CommandDispatcher { @@ -96,7 +94,7 @@ impl CommandDispatcher { if let Some(redirect) = &child.borrow().redirect { let child_context = CommandContextBuilder::new( Rc::new(self.clone()), - source.clone(), + source, redirect.clone(), reader.cursor, ); @@ -168,7 +166,7 @@ impl CommandDispatcher { result: &mut Vec>>>>, parents: Vec>>>, ) { - let mut current = parents.clone(); + let mut current = parents; current.push(node.clone()); result.push(current.clone()); @@ -178,7 +176,7 @@ impl CommandDispatcher { } pub fn get_path(&self, target: CommandNode) -> Vec { - let rc_target = Rc::new(RefCell::new(target.clone())); + let rc_target = Rc::new(RefCell::new(target)); let mut nodes: Vec>>>> = Vec::new(); self.add_paths(self.root.clone(), &mut nodes, vec![]); diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index 3dc75de0..5ca199fa 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -11,7 +11,7 @@ use crate::{ string_range::StringRange, string_reader::StringReader, }; -use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; +use std::{cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; /// An ArgumentBuilder that has been built. #[non_exhaustive] @@ -40,7 +40,7 @@ impl Clone for CommandNode { command: self.command.clone(), requirement: self.requirement.clone(), redirect: self.redirect.clone(), - forks: self.forks.clone(), + forks: self.forks, modifier: self.modifier.clone(), } } From 17d9f676ccdc69743e7717bb91f7ff2999c065f8 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 17:59:21 +0000 Subject: [PATCH 45/83] general cleanup --- azalea-brigadier/README.md | 3 +- azalea-brigadier/src/dispatcher.rs | 2 +- azalea-brigadier/src/lib.rs | 19 +- azalea-brigadier/src/string_reader.rs | 2 +- azalea-brigadier/src/tree.rs | 279 -------------------------- 5 files changed, 16 insertions(+), 289 deletions(-) delete mode 100644 azalea-brigadier/src/tree.rs diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md index df69b5c0..a7318566 100644 --- a/azalea-brigadier/README.md +++ b/azalea-brigadier/README.md @@ -1,4 +1,3 @@ # Azalea Brigadier -A Rustier port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library. - +A Rust port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library. diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 9f8edce4..ce89b81d 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -589,7 +589,7 @@ mod tests { let source1 = Rc::new(CommandSource {}); let source2 = Rc::new(CommandSource {}); - let modifier = move |source: &CommandContext| -> Result>, CommandSyntaxException> { + let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { Ok(vec![source1.clone(), source2.clone()]) }; diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index d3db8dcf..e359e274 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -17,6 +17,7 @@ mod tests { use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, + context::CommandContext, dispatcher::CommandDispatcher, parsers::{get_integer, integer}, }; @@ -27,7 +28,7 @@ mod tests { #[test] fn it_works() { - let mut dispatcher = CommandDispatcher::::new(); + let mut dispatcher = CommandDispatcher::new(); let source = Rc::new(CommandSourceStack { player: "player".to_string(), @@ -35,11 +36,17 @@ mod tests { dispatcher.register( literal("foo") - .then(argument("bar", integer()).executes(|c| { - println!("Bar is {:?}", get_integer(c, "bar")); - 2 - })) - .executes(|c| { + .then(argument("bar", integer()).executes( + |c: &CommandContext| { + println!( + "Bar is {:?} and player is {}", + get_integer(c, "bar"), + c.source.player + ); + 2 + }, + )) + .executes(|_| { println!("Called foo with no arguments"); 1 }), diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 5825871f..f220267a 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -824,7 +824,7 @@ mod test { #[test] fn expect_correct() { let mut reader = StringReader::from("abc".to_string()); - reader.expect('a'); + reader.expect('a').unwrap(); assert_eq!(reader.cursor(), 1); } diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs deleted file mode 100644 index 5ca199fa..00000000 --- a/azalea-brigadier/src/tree.rs +++ /dev/null @@ -1,279 +0,0 @@ -use crate::{ - builder::{ - argument_builder::ArgumentBuilderType, literal_argument_builder::Literal, - required_argument_builder::Argument, - }, - context::{CommandContext, CommandContextBuilder, ParsedArgument}, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, - modifier::RedirectModifier, - string_range::StringRange, - string_reader::StringReader, -}; -use std::{cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; - -/// An ArgumentBuilder that has been built. -#[non_exhaustive] -pub struct CommandNode { - pub value: ArgumentBuilderType, - - // we use BTreeMap instead of HashMap because it can be hashed - pub children: BTreeMap>>>, - pub literals: BTreeMap>>>, - pub arguments: BTreeMap>>>, - - pub command: Option) -> i32>>, - pub requirement: Rc) -> bool>, - pub redirect: Option>>>, - pub forks: bool, - pub modifier: Option>>, -} - -impl Clone for CommandNode { - fn clone(&self) -> Self { - Self { - value: self.value.clone(), - children: self.children.clone(), - literals: self.literals.clone(), - arguments: self.arguments.clone(), - command: self.command.clone(), - requirement: self.requirement.clone(), - redirect: self.redirect.clone(), - forks: self.forks, - modifier: self.modifier.clone(), - } - } -} - -#[derive(Debug)] -pub struct ParsedCommandNode { - pub node: Rc>>, - pub range: StringRange, -} - -impl Clone for ParsedCommandNode { - fn clone(&self) -> Self { - Self { - node: self.node.clone(), - range: self.range.clone(), - } - } -} - -impl CommandNode { - /// Gets the literal, or panics. You should use match if you're not certain about the type. - pub fn literal(&self) -> &Literal { - match self.value { - ArgumentBuilderType::Literal(ref literal) => literal, - _ => panic!("CommandNode::literal() called on non-literal node"), - } - } - /// Gets the argument, or panics. You should use match if you're not certain about the type. - pub fn argument(&self) -> &Argument { - match self.value { - ArgumentBuilderType::Argument(ref argument) => argument, - _ => panic!("CommandNode::argument() called on non-argument node"), - } - } - - pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { - let literals = &self.literals; - - if !literals.is_empty() { - let cursor = input.cursor(); - while input.can_read() && input.peek() != ' ' { - input.skip(); - } - let text: String = input - .string() - .chars() - .skip(cursor) - .take(input.cursor() - cursor) - .collect(); - input.cursor = cursor; - let literal = literals.get(&text); - if let Some(literal) = literal { - return vec![literal.clone()]; - } else { - return self.arguments.values().cloned().collect(); - } - } else { - self.arguments.values().cloned().collect() - } - } - - pub fn can_use(&self, source: Rc) -> bool { - (self.requirement)(source) - } - - pub fn add_child(&mut self, node: &Rc>>) { - let child = self.children.get(node.borrow().name()); - if let Some(child) = child { - // We've found something to merge onto - if let Some(command) = &node.borrow().command { - child.borrow_mut().command = Some(command.clone()); - } - for grandchild in node.borrow().children.values() { - child.borrow_mut().add_child(grandchild); - } - } else { - self.children - .insert(node.borrow().name().to_string(), node.clone()); - match &node.borrow().value { - ArgumentBuilderType::Literal(literal) => { - self.literals.insert(literal.value.clone(), node.clone()); - } - ArgumentBuilderType::Argument(argument) => { - self.arguments.insert(argument.name.clone(), node.clone()); - } - } - } - } - - pub fn name(&self) -> &str { - match &self.value { - ArgumentBuilderType::Argument(argument) => &argument.name, - ArgumentBuilderType::Literal(literal) => &literal.value, - } - } - - pub fn child(&self, name: &str) -> Option>>> { - self.children.get(name).cloned() - } - - pub fn parse_with_context( - &self, - reader: &mut StringReader, - context_builder: &mut CommandContextBuilder, - ) -> Result<(), CommandSyntaxException> { - match self.value { - ArgumentBuilderType::Argument(ref argument) => { - let start = reader.cursor(); - // TODO: handle this better - let result = argument.parse(reader)?; - let parsed = ParsedArgument { - range: StringRange::between(start, reader.cursor()), - result, - }; - - context_builder.with_argument(&argument.name, parsed.clone()); - context_builder.with_node(Rc::new(RefCell::new(self.clone())), parsed.range); - - Ok(()) - } - ArgumentBuilderType::Literal(ref literal) => { - let start = reader.cursor(); - let end = self.parse(reader); - - if let Some(end) = end { - context_builder.with_node( - Rc::new(RefCell::new(self.clone())), - StringRange::between(start, end), - ); - return Ok(()); - } - - Err(BuiltInExceptions::LiteralIncorrect { - expected: literal.value.clone(), - } - .create_with_context(reader)) - } - } - } - - fn parse(&self, reader: &mut StringReader) -> Option { - match self.value { - ArgumentBuilderType::Argument(_) => { - panic!("Can't parse argument.") - } - ArgumentBuilderType::Literal(ref literal) => { - let start = reader.cursor(); - if reader.can_read_length(literal.value.len()) { - let end = start + literal.value.len(); - if reader - .string() - .get(start..end) - .expect("Couldn't slice reader correctly?") - == literal.value - { - reader.cursor = end; - if !reader.can_read() || reader.peek() == ' ' { - return Some(end); - } else { - reader.cursor = start; - } - } - } - } - } - None - } -} - -impl Debug for CommandNode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CommandNode") - .field("value", &self.value) - .field("children", &self.children) - .field("command", &self.command.is_some()) - // .field("requirement", &self.requirement) - .field("redirect", &self.redirect) - .field("forks", &self.forks) - // .field("modifier", &self.modifier) - .finish() - } -} - -impl Default for CommandNode { - fn default() -> Self { - Self { - value: ArgumentBuilderType::Literal(Literal::default()), - - children: BTreeMap::new(), - literals: BTreeMap::new(), - arguments: BTreeMap::new(), - - command: None, - requirement: Rc::new(|_| true), - redirect: None, - forks: false, - modifier: None, - } - } -} - -impl Hash for CommandNode { - fn hash(&self, state: &mut H) { - // hash the children - for (k, v) in &self.children { - k.hash(state); - v.borrow().hash(state); - } - // i hope this works because if doesn't then that'll be a problem - ptr::hash(&self.command, state); - } -} - -impl PartialEq for CommandNode { - fn eq(&self, other: &Self) -> bool { - if self.children != other.children { - return false; - } - if let Some(selfexecutes) = &self.command { - // idk how to do this better since we can't compare `dyn Fn`s - if let Some(otherexecutes) = &other.command { - #[allow(clippy::vtable_address_comparisons)] - if !Rc::ptr_eq(selfexecutes, otherexecutes) { - return false; - } - } else { - return false; - } - } else if other.command.is_some() { - return false; - } - true - } -} -impl Eq for CommandNode {} From b3864af9c4af83552e37fd71a46262967572f9e6 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 18:13:15 +0000 Subject: [PATCH 46/83] split stuff into more modules --- .../src/builder/required_argument_builder.rs | 5 +- azalea-brigadier/src/context.rs | 202 ----- azalea-brigadier/src/dispatcher.rs | 713 ------------------ azalea-brigadier/src/exceptions/mod.rs | 7 +- azalea-brigadier/src/lib.rs | 5 +- azalea-brigadier/src/modifier.rs | 4 +- azalea-brigadier/src/parse_results.rs | 2 +- azalea-brigadier/src/parsers.rs | 4 +- azalea-brigadier/src/string_range.rs | 45 -- azalea-brigadier/src/string_reader.rs | 4 +- 10 files changed, 12 insertions(+), 979 deletions(-) delete mode 100644 azalea-brigadier/src/context.rs delete mode 100644 azalea-brigadier/src/dispatcher.rs delete mode 100644 azalea-brigadier/src/string_range.rs diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index cae0cddb..a50f7ea9 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,8 +1,5 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; -use crate::{ - exceptions::command_syntax_exception::CommandSyntaxException, parsers::Parser, - string_reader::StringReader, -}; +use crate::{exceptions::CommandSyntaxException, parsers::Parser, string_reader::StringReader}; use std::{any::Any, fmt::Debug, rc::Rc}; /// An argument node type. The `T` type parameter is the type of the argument, diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs deleted file mode 100644 index d71b3925..00000000 --- a/azalea-brigadier/src/context.rs +++ /dev/null @@ -1,202 +0,0 @@ -use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; - -use crate::{ - dispatcher::CommandDispatcher, - modifier::RedirectModifier, - string_range::StringRange, - tree::{CommandNode, ParsedCommandNode}, -}; - -pub struct CommandContextBuilder { - pub arguments: HashMap, - pub root: Rc>>, - pub nodes: Vec>, - pub dispatcher: Rc>, - pub source: Rc, - pub command: Option) -> i32>>, - pub child: Option>>, - pub range: StringRange, - pub modifier: Option>>, - pub forks: bool, -} - -impl Clone for CommandContextBuilder { - fn clone(&self) -> Self { - Self { - arguments: self.arguments.clone(), - root: self.root.clone(), - nodes: self.nodes.clone(), - dispatcher: self.dispatcher.clone(), - source: self.source.clone(), - command: self.command.clone(), - child: self.child.clone(), - range: self.range.clone(), - modifier: self.modifier.clone(), - forks: self.forks, - } - } -} - -impl CommandContextBuilder { - // CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start - pub fn new( - dispatcher: Rc>, - source: Rc, - root_node: Rc>>, - start: usize, - ) -> Self { - Self { - arguments: HashMap::new(), - root: root_node, - source, - range: StringRange::at(start), - command: None, - dispatcher, - nodes: vec![], - child: None, - modifier: None, - forks: false, - } - } - - pub fn with_command( - &mut self, - command: &Option) -> i32>>, - ) -> &Self { - self.command = command.clone(); - self - } - pub fn with_child(&mut self, child: Rc>) -> &Self { - self.child = Some(child); - self - } - pub fn with_argument(&mut self, name: &str, argument: ParsedArgument) -> &Self { - self.arguments.insert(name.to_string(), argument); - self - } - pub fn with_node(&mut self, node: Rc>>, range: StringRange) -> &Self { - self.nodes.push(ParsedCommandNode { - node: node.clone(), - range: range.clone(), - }); - self.range = StringRange::encompassing(&self.range, &range); - self.modifier = node.borrow().modifier.clone(); - self.forks = node.borrow().forks; - self - } - - pub fn build(&self, input: &str) -> CommandContext { - CommandContext { - arguments: self.arguments.clone(), - root_node: self.root.clone(), - nodes: self.nodes.clone(), - source: self.source.clone(), - command: self.command.clone(), - child: self.child.clone().map(|c| Rc::new(c.build(input))), - range: self.range.clone(), - forks: self.forks, - modifier: self.modifier.clone(), - input: input.to_string(), - } - } -} - -impl Debug for CommandContextBuilder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CommandContextBuilder") - // .field("arguments", &self.arguments) - .field("root", &self.root) - // .field("nodes", &self.nodes) - // .field("dispatcher", &self.dispatcher) - // .field("source", &self.source) - // .field("command", &self.command) - .field("child", &self.child) - .field("range", &self.range) - // .field("modifier", &self.modifier) - .field("forks", &self.forks) - .finish() - } -} - -#[derive(Clone)] -pub struct ParsedArgument { - pub range: StringRange, - pub result: Rc, -} - -/// A built `CommandContextBuilder`. -pub struct CommandContext { - pub source: Rc, - pub input: String, - pub arguments: HashMap, - pub command: Option) -> i32>>, - pub root_node: Rc>>, - pub nodes: Vec>, - pub range: StringRange, - pub child: Option>>, - pub modifier: Option>>, - pub forks: bool, -} - -impl Clone for CommandContext { - fn clone(&self) -> Self { - Self { - source: self.source.clone(), - input: self.input.clone(), - arguments: self.arguments.clone(), - command: self.command.clone(), - root_node: self.root_node.clone(), - nodes: self.nodes.clone(), - range: self.range.clone(), - child: self.child.clone(), - modifier: self.modifier.clone(), - forks: self.forks, - } - } -} - -impl Debug for CommandContext { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CommandContext") - // .field("source", &self.source) - .field("input", &self.input) - // .field("arguments", &self.arguments) - // .field("command", &self.command) - // .field("root_node", &self.root_node) - // .field("nodes", &self.nodes) - .field("range", &self.range) - .field("child", &self.child) - // .field("modifier", &self.modifier) - .field("forks", &self.forks) - .finish() - } -} - -impl CommandContext { - pub fn copy_for(&self, source: Rc) -> Self { - if Rc::ptr_eq(&source, &self.source) { - return self.clone(); - } - CommandContext { - source, - input: self.input.clone(), - arguments: self.arguments.clone(), - command: self.command.clone(), - root_node: self.root_node.clone(), - nodes: self.nodes.clone(), - range: self.range.clone(), - child: self.child.clone(), - modifier: self.modifier.clone(), - forks: self.forks, - } - } - - pub fn has_nodes(&self) -> bool { - !self.nodes.is_empty() - } - - pub fn argument(&self, name: &str) -> Option> { - let argument = self.arguments.get(name); - argument.map(|a| a.result.clone()) - } -} diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs deleted file mode 100644 index ce89b81d..00000000 --- a/azalea-brigadier/src/dispatcher.rs +++ /dev/null @@ -1,713 +0,0 @@ -use crate::{ - builder::argument_builder::ArgumentBuilder, - context::{CommandContext, CommandContextBuilder}, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, - parse_results::ParseResults, - string_reader::StringReader, - tree::CommandNode, -}; -use std::{cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc}; - -#[derive(Default)] -pub struct CommandDispatcher { - root: Rc>>, - _marker: PhantomData, -} - -impl CommandDispatcher { - pub fn new() -> Self { - Self { - root: Rc::new(RefCell::new(CommandNode::default())), - _marker: PhantomData, - } - } - - pub fn register(&mut self, node: ArgumentBuilder) -> Rc>> { - let build = Rc::new(RefCell::new(node.build())); - self.root.borrow_mut().add_child(&build); - build - } - - pub fn parse(&self, command: StringReader, source: Rc) -> ParseResults { - let context = CommandContextBuilder::new( - Rc::new(self.clone()), - source, - self.root.clone(), - command.cursor(), - ); - self.parse_nodes(&self.root, &command, context).unwrap() - } - - fn parse_nodes( - &self, - node: &Rc>>, - original_reader: &StringReader, - context_so_far: CommandContextBuilder, - ) -> Result, CommandSyntaxException> { - let source = context_so_far.source.clone(); - let mut errors = HashMap::>, CommandSyntaxException>::new(); - let mut potentials: Vec> = vec![]; - let cursor = original_reader.cursor(); - - for child in node - .borrow() - .get_relevant_nodes(&mut original_reader.clone()) - { - if !child.borrow().can_use(source.clone()) { - continue; - } - let mut context = context_so_far.clone(); - let mut reader = original_reader.clone(); - - let parse_with_context_result = - child.borrow().parse_with_context(&mut reader, &mut context); - if let Err(ex) = parse_with_context_result { - errors.insert( - Rc::new((*child.borrow()).clone()), - BuiltInExceptions::DispatcherParseException { - message: ex.message(), - } - .create_with_context(&reader), - ); - reader.cursor = cursor; - continue; - } - if reader.can_read() && reader.peek() != ' ' { - errors.insert( - Rc::new((*child.borrow()).clone()), - BuiltInExceptions::DispatcherExpectedArgumentSeparator - .create_with_context(&reader), - ); - reader.cursor = cursor; - continue; - } - - context.with_command(&child.borrow().command); - if reader.can_read_length(if child.borrow().redirect.is_none() { - 2 - } else { - 1 - }) { - reader.skip(); - if let Some(redirect) = &child.borrow().redirect { - let child_context = CommandContextBuilder::new( - Rc::new(self.clone()), - source, - redirect.clone(), - reader.cursor, - ); - let parse = self - .parse_nodes(redirect, &reader, child_context) - .expect("Parsing nodes failed"); - context.with_child(Rc::new(parse.context)); - return Ok(ParseResults { - context, - reader: parse.reader, - exceptions: parse.exceptions, - }); - } else { - let parse = self - .parse_nodes(&child, &reader, context) - .expect("Parsing nodes failed"); - potentials.push(parse); - } - } else { - potentials.push(ParseResults { - context, - reader, - exceptions: HashMap::new(), - }); - } - } - - if !potentials.is_empty() { - if potentials.len() > 1 { - potentials.sort_by(|a, b| { - if !a.reader.can_read() && b.reader.can_read() { - return Ordering::Less; - }; - if a.reader.can_read() && !b.reader.can_read() { - return Ordering::Greater; - }; - if a.exceptions.is_empty() && !b.exceptions.is_empty() { - return Ordering::Less; - }; - if !a.exceptions.is_empty() && b.exceptions.is_empty() { - return Ordering::Greater; - }; - Ordering::Equal - }) - } - let best_potential = potentials.into_iter().next().unwrap(); - return Ok(best_potential); - } - - Ok(ParseResults { - context: context_so_far, - reader: original_reader.clone(), - exceptions: errors, - }) - } - - pub fn execute( - &self, - input: StringReader, - source: Rc, - ) -> Result { - let parse = self.parse(input, source); - Self::execute_parsed(parse) - } - - pub fn add_paths( - &self, - node: Rc>>, - result: &mut Vec>>>>, - parents: Vec>>>, - ) { - let mut current = parents; - current.push(node.clone()); - result.push(current.clone()); - - for child in node.borrow().children.values() { - self.add_paths(child.clone(), result, current.clone()); - } - } - - pub fn get_path(&self, target: CommandNode) -> Vec { - let rc_target = Rc::new(RefCell::new(target)); - let mut nodes: Vec>>>> = Vec::new(); - self.add_paths(self.root.clone(), &mut nodes, vec![]); - - for list in nodes { - if *list.last().expect("Nothing in list").borrow() == *rc_target.borrow() { - let mut result: Vec = Vec::with_capacity(list.len()); - for node in list { - if node != self.root { - result.push(node.borrow().name().to_string()); - } - } - return result; - } - } - vec![] - } - - pub fn find_node(&self, path: &[&str]) -> Option>>> { - let mut node = self.root.clone(); - for name in path { - if let Some(child) = node.clone().borrow().child(name) { - node = child - } else { - return None; - } - } - Some(node) - } - - /// Executes a given pre-parsed command. - pub fn execute_parsed(parse: ParseResults) -> Result { - if parse.reader.can_read() { - if parse.exceptions.len() == 1 { - return Err(parse.exceptions.values().next().unwrap().clone()); - } - if parse.context.range.is_empty() { - return Err( - BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader) - ); - } - return Err( - BuiltInExceptions::DispatcherUnknownArgument.create_with_context(&parse.reader) - ); - } - let mut result = 0i32; - let mut successful_forks = 0; - let mut forked = false; - let mut found_command = false; - let command = parse.reader.string(); - let original = parse.context.build(command); - let mut contexts = vec![original]; - let mut next: Vec> = vec![]; - - while !contexts.is_empty() { - for context in contexts.iter() { - let child = &context.child; - if let Some(child) = child { - println!("aaaaaaa {:?}", child); - forked |= child.forks; - if child.has_nodes() { - found_command = true; - let modifier = &context.modifier; - if let Some(modifier) = modifier { - let results = modifier(context); - if let Ok(results) = results { - if !results.is_empty() { - next.extend(results.iter().map(|s| child.copy_for(s.clone()))); - } - } else { - // TODO - // self.consumer.on_command_complete(context, false, 0); - if !forked { - return Err(results.err().unwrap()); - } - } - } else { - next.push(child.copy_for(context.source.clone())); - } - } - } else if let Some(context_command) = &context.command { - found_command = true; - - let value = context_command(context); - result += value; - // consumer.on_command_complete(context, true, value); - successful_forks += 1; - - // TODO: allow context_command to error and handle those errors - } - } - - // move next into contexts and clear next - mem::swap(&mut contexts, &mut next); - next.clear(); - } - - if !found_command { - // consumer.on_command_complete(original, false, 0); - return Err( - BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader) - ); - } - - // TODO: this is not how vanilla does it but it works - Ok(if successful_forks >= 2 { - successful_forks - } else { - result - }) - // Ok(if forked { successful_forks } else { result }) - } -} - -impl Clone for CommandDispatcher { - fn clone(&self) -> Self { - Self { - root: self.root.clone(), - _marker: PhantomData, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - parsers::integer, - }; - - #[derive(Debug, PartialEq)] - struct CommandSource {} - - fn input_with_offset(input: &str, offset: usize) -> StringReader { - let mut result: StringReader = input.into(); - result.cursor = offset; - result - } - - #[test] - fn create_and_execute_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42)); - - assert_eq!( - subject - .execute("foo".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn create_and_execute_offset_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42)); - - assert_eq!( - subject - .execute(input_with_offset("/foo", 1), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn create_and_merge_commands() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("base").then(literal("foo").executes(|_| 42))); - subject.register(literal("base").then(literal("bar").executes(|_| 42))); - - assert_eq!( - subject - .execute("base foo".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - assert_eq!( - subject - .execute("base bar".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn execute_unknown_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("bar")); - subject.register(literal("baz")); - - let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 0); - } - - #[test] - fn execute_impermissible_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").requires(|_| false)); - - let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 0); - } - - #[test] - fn execute_empty_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("")); - - let execute_result = subject.execute("".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 0); - } - - #[test] - fn execute_unknown_subcommand() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42)); - - let execute_result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 4); - } - - #[test] - fn execute_incorrect_literal() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42).then(literal("bar"))); - - let execute_result = subject.execute("foo baz".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 4); - } - - #[test] - fn execute_ambiguous_incorrect_argument() { - let mut subject = CommandDispatcher::new(); - subject.register( - literal("foo") - .executes(|_| 42) - .then(literal("bar")) - .then(literal("baz")), - ); - - let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 4); - } - - #[test] - fn execute_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(literal("a")) - .then(literal("=").executes(|_| 100)) - .then(literal("c")) - .executes(|_| 42), - ); - - assert_eq!( - subject - .execute("foo =".into(), Rc::new(CommandSource {})) - .unwrap(), - 100 - ); - } - - #[test] - fn parse_incomplete_literal() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").then(literal("bar").executes(|_| 42))); - - let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); - assert_eq!(parse.reader.remaining(), " "); - assert_eq!(parse.context.nodes.len(), 1); - } - - #[test] - fn parse_incomplete_argument() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42))); - - let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); - assert_eq!(parse.reader.remaining(), " "); - assert_eq!(parse.context.nodes.len(), 1); - } - - #[test] - fn execute_ambiguious_parent_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("test") - .then(argument("incorrect", integer()).executes(|_| 42)) - .then( - argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), - ), - ); - - assert_eq!( - subject - .execute("test 1 2".into(), Rc::new(CommandSource {})) - .unwrap(), - 100 - ); - } - - #[test] - fn execute_ambiguious_parent_subcommand_via_redirect() { - let mut subject = CommandDispatcher::new(); - - let real = subject.register( - literal("test") - .then(argument("incorrect", integer()).executes(|_| 42)) - .then( - argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), - ), - ); - - subject.register(literal("redirect").redirect(real)); - - assert_eq!( - subject - .execute("redirect 1 2".into(), Rc::new(CommandSource {})) - .unwrap(), - 100 - ); - } - - #[test] - fn execute_redirected_multiple_times() { - let mut subject = CommandDispatcher::new(); - - let concrete_node = subject.register(literal("actual").executes(|_| 42)); - let root = subject.root.clone(); - let redirect_node = subject.register(literal("redirected").redirect(root.clone())); - - let input = "redirected redirected actual"; - - let parse = subject.parse(input.into(), Rc::new(CommandSource {})); - assert_eq!(parse.context.range.get(input), "redirected"); - assert_eq!(parse.context.nodes.len(), 1); - assert_eq!(parse.context.root, root); - assert_eq!(parse.context.nodes[0].range, parse.context.range); - assert_eq!(parse.context.nodes[0].node, redirect_node); - - let child1 = parse.context.child.clone(); - assert!(child1.is_some()); - assert_eq!(child1.clone().unwrap().range.get(input), "redirected"); - assert_eq!(child1.clone().unwrap().nodes.len(), 1); - assert_eq!(child1.clone().unwrap().root, root); - assert_eq!( - child1.clone().unwrap().nodes[0].range, - child1.clone().unwrap().range - ); - assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node); - - let child2 = child1.unwrap().child.clone(); - assert!(child2.is_some()); - assert_eq!(child2.clone().unwrap().range.get(input), "actual"); - assert_eq!(child2.clone().unwrap().nodes.len(), 1); - assert_eq!(child2.clone().unwrap().root, root); - assert_eq!( - child2.clone().unwrap().nodes[0].range, - child2.clone().unwrap().range - ); - assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node); - - assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); - } - - #[test] - fn execute_redirected() { - let mut subject = CommandDispatcher::new(); - - let source1 = Rc::new(CommandSource {}); - let source2 = Rc::new(CommandSource {}); - - let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { - Ok(vec![source1.clone(), source2.clone()]) - }; - - let concrete_node = subject.register(literal("actual").executes(|_| 42)); - let redirect_node = - subject.register(literal("redirected").fork(subject.root.clone(), Rc::new(modifier))); - - let input = "redirected actual"; - let parse = subject.parse(input.into(), Rc::new(CommandSource {})); - assert_eq!(parse.context.range.get(input), "redirected"); - assert_eq!(parse.context.nodes.len(), 1); - assert_eq!(parse.context.root, subject.root); - assert_eq!(parse.context.nodes[0].range, parse.context.range); - assert_eq!(parse.context.nodes[0].node, redirect_node); - - let parent = parse.context.child.clone(); - assert!(parent.is_some()); - let parent = parent.unwrap(); - assert_eq!(parent.range.get(input), "actual"); - assert_eq!(parent.nodes.len(), 1); - assert_eq!(parse.context.root, subject.root); - assert_eq!(parent.nodes[0].range, parent.range); - assert_eq!(parent.nodes[0].node, concrete_node); - assert_eq!(parent.source, Rc::new(CommandSource {})); - - assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); - } - - #[test] - fn execute_orphaned_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(argument("bar", integer())) - .executes(|_| 42), - ); - - let result = subject.execute("foo 5".into(), Rc::new(CommandSource {})); - assert!(result.is_err()); - let result = result.unwrap_err(); - assert_eq!( - *result.get_type(), - BuiltInExceptions::DispatcherUnknownCommand - ); - assert_eq!(result.cursor(), Some(5)); - } - - #[test] - fn execute_invalid_other() { - let mut subject = CommandDispatcher::new(); - - subject.register(literal("w").executes(|_| panic!("This should not run"))); - subject.register(literal("world").executes(|_| 42)); - - assert_eq!( - subject - .execute("world".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn parse_no_space_separator() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(argument("bar", integer())) - .executes(|_| 42), - ); - - let result = subject.execute("foo$".into(), Rc::new(CommandSource {})); - assert!(result.is_err()); - let result = result.unwrap_err(); - assert_eq!( - *result.get_type(), - BuiltInExceptions::DispatcherUnknownCommand - ); - assert_eq!(result.cursor(), Some(0)); - } - - #[test] - fn execute_invalid_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(argument("bar", integer())) - .executes(|_| 42), - ); - - let result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); - assert!(result.is_err()); - let result = result.unwrap_err(); - // this fails for some reason, i blame mojang - // assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt); - assert_eq!(result.cursor(), Some(4)); - } - - #[test] - fn get_path() { - let mut subject = CommandDispatcher::<()>::new(); - - let bar = literal("bar").build(); - subject.register(literal("foo").then_built(bar.clone())); - - assert_eq!( - subject.get_path(bar), - vec!["foo".to_string(), "bar".to_string()] - ); - } - - #[test] - fn find_node_doesnt_exist() { - let subject = CommandDispatcher::<()>::new(); - - assert_eq!(subject.find_node(&vec!["foo", "bar"]), None) - } -} diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs index 0bca556e..6b9c8d62 100644 --- a/azalea-brigadier/src/exceptions/mod.rs +++ b/azalea-brigadier/src/exceptions/mod.rs @@ -1,2 +1,5 @@ -pub mod builtin_exceptions; -pub mod command_syntax_exception; +mod builtin_exceptions; +mod command_syntax_exception; + +pub use builtin_exceptions::BuiltInExceptions; +pub use command_syntax_exception::CommandSyntaxException; diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index e359e274..cffaac12 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,12 +1,11 @@ pub mod builder; +pub mod command_dispatcher; pub mod context; -pub mod dispatcher; pub mod exceptions; pub mod message; pub mod modifier; pub mod parse_results; pub mod parsers; -pub mod string_range; pub mod string_reader; pub mod tree; @@ -17,8 +16,8 @@ mod tests { use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, + command_dispatcher::CommandDispatcher, context::CommandContext, - dispatcher::CommandDispatcher, parsers::{get_integer, integer}, }; diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs index 68a3304e..d40d59f9 100644 --- a/azalea-brigadier/src/modifier.rs +++ b/azalea-brigadier/src/modifier.rs @@ -1,8 +1,6 @@ use std::rc::Rc; -use crate::{ - context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, -}; +use crate::{context::CommandContext, exceptions::CommandSyntaxException}; pub type RedirectModifier = dyn Fn(&CommandContext) -> Result>, CommandSyntaxException>; diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index c9f26a04..3698ae82 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -1,5 +1,5 @@ use crate::{ - context::CommandContextBuilder, exceptions::command_syntax_exception::CommandSyntaxException, + context::CommandContextBuilder, exceptions::CommandSyntaxException, string_reader::StringReader, tree::CommandNode, }; use std::{collections::HashMap, fmt::Debug, rc::Rc}; diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index 1984b52f..18ee9119 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -2,9 +2,7 @@ use std::{any::Any, rc::Rc}; use crate::{ context::CommandContext, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, + exceptions::{BuiltInExceptions, CommandSyntaxException}, string_reader::StringReader, }; diff --git a/azalea-brigadier/src/string_range.rs b/azalea-brigadier/src/string_range.rs deleted file mode 100644 index 8ca88624..00000000 --- a/azalea-brigadier/src/string_range.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::cmp; - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct StringRange { - start: usize, - end: usize, -} - -impl StringRange { - pub fn new(start: usize, end: usize) -> Self { - Self { start, end } - } - - pub fn at(pos: usize) -> Self { - Self::new(pos, pos) - } - - pub fn between(start: usize, end: usize) -> Self { - Self::new(start, end) - } - - pub fn encompassing(a: &Self, b: &Self) -> Self { - Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end)) - } - - pub fn start(&self) -> usize { - self.start - } - - pub fn end(&self) -> usize { - self.end - } - - pub fn get<'a>(&self, reader: &'a str) -> &'a str { - &reader[self.start..self.end] - } - - pub fn is_empty(&self) -> bool { - self.start == self.end - } - - pub fn length(&self) -> usize { - self.end - self.start - } -} diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index f220267a..dcb35fcb 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,6 +1,4 @@ -use crate::exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, -}; +use crate::exceptions::{BuiltInExceptions, CommandSyntaxException}; use std::str::FromStr; #[derive(Clone)] From 8d71fbf813391783531a9f7c70e75e105fabaf03 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 18:14:25 +0000 Subject: [PATCH 47/83] change a BTreeMap to a HashMap --- azalea-brigadier/src/command_dispatcher.rs | 711 ++++++++++++++++++ .../src/context/command_context.rs | 80 ++ .../src/context/command_context_builder.rs | 116 +++ azalea-brigadier/src/context/mod.rs | 11 + .../src/context/parsed_argument.rs | 8 + .../src/context/parsed_command_node.rs | 18 + azalea-brigadier/src/context/string_range.rs | 45 ++ azalea-brigadier/src/tree/mod.rs | 259 +++++++ 8 files changed, 1248 insertions(+) create mode 100644 azalea-brigadier/src/command_dispatcher.rs create mode 100644 azalea-brigadier/src/context/command_context.rs create mode 100644 azalea-brigadier/src/context/command_context_builder.rs create mode 100644 azalea-brigadier/src/context/mod.rs create mode 100644 azalea-brigadier/src/context/parsed_argument.rs create mode 100644 azalea-brigadier/src/context/parsed_command_node.rs create mode 100644 azalea-brigadier/src/context/string_range.rs create mode 100644 azalea-brigadier/src/tree/mod.rs diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs new file mode 100644 index 00000000..ea788130 --- /dev/null +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -0,0 +1,711 @@ +use crate::{ + builder::argument_builder::ArgumentBuilder, + context::{CommandContext, CommandContextBuilder}, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + parse_results::ParseResults, + string_reader::StringReader, + tree::CommandNode, +}; +use std::{cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc}; + +#[derive(Default)] +pub struct CommandDispatcher { + root: Rc>>, + _marker: PhantomData, +} + +impl CommandDispatcher { + pub fn new() -> Self { + Self { + root: Rc::new(RefCell::new(CommandNode::default())), + _marker: PhantomData, + } + } + + pub fn register(&mut self, node: ArgumentBuilder) -> Rc>> { + let build = Rc::new(RefCell::new(node.build())); + self.root.borrow_mut().add_child(&build); + build + } + + pub fn parse(&self, command: StringReader, source: Rc) -> ParseResults { + let context = CommandContextBuilder::new( + Rc::new(self.clone()), + source, + self.root.clone(), + command.cursor(), + ); + self.parse_nodes(&self.root, &command, context).unwrap() + } + + fn parse_nodes( + &self, + node: &Rc>>, + original_reader: &StringReader, + context_so_far: CommandContextBuilder, + ) -> Result, CommandSyntaxException> { + let source = context_so_far.source.clone(); + let mut errors = HashMap::>, CommandSyntaxException>::new(); + let mut potentials: Vec> = vec![]; + let cursor = original_reader.cursor(); + + for child in node + .borrow() + .get_relevant_nodes(&mut original_reader.clone()) + { + if !child.borrow().can_use(source.clone()) { + continue; + } + let mut context = context_so_far.clone(); + let mut reader = original_reader.clone(); + + let parse_with_context_result = + child.borrow().parse_with_context(&mut reader, &mut context); + if let Err(ex) = parse_with_context_result { + errors.insert( + Rc::new((*child.borrow()).clone()), + BuiltInExceptions::DispatcherParseException { + message: ex.message(), + } + .create_with_context(&reader), + ); + reader.cursor = cursor; + continue; + } + if reader.can_read() && reader.peek() != ' ' { + errors.insert( + Rc::new((*child.borrow()).clone()), + BuiltInExceptions::DispatcherExpectedArgumentSeparator + .create_with_context(&reader), + ); + reader.cursor = cursor; + continue; + } + + context.with_command(&child.borrow().command); + if reader.can_read_length(if child.borrow().redirect.is_none() { + 2 + } else { + 1 + }) { + reader.skip(); + if let Some(redirect) = &child.borrow().redirect { + let child_context = CommandContextBuilder::new( + Rc::new(self.clone()), + source, + redirect.clone(), + reader.cursor, + ); + let parse = self + .parse_nodes(redirect, &reader, child_context) + .expect("Parsing nodes failed"); + context.with_child(Rc::new(parse.context)); + return Ok(ParseResults { + context, + reader: parse.reader, + exceptions: parse.exceptions, + }); + } else { + let parse = self + .parse_nodes(&child, &reader, context) + .expect("Parsing nodes failed"); + potentials.push(parse); + } + } else { + potentials.push(ParseResults { + context, + reader, + exceptions: HashMap::new(), + }); + } + } + + if !potentials.is_empty() { + if potentials.len() > 1 { + potentials.sort_by(|a, b| { + if !a.reader.can_read() && b.reader.can_read() { + return Ordering::Less; + }; + if a.reader.can_read() && !b.reader.can_read() { + return Ordering::Greater; + }; + if a.exceptions.is_empty() && !b.exceptions.is_empty() { + return Ordering::Less; + }; + if !a.exceptions.is_empty() && b.exceptions.is_empty() { + return Ordering::Greater; + }; + Ordering::Equal + }) + } + let best_potential = potentials.into_iter().next().unwrap(); + return Ok(best_potential); + } + + Ok(ParseResults { + context: context_so_far, + reader: original_reader.clone(), + exceptions: errors, + }) + } + + pub fn execute( + &self, + input: StringReader, + source: Rc, + ) -> Result { + let parse = self.parse(input, source); + Self::execute_parsed(parse) + } + + pub fn add_paths( + &self, + node: Rc>>, + result: &mut Vec>>>>, + parents: Vec>>>, + ) { + let mut current = parents; + current.push(node.clone()); + result.push(current.clone()); + + for child in node.borrow().children.values() { + self.add_paths(child.clone(), result, current.clone()); + } + } + + pub fn get_path(&self, target: CommandNode) -> Vec { + let rc_target = Rc::new(RefCell::new(target)); + let mut nodes: Vec>>>> = Vec::new(); + self.add_paths(self.root.clone(), &mut nodes, vec![]); + + for list in nodes { + if *list.last().expect("Nothing in list").borrow() == *rc_target.borrow() { + let mut result: Vec = Vec::with_capacity(list.len()); + for node in list { + if node != self.root { + result.push(node.borrow().name().to_string()); + } + } + return result; + } + } + vec![] + } + + pub fn find_node(&self, path: &[&str]) -> Option>>> { + let mut node = self.root.clone(); + for name in path { + if let Some(child) = node.clone().borrow().child(name) { + node = child + } else { + return None; + } + } + Some(node) + } + + /// Executes a given pre-parsed command. + pub fn execute_parsed(parse: ParseResults) -> Result { + if parse.reader.can_read() { + if parse.exceptions.len() == 1 { + return Err(parse.exceptions.values().next().unwrap().clone()); + } + if parse.context.range.is_empty() { + return Err( + BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader) + ); + } + return Err( + BuiltInExceptions::DispatcherUnknownArgument.create_with_context(&parse.reader) + ); + } + let mut result = 0i32; + let mut successful_forks = 0; + let mut forked = false; + let mut found_command = false; + let command = parse.reader.string(); + let original = parse.context.build(command); + let mut contexts = vec![original]; + let mut next: Vec> = vec![]; + + while !contexts.is_empty() { + for context in contexts.iter() { + let child = &context.child; + if let Some(child) = child { + println!("aaaaaaa {:?}", child); + forked |= child.forks; + if child.has_nodes() { + found_command = true; + let modifier = &context.modifier; + if let Some(modifier) = modifier { + let results = modifier(context); + if let Ok(results) = results { + if !results.is_empty() { + next.extend(results.iter().map(|s| child.copy_for(s.clone()))); + } + } else { + // TODO + // self.consumer.on_command_complete(context, false, 0); + if !forked { + return Err(results.err().unwrap()); + } + } + } else { + next.push(child.copy_for(context.source.clone())); + } + } + } else if let Some(context_command) = &context.command { + found_command = true; + + let value = context_command(context); + result += value; + // consumer.on_command_complete(context, true, value); + successful_forks += 1; + + // TODO: allow context_command to error and handle those errors + } + } + + // move next into contexts and clear next + mem::swap(&mut contexts, &mut next); + next.clear(); + } + + if !found_command { + // consumer.on_command_complete(original, false, 0); + return Err( + BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader) + ); + } + + // TODO: this is not how vanilla does it but it works + Ok(if successful_forks >= 2 { + successful_forks + } else { + result + }) + // Ok(if forked { successful_forks } else { result }) + } +} + +impl Clone for CommandDispatcher { + fn clone(&self) -> Self { + Self { + root: self.root.clone(), + _marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + parsers::integer, + }; + + #[derive(Debug, PartialEq)] + struct CommandSource {} + + fn input_with_offset(input: &str, offset: usize) -> StringReader { + let mut result: StringReader = input.into(); + result.cursor = offset; + result + } + + #[test] + fn create_and_execute_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute("foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn create_and_execute_offset_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute(input_with_offset("/foo", 1), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn create_and_merge_commands() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("base").then(literal("foo").executes(|_| 42))); + subject.register(literal("base").then(literal("bar").executes(|_| 42))); + + assert_eq!( + subject + .execute("base foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + assert_eq!( + subject + .execute("base bar".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn execute_unknown_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("bar")); + subject.register(literal("baz")); + + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + + #[test] + fn execute_impermissible_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").requires(|_| false)); + + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + + #[test] + fn execute_empty_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("")); + + let execute_result = subject.execute("".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + + #[test] + fn execute_unknown_subcommand() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + let execute_result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + + #[test] + fn execute_incorrect_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42).then(literal("bar"))); + + let execute_result = subject.execute("foo baz".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + + #[test] + fn execute_ambiguous_incorrect_argument() { + let mut subject = CommandDispatcher::new(); + subject.register( + literal("foo") + .executes(|_| 42) + .then(literal("bar")) + .then(literal("baz")), + ); + + let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + + #[test] + fn execute_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(literal("a")) + .then(literal("=").executes(|_| 100)) + .then(literal("c")) + .executes(|_| 42), + ); + + assert_eq!( + subject + .execute("foo =".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + + #[test] + fn parse_incomplete_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(literal("bar").executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); + } + + #[test] + fn parse_incomplete_argument() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); + } + + #[test] + fn execute_ambiguious_parent_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then( + argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), + ), + ); + + assert_eq!( + subject + .execute("test 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + + #[test] + fn execute_ambiguious_parent_subcommand_via_redirect() { + let mut subject = CommandDispatcher::new(); + + let real = subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then( + argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), + ), + ); + + subject.register(literal("redirect").redirect(real)); + + assert_eq!( + subject + .execute("redirect 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + + #[test] + fn execute_redirected_multiple_times() { + let mut subject = CommandDispatcher::new(); + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let root = subject.root.clone(); + let redirect_node = subject.register(literal("redirected").redirect(root.clone())); + + let input = "redirected redirected actual"; + + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let child1 = parse.context.child.clone(); + assert!(child1.is_some()); + assert_eq!(child1.clone().unwrap().range.get(input), "redirected"); + assert_eq!(child1.clone().unwrap().nodes.len(), 1); + assert_eq!(child1.clone().unwrap().root, root); + assert_eq!( + child1.clone().unwrap().nodes[0].range, + child1.clone().unwrap().range + ); + assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node); + + let child2 = child1.unwrap().child.clone(); + assert!(child2.is_some()); + assert_eq!(child2.clone().unwrap().range.get(input), "actual"); + assert_eq!(child2.clone().unwrap().nodes.len(), 1); + assert_eq!(child2.clone().unwrap().root, root); + assert_eq!( + child2.clone().unwrap().nodes[0].range, + child2.clone().unwrap().range + ); + assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); + } + + #[test] + fn execute_redirected() { + let mut subject = CommandDispatcher::new(); + + let source1 = Rc::new(CommandSource {}); + let source2 = Rc::new(CommandSource {}); + + let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { + Ok(vec![source1.clone(), source2.clone()]) + }; + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let redirect_node = + subject.register(literal("redirected").fork(subject.root.clone(), Rc::new(modifier))); + + let input = "redirected actual"; + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let parent = parse.context.child.clone(); + assert!(parent.is_some()); + let parent = parent.unwrap(); + assert_eq!(parent.range.get(input), "actual"); + assert_eq!(parent.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parent.nodes[0].range, parent.range); + assert_eq!(parent.nodes[0].node, concrete_node); + assert_eq!(parent.source, Rc::new(CommandSource {})); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); + } + + #[test] + fn execute_orphaned_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo 5".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(5)); + } + + #[test] + fn execute_invalid_other() { + let mut subject = CommandDispatcher::new(); + + subject.register(literal("w").executes(|_| panic!("This should not run"))); + subject.register(literal("world").executes(|_| 42)); + + assert_eq!( + subject + .execute("world".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn parse_no_space_separator() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo$".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(0)); + } + + #[test] + fn execute_invalid_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + // this fails for some reason, i blame mojang + // assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt); + assert_eq!(result.cursor(), Some(4)); + } + + #[test] + fn get_path() { + let mut subject = CommandDispatcher::<()>::new(); + + let bar = literal("bar").build(); + subject.register(literal("foo").then_built(bar.clone())); + + assert_eq!( + subject.get_path(bar), + vec!["foo".to_string(), "bar".to_string()] + ); + } + + #[test] + fn find_node_doesnt_exist() { + let subject = CommandDispatcher::<()>::new(); + + assert_eq!(subject.find_node(&vec!["foo", "bar"]), None) + } +} diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs new file mode 100644 index 00000000..1834a73d --- /dev/null +++ b/azalea-brigadier/src/context/command_context.rs @@ -0,0 +1,80 @@ +use super::{parsed_command_node::ParsedCommandNode, string_range::StringRange, ParsedArgument}; +use crate::{modifier::RedirectModifier, tree::CommandNode}; +use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; + +/// A built `CommandContextBuilder`. +pub struct CommandContext { + pub source: Rc, + pub input: String, + pub arguments: HashMap, + pub command: Option) -> i32>>, + pub root_node: Rc>>, + pub nodes: Vec>, + pub range: StringRange, + pub child: Option>>, + pub modifier: Option>>, + pub forks: bool, +} + +impl Clone for CommandContext { + fn clone(&self) -> Self { + Self { + source: self.source.clone(), + input: self.input.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + root_node: self.root_node.clone(), + nodes: self.nodes.clone(), + range: self.range.clone(), + child: self.child.clone(), + modifier: self.modifier.clone(), + forks: self.forks, + } + } +} + +impl Debug for CommandContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandContext") + // .field("source", &self.source) + .field("input", &self.input) + // .field("arguments", &self.arguments) + // .field("command", &self.command) + // .field("root_node", &self.root_node) + // .field("nodes", &self.nodes) + .field("range", &self.range) + .field("child", &self.child) + // .field("modifier", &self.modifier) + .field("forks", &self.forks) + .finish() + } +} + +impl CommandContext { + pub fn copy_for(&self, source: Rc) -> Self { + if Rc::ptr_eq(&source, &self.source) { + return self.clone(); + } + CommandContext { + source, + input: self.input.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + root_node: self.root_node.clone(), + nodes: self.nodes.clone(), + range: self.range.clone(), + child: self.child.clone(), + modifier: self.modifier.clone(), + forks: self.forks, + } + } + + pub fn has_nodes(&self) -> bool { + !self.nodes.is_empty() + } + + pub fn argument(&self, name: &str) -> Option> { + let argument = self.arguments.get(name); + argument.map(|a| a.result.clone()) + } +} diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs new file mode 100644 index 00000000..f192f6b7 --- /dev/null +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -0,0 +1,116 @@ +use super::{ + command_context::CommandContext, parsed_command_node::ParsedCommandNode, + string_range::StringRange, ParsedArgument, +}; +use crate::{command_dispatcher::CommandDispatcher, modifier::RedirectModifier, tree::CommandNode}; +use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; + +pub struct CommandContextBuilder { + pub arguments: HashMap, + pub root: Rc>>, + pub nodes: Vec>, + pub dispatcher: Rc>, + pub source: Rc, + pub command: Option) -> i32>>, + pub child: Option>>, + pub range: StringRange, + pub modifier: Option>>, + pub forks: bool, +} + +impl Clone for CommandContextBuilder { + fn clone(&self) -> Self { + Self { + arguments: self.arguments.clone(), + root: self.root.clone(), + nodes: self.nodes.clone(), + dispatcher: self.dispatcher.clone(), + source: self.source.clone(), + command: self.command.clone(), + child: self.child.clone(), + range: self.range.clone(), + modifier: self.modifier.clone(), + forks: self.forks, + } + } +} + +impl CommandContextBuilder { + pub fn new( + dispatcher: Rc>, + source: Rc, + root_node: Rc>>, + start: usize, + ) -> Self { + Self { + arguments: HashMap::new(), + root: root_node, + source, + range: StringRange::at(start), + command: None, + dispatcher, + nodes: vec![], + child: None, + modifier: None, + forks: false, + } + } + + pub fn with_command( + &mut self, + command: &Option) -> i32>>, + ) -> &Self { + self.command = command.clone(); + self + } + pub fn with_child(&mut self, child: Rc>) -> &Self { + self.child = Some(child); + self + } + pub fn with_argument(&mut self, name: &str, argument: ParsedArgument) -> &Self { + self.arguments.insert(name.to_string(), argument); + self + } + pub fn with_node(&mut self, node: Rc>>, range: StringRange) -> &Self { + self.nodes.push(ParsedCommandNode { + node: node.clone(), + range: range.clone(), + }); + self.range = StringRange::encompassing(&self.range, &range); + self.modifier = node.borrow().modifier.clone(); + self.forks = node.borrow().forks; + self + } + + pub fn build(&self, input: &str) -> CommandContext { + CommandContext { + arguments: self.arguments.clone(), + root_node: self.root.clone(), + nodes: self.nodes.clone(), + source: self.source.clone(), + command: self.command.clone(), + child: self.child.clone().map(|c| Rc::new(c.build(input))), + range: self.range.clone(), + forks: self.forks, + modifier: self.modifier.clone(), + input: input.to_string(), + } + } +} + +impl Debug for CommandContextBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandContextBuilder") + // .field("arguments", &self.arguments) + .field("root", &self.root) + // .field("nodes", &self.nodes) + // .field("dispatcher", &self.dispatcher) + // .field("source", &self.source) + // .field("command", &self.command) + .field("child", &self.child) + .field("range", &self.range) + // .field("modifier", &self.modifier) + .field("forks", &self.forks) + .finish() + } +} diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs new file mode 100644 index 00000000..d535602a --- /dev/null +++ b/azalea-brigadier/src/context/mod.rs @@ -0,0 +1,11 @@ +mod command_context; +mod command_context_builder; +mod parsed_argument; +mod parsed_command_node; +mod string_range; + +pub use command_context::CommandContext; +pub use command_context_builder::CommandContextBuilder; +pub use parsed_argument::ParsedArgument; +pub use parsed_command_node::ParsedCommandNode; +pub use string_range::StringRange; diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs new file mode 100644 index 00000000..3302b1be --- /dev/null +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -0,0 +1,8 @@ +use super::string_range::StringRange; +use std::{any::Any, rc::Rc}; + +#[derive(Clone)] +pub struct ParsedArgument { + pub range: StringRange, + pub result: Rc, +} diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs new file mode 100644 index 00000000..ed49928d --- /dev/null +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -0,0 +1,18 @@ +use super::string_range::StringRange; +use crate::tree::CommandNode; +use std::{cell::RefCell, rc::Rc}; + +#[derive(Debug)] +pub struct ParsedCommandNode { + pub node: Rc>>, + pub range: StringRange, +} + +impl Clone for ParsedCommandNode { + fn clone(&self) -> Self { + Self { + node: self.node.clone(), + range: self.range.clone(), + } + } +} diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs new file mode 100644 index 00000000..8ca88624 --- /dev/null +++ b/azalea-brigadier/src/context/string_range.rs @@ -0,0 +1,45 @@ +use std::cmp; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct StringRange { + start: usize, + end: usize, +} + +impl StringRange { + pub fn new(start: usize, end: usize) -> Self { + Self { start, end } + } + + pub fn at(pos: usize) -> Self { + Self::new(pos, pos) + } + + pub fn between(start: usize, end: usize) -> Self { + Self::new(start, end) + } + + pub fn encompassing(a: &Self, b: &Self) -> Self { + Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end)) + } + + pub fn start(&self) -> usize { + self.start + } + + pub fn end(&self) -> usize { + self.end + } + + pub fn get<'a>(&self, reader: &'a str) -> &'a str { + &reader[self.start..self.end] + } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } + + pub fn length(&self) -> usize { + self.end - self.start + } +} diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs new file mode 100644 index 00000000..b6181c73 --- /dev/null +++ b/azalea-brigadier/src/tree/mod.rs @@ -0,0 +1,259 @@ +use crate::{ + builder::{ + argument_builder::ArgumentBuilderType, literal_argument_builder::Literal, + required_argument_builder::Argument, + }, + context::{CommandContext, CommandContextBuilder, ParsedArgument, StringRange}, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + modifier::RedirectModifier, + string_reader::StringReader, +}; +use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; + +/// An ArgumentBuilder that has been built. +#[non_exhaustive] +pub struct CommandNode { + pub value: ArgumentBuilderType, + + pub children: HashMap>>>, + pub literals: HashMap>>>, + pub arguments: HashMap>>>, + + pub command: Option) -> i32>>, + pub requirement: Rc) -> bool>, + pub redirect: Option>>>, + pub forks: bool, + pub modifier: Option>>, +} + +impl Clone for CommandNode { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + children: self.children.clone(), + literals: self.literals.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + requirement: self.requirement.clone(), + redirect: self.redirect.clone(), + forks: self.forks, + modifier: self.modifier.clone(), + } + } +} + +impl CommandNode { + /// Gets the literal, or panics. You should use match if you're not certain about the type. + pub fn literal(&self) -> &Literal { + match self.value { + ArgumentBuilderType::Literal(ref literal) => literal, + _ => panic!("CommandNode::literal() called on non-literal node"), + } + } + /// Gets the argument, or panics. You should use match if you're not certain about the type. + pub fn argument(&self) -> &Argument { + match self.value { + ArgumentBuilderType::Argument(ref argument) => argument, + _ => panic!("CommandNode::argument() called on non-argument node"), + } + } + + pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { + let literals = &self.literals; + + if !literals.is_empty() { + let cursor = input.cursor(); + while input.can_read() && input.peek() != ' ' { + input.skip(); + } + let text: String = input + .string() + .chars() + .skip(cursor) + .take(input.cursor() - cursor) + .collect(); + input.cursor = cursor; + let literal = literals.get(&text); + if let Some(literal) = literal { + return vec![literal.clone()]; + } else { + return self.arguments.values().cloned().collect(); + } + } else { + self.arguments.values().cloned().collect() + } + } + + pub fn can_use(&self, source: Rc) -> bool { + (self.requirement)(source) + } + + pub fn add_child(&mut self, node: &Rc>>) { + let child = self.children.get(node.borrow().name()); + if let Some(child) = child { + // We've found something to merge onto + if let Some(command) = &node.borrow().command { + child.borrow_mut().command = Some(command.clone()); + } + for grandchild in node.borrow().children.values() { + child.borrow_mut().add_child(grandchild); + } + } else { + self.children + .insert(node.borrow().name().to_string(), node.clone()); + match &node.borrow().value { + ArgumentBuilderType::Literal(literal) => { + self.literals.insert(literal.value.clone(), node.clone()); + } + ArgumentBuilderType::Argument(argument) => { + self.arguments.insert(argument.name.clone(), node.clone()); + } + } + } + } + + pub fn name(&self) -> &str { + match &self.value { + ArgumentBuilderType::Argument(argument) => &argument.name, + ArgumentBuilderType::Literal(literal) => &literal.value, + } + } + + pub fn child(&self, name: &str) -> Option>>> { + self.children.get(name).cloned() + } + + pub fn parse_with_context( + &self, + reader: &mut StringReader, + context_builder: &mut CommandContextBuilder, + ) -> Result<(), CommandSyntaxException> { + match self.value { + ArgumentBuilderType::Argument(ref argument) => { + let start = reader.cursor(); + let result = argument.parse(reader)?; + let parsed = ParsedArgument { + range: StringRange::between(start, reader.cursor()), + result, + }; + + context_builder.with_argument(&argument.name, parsed.clone()); + context_builder.with_node(Rc::new(RefCell::new(self.clone())), parsed.range); + + Ok(()) + } + ArgumentBuilderType::Literal(ref literal) => { + let start = reader.cursor(); + let end = self.parse(reader); + + if let Some(end) = end { + context_builder.with_node( + Rc::new(RefCell::new(self.clone())), + StringRange::between(start, end), + ); + return Ok(()); + } + + Err(BuiltInExceptions::LiteralIncorrect { + expected: literal.value.clone(), + } + .create_with_context(reader)) + } + } + } + + fn parse(&self, reader: &mut StringReader) -> Option { + match self.value { + ArgumentBuilderType::Argument(_) => { + panic!("Can't parse argument.") + } + ArgumentBuilderType::Literal(ref literal) => { + let start = reader.cursor(); + if reader.can_read_length(literal.value.len()) { + let end = start + literal.value.len(); + if reader + .string() + .get(start..end) + .expect("Couldn't slice reader correctly?") + == literal.value + { + reader.cursor = end; + if !reader.can_read() || reader.peek() == ' ' { + return Some(end); + } else { + reader.cursor = start; + } + } + } + } + } + None + } +} + +impl Debug for CommandNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandNode") + .field("value", &self.value) + .field("children", &self.children) + .field("command", &self.command.is_some()) + // .field("requirement", &self.requirement) + .field("redirect", &self.redirect) + .field("forks", &self.forks) + // .field("modifier", &self.modifier) + .finish() + } +} + +impl Default for CommandNode { + fn default() -> Self { + Self { + value: ArgumentBuilderType::Literal(Literal::default()), + + children: HashMap::new(), + literals: HashMap::new(), + arguments: HashMap::new(), + + command: None, + requirement: Rc::new(|_| true), + redirect: None, + forks: false, + modifier: None, + } + } +} + +impl Hash for CommandNode { + fn hash(&self, state: &mut H) { + // hash the children + for (k, v) in &self.children { + k.hash(state); + v.borrow().hash(state); + } + // i hope this works because if doesn't then that'll be a problem + ptr::hash(&self.command, state); + } +} + +impl PartialEq for CommandNode { + fn eq(&self, other: &Self) -> bool { + if self.children != other.children { + return false; + } + if let Some(selfexecutes) = &self.command { + // idk how to do this better since we can't compare `dyn Fn`s + if let Some(otherexecutes) = &other.command { + #[allow(clippy::vtable_address_comparisons)] + if !Rc::ptr_eq(selfexecutes, otherexecutes) { + return false; + } + } else { + return false; + } + } else if other.command.is_some() { + return false; + } + true + } +} +impl Eq for CommandNode {} From b8ceb56e7141320d5ba23a946fe3eceee43f51f5 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 19:38:08 -0500 Subject: [PATCH 48/83] move `parsers` into `arguments` --- azalea-brigadier/src/arguments/argument_type.rs | 7 +++++++ .../{parsers.rs => arguments/integer_argument_type.rs} | 8 +++----- azalea-brigadier/src/arguments/mod.rs | 4 ++++ azalea-brigadier/src/builder/argument_builder.rs | 2 +- .../src/builder/required_argument_builder.rs | 10 ++++++---- azalea-brigadier/src/command_dispatcher.rs | 2 +- azalea-brigadier/src/lib.rs | 4 ++-- 7 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 azalea-brigadier/src/arguments/argument_type.rs rename azalea-brigadier/src/{parsers.rs => arguments/integer_argument_type.rs} (88%) create mode 100644 azalea-brigadier/src/arguments/mod.rs diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs new file mode 100644 index 00000000..029e4696 --- /dev/null +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -0,0 +1,7 @@ +use std::{any::Any, rc::Rc}; + +use crate::{exceptions::CommandSyntaxException, string_reader::StringReader}; + +pub trait ArgumentType { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; +} diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs similarity index 88% rename from azalea-brigadier/src/parsers.rs rename to azalea-brigadier/src/arguments/integer_argument_type.rs index 18ee9119..336046a7 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/arguments/integer_argument_type.rs @@ -6,9 +6,7 @@ use crate::{ string_reader::StringReader, }; -pub trait Parser { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; -} +use super::ArgumentType; #[derive(Default)] struct Integer { @@ -16,7 +14,7 @@ struct Integer { pub maximum: Option, } -impl Parser for Integer { +impl ArgumentType for Integer { fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { let start = reader.cursor; let result = reader.read_int()?; @@ -44,7 +42,7 @@ impl Parser for Integer { } } -pub fn integer() -> impl Parser { +pub fn integer() -> impl ArgumentType { Integer::default() } pub fn get_integer(context: &CommandContext, name: &str) -> Option { diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs new file mode 100644 index 00000000..dec39297 --- /dev/null +++ b/azalea-brigadier/src/arguments/mod.rs @@ -0,0 +1,4 @@ +mod argument_type; +pub mod integer_argument_type; + +pub use argument_type::ArgumentType; diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index dee6ccfe..14d41f4e 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -141,8 +141,8 @@ mod tests { use std::rc::Rc; use crate::{ + arguments::integer_argument_type::integer, builder::{literal_argument_builder::literal, required_argument_builder::argument}, - parsers::integer, }; use super::ArgumentBuilder; diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index a50f7ea9..9d4d9e0a 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,5 +1,7 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; -use crate::{exceptions::CommandSyntaxException, parsers::Parser, string_reader::StringReader}; +use crate::{ + arguments::ArgumentType, exceptions::CommandSyntaxException, string_reader::StringReader, +}; use std::{any::Any, fmt::Debug, rc::Rc}; /// An argument node type. The `T` type parameter is the type of the argument, @@ -7,10 +9,10 @@ use std::{any::Any, fmt::Debug, rc::Rc}; #[derive(Clone)] pub struct Argument { pub name: String, - parser: Rc, + parser: Rc, } impl Argument { - pub fn new(name: &str, parser: Rc) -> Self { + pub fn new(name: &str, parser: Rc) -> Self { Self { name: name.to_string(), parser, @@ -38,6 +40,6 @@ impl Debug for Argument { } /// Shortcut for creating a new argument builder node. -pub fn argument(name: &str, parser: impl Parser + 'static) -> ArgumentBuilder { +pub fn argument(name: &str, parser: impl ArgumentType + 'static) -> ArgumentBuilder { ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) } diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index ea788130..fc3f3d50 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -301,8 +301,8 @@ impl Clone for CommandDispatcher { mod tests { use super::*; use crate::{ + arguments::integer_argument_type::integer, builder::{literal_argument_builder::literal, required_argument_builder::argument}, - parsers::integer, }; #[derive(Debug, PartialEq)] diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index cffaac12..db46d46e 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,3 +1,4 @@ +pub mod arguments; pub mod builder; pub mod command_dispatcher; pub mod context; @@ -5,7 +6,6 @@ pub mod exceptions; pub mod message; pub mod modifier; pub mod parse_results; -pub mod parsers; pub mod string_reader; pub mod tree; @@ -15,10 +15,10 @@ mod tests { use std::rc::Rc; use crate::{ + arguments::integer_argument_type::{get_integer, integer}, builder::{literal_argument_builder::literal, required_argument_builder::argument}, command_dispatcher::CommandDispatcher, context::CommandContext, - parsers::{get_integer, integer}, }; struct CommandSourceStack { From 78e692efc3fffc939f1b5af02e3cf32ca5302265 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 19:45:32 -0500 Subject: [PATCH 49/83] move tests into tests directory --- .../src/builder/argument_builder.rs | 79 --- azalea-brigadier/src/command_dispatcher.rs | 415 +----------- azalea-brigadier/src/lib.rs | 48 -- azalea-brigadier/src/string_reader.rs | 616 ------------------ .../arguments/bool_argument_type_test.rs | 0 .../arguments/double_argument_type_test.rs | 0 .../arguments/float_argument_type_test.rs | 0 .../arguments/integer_argument_type_test.rs | 0 .../arguments/long_argument_type_test.rs | 0 .../arguments/string_argument_type_test.rs | 0 .../tests/builder/argument_builder_test.rs | 75 +++ .../builder/literal_argument_builder_test.rs | 0 .../builder/required_argument_builder_test.rs | 0 .../tests/command_dispatcher_test.rs | 410 ++++++++++++ .../tests/command_dispatcher_usages_test.rs | 1 + .../tests/command_suggestions_test.rs | 1 + .../tests/context/command_context_test.rs | 0 .../tests/context/parsed_argument_test.rs | 0 ...amic_command_syntax_exception_type_test.rs | 0 ...mple_command_syntax_exception_type_test.rs | 0 azalea-brigadier/tests/string_reader_test.rs | 612 +++++++++++++++++ .../tests/suggestion/suggestion_test.rs | 0 .../suggestion/suggestions_builder_test.rs | 0 .../tests/suggestion/suggestions_test.rs | 0 .../tests/tree/abstract_command_node_test.rs | 0 .../tests/tree/argument_command_node_test.rs | 0 .../tests/tree/literal_command_node_test.rs | 0 .../tests/tree/root_command_node_test.rs | 0 28 files changed, 1100 insertions(+), 1157 deletions(-) create mode 100644 azalea-brigadier/tests/arguments/bool_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/double_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/float_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/integer_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/long_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/string_argument_type_test.rs create mode 100644 azalea-brigadier/tests/builder/argument_builder_test.rs create mode 100644 azalea-brigadier/tests/builder/literal_argument_builder_test.rs create mode 100644 azalea-brigadier/tests/builder/required_argument_builder_test.rs create mode 100644 azalea-brigadier/tests/command_dispatcher_test.rs create mode 100644 azalea-brigadier/tests/command_dispatcher_usages_test.rs create mode 100644 azalea-brigadier/tests/command_suggestions_test.rs create mode 100644 azalea-brigadier/tests/context/command_context_test.rs create mode 100644 azalea-brigadier/tests/context/parsed_argument_test.rs create mode 100644 azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs create mode 100644 azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs create mode 100644 azalea-brigadier/tests/string_reader_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestion_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestions_builder_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestions_test.rs create mode 100644 azalea-brigadier/tests/tree/abstract_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/argument_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/literal_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/root_command_node_test.rs diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 14d41f4e..d26b2a8a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -135,82 +135,3 @@ impl Debug for ArgumentBuilder { .finish() } } - -#[cfg(test)] -mod tests { - use std::rc::Rc; - - use crate::{ - arguments::integer_argument_type::integer, - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - }; - - use super::ArgumentBuilder; - - // public class ArgumentBuilderTest { - // private TestableArgumentBuilder builder; - - // @Before - // public void setUp() throws Exception { - // builder = new TestableArgumentBuilder<>(); - // } - - // @Test - // public void testArguments() throws Exception { - // final RequiredArgumentBuilder argument = argument("bar", integer()); - - // builder.then(argument); - - // assertThat(builder.getArguments(), hasSize(1)); - // assertThat(builder.getArguments(), hasItem((CommandNode) argument.build())); - // } - - #[test] - fn test_arguments() { - let mut builder: ArgumentBuilder<()> = literal("foo"); - - let argument: ArgumentBuilder<()> = argument("bar", integer()); - builder.then(argument.clone()); - assert_eq!(builder.arguments.children.len(), 1); - let built_argument = Rc::new(argument.build()); - assert!(builder - .arguments - .children - .values() - .any(|e| *e.borrow() == *built_argument)); - } - - // @Test - // public void testRedirect() throws Exception { - // final CommandNode target = mock(CommandNode.class); - // builder.redirect(target); - // assertThat(builder.getRedirect(), is(target)); - // } - - // @Test(expected = IllegalStateException.class) - // public void testRedirect_withChild() throws Exception { - // final CommandNode target = mock(CommandNode.class); - // builder.then(literal("foo")); - // builder.redirect(target); - // } - - // @Test(expected = IllegalStateException.class) - // public void testThen_withRedirect() throws Exception { - // final CommandNode target = mock(CommandNode.class); - // builder.redirect(target); - // builder.then(literal("foo")); - // } - - // private static class TestableArgumentBuilder extends ArgumentBuilder> { - // @Override - // protected TestableArgumentBuilder getThis() { - // return this; - // } - - // @Override - // public CommandNode build() { - // return null; - // } - // } - // } -} diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index fc3f3d50..eab42dd8 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -10,7 +10,7 @@ use std::{cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomDat #[derive(Default)] pub struct CommandDispatcher { - root: Rc>>, + pub root: Rc>>, _marker: PhantomData, } @@ -296,416 +296,3 @@ impl Clone for CommandDispatcher { } } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - arguments::integer_argument_type::integer, - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - }; - - #[derive(Debug, PartialEq)] - struct CommandSource {} - - fn input_with_offset(input: &str, offset: usize) -> StringReader { - let mut result: StringReader = input.into(); - result.cursor = offset; - result - } - - #[test] - fn create_and_execute_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42)); - - assert_eq!( - subject - .execute("foo".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn create_and_execute_offset_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42)); - - assert_eq!( - subject - .execute(input_with_offset("/foo", 1), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn create_and_merge_commands() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("base").then(literal("foo").executes(|_| 42))); - subject.register(literal("base").then(literal("bar").executes(|_| 42))); - - assert_eq!( - subject - .execute("base foo".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - assert_eq!( - subject - .execute("base bar".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn execute_unknown_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("bar")); - subject.register(literal("baz")); - - let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 0); - } - - #[test] - fn execute_impermissible_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").requires(|_| false)); - - let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 0); - } - - #[test] - fn execute_empty_command() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("")); - - let execute_result = subject.execute("".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 0); - } - - #[test] - fn execute_unknown_subcommand() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42)); - - let execute_result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 4); - } - - #[test] - fn execute_incorrect_literal() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").executes(|_| 42).then(literal("bar"))); - - let execute_result = subject.execute("foo baz".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 4); - } - - #[test] - fn execute_ambiguous_incorrect_argument() { - let mut subject = CommandDispatcher::new(); - subject.register( - literal("foo") - .executes(|_| 42) - .then(literal("bar")) - .then(literal("baz")), - ); - - let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {})); - - let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } - assert_eq!(err.cursor().unwrap(), 4); - } - - #[test] - fn execute_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(literal("a")) - .then(literal("=").executes(|_| 100)) - .then(literal("c")) - .executes(|_| 42), - ); - - assert_eq!( - subject - .execute("foo =".into(), Rc::new(CommandSource {})) - .unwrap(), - 100 - ); - } - - #[test] - fn parse_incomplete_literal() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").then(literal("bar").executes(|_| 42))); - - let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); - assert_eq!(parse.reader.remaining(), " "); - assert_eq!(parse.context.nodes.len(), 1); - } - - #[test] - fn parse_incomplete_argument() { - let mut subject = CommandDispatcher::new(); - subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42))); - - let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); - assert_eq!(parse.reader.remaining(), " "); - assert_eq!(parse.context.nodes.len(), 1); - } - - #[test] - fn execute_ambiguious_parent_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("test") - .then(argument("incorrect", integer()).executes(|_| 42)) - .then( - argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), - ), - ); - - assert_eq!( - subject - .execute("test 1 2".into(), Rc::new(CommandSource {})) - .unwrap(), - 100 - ); - } - - #[test] - fn execute_ambiguious_parent_subcommand_via_redirect() { - let mut subject = CommandDispatcher::new(); - - let real = subject.register( - literal("test") - .then(argument("incorrect", integer()).executes(|_| 42)) - .then( - argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), - ), - ); - - subject.register(literal("redirect").redirect(real)); - - assert_eq!( - subject - .execute("redirect 1 2".into(), Rc::new(CommandSource {})) - .unwrap(), - 100 - ); - } - - #[test] - fn execute_redirected_multiple_times() { - let mut subject = CommandDispatcher::new(); - - let concrete_node = subject.register(literal("actual").executes(|_| 42)); - let root = subject.root.clone(); - let redirect_node = subject.register(literal("redirected").redirect(root.clone())); - - let input = "redirected redirected actual"; - - let parse = subject.parse(input.into(), Rc::new(CommandSource {})); - assert_eq!(parse.context.range.get(input), "redirected"); - assert_eq!(parse.context.nodes.len(), 1); - assert_eq!(parse.context.root, root); - assert_eq!(parse.context.nodes[0].range, parse.context.range); - assert_eq!(parse.context.nodes[0].node, redirect_node); - - let child1 = parse.context.child.clone(); - assert!(child1.is_some()); - assert_eq!(child1.clone().unwrap().range.get(input), "redirected"); - assert_eq!(child1.clone().unwrap().nodes.len(), 1); - assert_eq!(child1.clone().unwrap().root, root); - assert_eq!( - child1.clone().unwrap().nodes[0].range, - child1.clone().unwrap().range - ); - assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node); - - let child2 = child1.unwrap().child.clone(); - assert!(child2.is_some()); - assert_eq!(child2.clone().unwrap().range.get(input), "actual"); - assert_eq!(child2.clone().unwrap().nodes.len(), 1); - assert_eq!(child2.clone().unwrap().root, root); - assert_eq!( - child2.clone().unwrap().nodes[0].range, - child2.clone().unwrap().range - ); - assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node); - - assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); - } - - #[test] - fn execute_redirected() { - let mut subject = CommandDispatcher::new(); - - let source1 = Rc::new(CommandSource {}); - let source2 = Rc::new(CommandSource {}); - - let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { - Ok(vec![source1.clone(), source2.clone()]) - }; - - let concrete_node = subject.register(literal("actual").executes(|_| 42)); - let redirect_node = - subject.register(literal("redirected").fork(subject.root.clone(), Rc::new(modifier))); - - let input = "redirected actual"; - let parse = subject.parse(input.into(), Rc::new(CommandSource {})); - assert_eq!(parse.context.range.get(input), "redirected"); - assert_eq!(parse.context.nodes.len(), 1); - assert_eq!(parse.context.root, subject.root); - assert_eq!(parse.context.nodes[0].range, parse.context.range); - assert_eq!(parse.context.nodes[0].node, redirect_node); - - let parent = parse.context.child.clone(); - assert!(parent.is_some()); - let parent = parent.unwrap(); - assert_eq!(parent.range.get(input), "actual"); - assert_eq!(parent.nodes.len(), 1); - assert_eq!(parse.context.root, subject.root); - assert_eq!(parent.nodes[0].range, parent.range); - assert_eq!(parent.nodes[0].node, concrete_node); - assert_eq!(parent.source, Rc::new(CommandSource {})); - - assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); - } - - #[test] - fn execute_orphaned_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(argument("bar", integer())) - .executes(|_| 42), - ); - - let result = subject.execute("foo 5".into(), Rc::new(CommandSource {})); - assert!(result.is_err()); - let result = result.unwrap_err(); - assert_eq!( - *result.get_type(), - BuiltInExceptions::DispatcherUnknownCommand - ); - assert_eq!(result.cursor(), Some(5)); - } - - #[test] - fn execute_invalid_other() { - let mut subject = CommandDispatcher::new(); - - subject.register(literal("w").executes(|_| panic!("This should not run"))); - subject.register(literal("world").executes(|_| 42)); - - assert_eq!( - subject - .execute("world".into(), Rc::new(CommandSource {})) - .unwrap(), - 42 - ); - } - - #[test] - fn parse_no_space_separator() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(argument("bar", integer())) - .executes(|_| 42), - ); - - let result = subject.execute("foo$".into(), Rc::new(CommandSource {})); - assert!(result.is_err()); - let result = result.unwrap_err(); - assert_eq!( - *result.get_type(), - BuiltInExceptions::DispatcherUnknownCommand - ); - assert_eq!(result.cursor(), Some(0)); - } - - #[test] - fn execute_invalid_subcommand() { - let mut subject = CommandDispatcher::new(); - - subject.register( - literal("foo") - .then(argument("bar", integer())) - .executes(|_| 42), - ); - - let result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); - assert!(result.is_err()); - let result = result.unwrap_err(); - // this fails for some reason, i blame mojang - // assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt); - assert_eq!(result.cursor(), Some(4)); - } - - #[test] - fn get_path() { - let mut subject = CommandDispatcher::<()>::new(); - - let bar = literal("bar").build(); - subject.register(literal("foo").then_built(bar.clone())); - - assert_eq!( - subject.get_path(bar), - vec!["foo".to_string(), "bar".to_string()] - ); - } - - #[test] - fn find_node_doesnt_exist() { - let subject = CommandDispatcher::<()>::new(); - - assert_eq!(subject.find_node(&vec!["foo", "bar"]), None) - } -} diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index db46d46e..a294eb19 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -8,51 +8,3 @@ pub mod modifier; pub mod parse_results; pub mod string_reader; pub mod tree; - -#[cfg(test)] -mod tests { - - use std::rc::Rc; - - use crate::{ - arguments::integer_argument_type::{get_integer, integer}, - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - command_dispatcher::CommandDispatcher, - context::CommandContext, - }; - - struct CommandSourceStack { - player: String, - } - - #[test] - fn it_works() { - let mut dispatcher = CommandDispatcher::new(); - - let source = Rc::new(CommandSourceStack { - player: "player".to_string(), - }); - - dispatcher.register( - literal("foo") - .then(argument("bar", integer()).executes( - |c: &CommandContext| { - println!( - "Bar is {:?} and player is {}", - get_integer(c, "bar"), - c.source.player - ); - 2 - }, - )) - .executes(|_| { - println!("Called foo with no arguments"); - 1 - }), - ); - - let parse = dispatcher.parse("foo 123".into(), source.clone()); - assert_eq!(CommandDispatcher::<_>::execute_parsed(parse).unwrap(), 2); - assert_eq!(dispatcher.execute("foo".into(), source).unwrap(), 1); - } -} diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index dcb35fcb..9eb09b57 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -272,619 +272,3 @@ impl StringReader { Ok(()) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn can_read() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.can_read(), true); - reader.skip(); // 'a' - assert_eq!(reader.can_read(), true); - reader.skip(); // 'b' - assert_eq!(reader.can_read(), true); - reader.skip(); // 'c' - assert_eq!(reader.can_read(), false); - } - - #[test] - fn get_remaining_length() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.remaining_length(), 3); - reader.cursor = 1; - assert_eq!(reader.remaining_length(), 2); - reader.cursor = 2; - assert_eq!(reader.remaining_length(), 1); - reader.cursor = 3; - assert_eq!(reader.remaining_length(), 0); - } - - #[test] - fn can_read_length() { - let reader = StringReader::from("abc".to_string()); - assert_eq!(reader.can_read_length(1), true); - assert_eq!(reader.can_read_length(2), true); - assert_eq!(reader.can_read_length(3), true); - assert_eq!(reader.can_read_length(4), false); - assert_eq!(reader.can_read_length(5), false); - } - - #[test] - fn peek() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.peek(), 'a'); - assert_eq!(reader.cursor(), 0); - reader.cursor = 2; - assert_eq!(reader.peek(), 'c'); - assert_eq!(reader.cursor(), 2); - } - - #[test] - fn peek_length() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.peek_offset(0), 'a'); - assert_eq!(reader.peek_offset(2), 'c'); - assert_eq!(reader.cursor(), 0); - reader.cursor = 1; - assert_eq!(reader.peek_offset(1), 'c'); - assert_eq!(reader.cursor(), 1); - } - - #[test] - fn read() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.read(), 'a'); - assert_eq!(reader.read(), 'b'); - assert_eq!(reader.read(), 'c'); - assert_eq!(reader.cursor(), 3); - } - - #[test] - fn skip() { - let mut reader = StringReader::from("abc".to_string()); - reader.skip(); - assert_eq!(reader.cursor(), 1); - } - - #[test] - fn get_remaining() { - let mut reader = StringReader::from("Hello!".to_string()); - assert_eq!(reader.remaining(), "Hello!"); - reader.cursor = 3; - assert_eq!(reader.remaining(), "lo!"); - reader.cursor = 6; - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn get_read() { - let mut reader = StringReader::from("Hello!".to_string()); - assert_eq!(reader.get_read(), ""); - reader.cursor = 3; - assert_eq!(reader.get_read(), "Hel"); - reader.cursor = 6; - assert_eq!(reader.get_read(), "Hello!"); - } - - #[test] - fn skip_whitespace_none() { - let mut reader = StringReader::from("Hello!".to_string()); - reader.skip_whitespace(); - assert_eq!(reader.cursor(), 0); - } - - #[test] - fn skip_whitespace_mixed() { - let mut reader = StringReader::from(" \t \t\nHello!".to_string()); - reader.skip_whitespace(); - assert_eq!(reader.cursor(), 5); - } - - #[test] - fn skip_whitespace_empty() { - let mut reader = StringReader::from("".to_string()); - reader.skip_whitespace(); - assert_eq!(reader.cursor(), 0); - } - - #[test] - fn read_unquoted_string() { - let mut reader = StringReader::from("hello world".to_string()); - assert_eq!(reader.read_unquoted_string(), "hello"); - assert_eq!(reader.get_read(), "hello"); - assert_eq!(reader.remaining(), " world"); - } - - #[test] - fn read_unquoted_string_empty() { - let mut reader = StringReader::from("".to_string()); - assert_eq!(reader.read_unquoted_string(), ""); - assert_eq!(reader.get_read(), ""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_unquoted_string_empty_with_remaining() { - let mut reader = StringReader::from(" hello world".to_string()); - assert_eq!(reader.read_unquoted_string(), ""); - assert_eq!(reader.get_read(), ""); - assert_eq!(reader.remaining(), " hello world"); - } - - #[test] - fn read_quoted_string() { - let mut reader = StringReader::from("\"hello world\"".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); - assert_eq!(reader.get_read(), "\"hello world\""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_single_quoted_string() { - let mut reader = StringReader::from("'hello world'".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); - assert_eq!(reader.get_read(), "'hello world'"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_mixed_quoted_string_double_inside_single() { - let mut reader = StringReader::from("'hello \"world\"'".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); - assert_eq!(reader.get_read(), "'hello \"world\"'"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_mixed_quoted_string_single_inside_double() { - let mut reader = StringReader::from("\"hello 'world'\"".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "hello 'world'"); - assert_eq!(reader.get_read(), "\"hello 'world'\""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_quoted_string_empty_quoted() { - let mut reader = StringReader::from("".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), ""); - assert_eq!(reader.get_read(), ""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_quoted_string_empty_quoted_with_remaining() { - let mut reader = StringReader::from("\"\" hello world".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), ""); - assert_eq!(reader.get_read(), "\"\""); - assert_eq!(reader.remaining(), " hello world"); - } - - #[test] - fn read_quoted_string_with_escaped_quote() { - let mut reader = StringReader::from("\"hello \\\"world\\\"\"".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); - assert_eq!(reader.get_read(), "\"hello \\\"world\\\"\""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_quoted_string_with_escaped_escapes() { - let mut reader = StringReader::from("\"\\\\o/\"".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "\\o/"); - assert_eq!(reader.get_read(), "\"\\\\o/\""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_quoted_string_with_remaining() { - let mut reader = StringReader::from("\"hello world\" foo bar".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); - assert_eq!(reader.get_read(), "\"hello world\""); - assert_eq!(reader.remaining(), " foo bar"); - } - - #[test] - fn read_quoted_string_with_immediate_remaining() { - let mut reader = StringReader::from("\"hello world\"foo bar".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); - assert_eq!(reader.get_read(), "\"hello world\""); - assert_eq!(reader.remaining(), "foo bar"); - } - - #[test] - fn read_quoted_string_no_open() { - let mut reader = StringReader::from("hello world\"".to_string()); - let result = reader.read_quoted_string(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedStartOfQuote); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_quoted_string_no_close() { - let mut reader = StringReader::from("\"hello world".to_string()); - let result = reader.read_quoted_string(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedEndOfQuote); - assert_eq!(e.cursor(), Some(12)); - } - } - - #[test] - fn read_quoted_string_invalid_escape() { - let mut reader = StringReader::from("\"hello\\nworld\"".to_string()); - let result = reader.read_quoted_string(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidEscape { character: 'n' } - ); - assert_eq!(e.cursor(), Some(7)); - } - } - - #[test] - fn read_quoted_string_invalid_quote_escape() { - let mut reader = StringReader::from("'hello\\\"\'world".to_string()); - let result = reader.read_quoted_string(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidEscape { character: '"' } - ); - assert_eq!(e.cursor(), Some(7)); - } - } - - #[test] - fn read_string_no_quotes() { - let mut reader = StringReader::from("hello world".to_string()); - assert_eq!(reader.read_string().unwrap(), "hello"); - assert_eq!(reader.get_read(), "hello"); - assert_eq!(reader.remaining(), " world"); - } - - #[test] - fn read_string_single_quotes() { - let mut reader = StringReader::from("'hello world'".to_string()); - assert_eq!(reader.read_string().unwrap(), "hello world"); - assert_eq!(reader.get_read(), "'hello world'"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_string_double_quotes() { - let mut reader = StringReader::from("\"hello world\"".to_string()); - assert_eq!(reader.read_string().unwrap(), "hello world"); - assert_eq!(reader.get_read(), "\"hello world\""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_int() { - let mut reader = StringReader::from("1234567890".to_string()); - assert_eq!(reader.read_int().unwrap(), 1234567890); - assert_eq!(reader.get_read(), "1234567890"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_int_negative() { - let mut reader = StringReader::from("-1234567890".to_string()); - assert_eq!(reader.read_int().unwrap(), -1234567890); - assert_eq!(reader.get_read(), "-1234567890"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_int_invalid() { - let mut reader = StringReader::from("12.34".to_string()); - let result = reader.read_int(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidInt { - value: "12.34".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_int_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_int(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_int_with_remaining() { - let mut reader = StringReader::from("1234567890 foo bar".to_string()); - assert_eq!(reader.read_int().unwrap(), 1234567890); - assert_eq!(reader.get_read(), "1234567890"); - assert_eq!(reader.remaining(), " foo bar"); - } - - #[test] - fn read_int_with_remaining_immediate() { - let mut reader = StringReader::from("1234567890foo bar".to_string()); - assert_eq!(reader.read_int().unwrap(), 1234567890); - assert_eq!(reader.get_read(), "1234567890"); - assert_eq!(reader.remaining(), "foo bar"); - } - - #[test] - fn read_long() { - let mut reader = StringReader::from("1234567890".to_string()); - assert_eq!(reader.read_long().unwrap(), 1234567890); - assert_eq!(reader.get_read(), "1234567890"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_long_negative() { - let mut reader = StringReader::from("-1234567890".to_string()); - assert_eq!(reader.read_long().unwrap(), -1234567890); - assert_eq!(reader.get_read(), "-1234567890"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_long_invalid() { - let mut reader = StringReader::from("12.34".to_string()); - let result = reader.read_long(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidLong { - value: "12.34".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_long_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_long(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_long_with_remaining() { - let mut reader = StringReader::from("1234567890 foo bar".to_string()); - assert_eq!(reader.read_long().unwrap(), 1234567890); - assert_eq!(reader.get_read(), "1234567890"); - assert_eq!(reader.remaining(), " foo bar"); - } - - #[test] - fn read_long_with_remaining_immediate() { - let mut reader = StringReader::from("1234567890foo bar".to_string()); - assert_eq!(reader.read_long().unwrap(), 1234567890); - assert_eq!(reader.get_read(), "1234567890"); - assert_eq!(reader.remaining(), "foo bar"); - } - - #[test] - fn read_double() { - let mut reader = StringReader::from("123".to_string()); - assert_eq!(reader.read_double().unwrap(), 123.0); - assert_eq!(reader.get_read(), "123"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_double_with_decimal() { - let mut reader = StringReader::from("12.34".to_string()); - assert_eq!(reader.read_double().unwrap(), 12.34); - assert_eq!(reader.get_read(), "12.34"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_double_negative() { - let mut reader = StringReader::from("-123".to_string()); - assert_eq!(reader.read_double().unwrap(), -123.0); - assert_eq!(reader.get_read(), "-123"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_double_invalid() { - let mut reader = StringReader::from("12.34.56".to_string()); - let result = reader.read_double(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidDouble { - value: "12.34.56".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_double_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_double(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_double_with_remaining() { - let mut reader = StringReader::from("12.34 foo bar".to_string()); - assert_eq!(reader.read_double().unwrap(), 12.34); - assert_eq!(reader.get_read(), "12.34"); - assert_eq!(reader.remaining(), " foo bar"); - } - - #[test] - fn read_double_with_remaining_immediate() { - let mut reader = StringReader::from("12.34foo bar".to_string()); - assert_eq!(reader.read_double().unwrap(), 12.34); - assert_eq!(reader.get_read(), "12.34"); - assert_eq!(reader.remaining(), "foo bar"); - } - - #[test] - fn read_float() { - let mut reader = StringReader::from("123".to_string()); - assert_eq!(reader.read_float().unwrap(), 123.0f32); - assert_eq!(reader.get_read(), "123"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_float_with_decimal() { - let mut reader = StringReader::from("12.34".to_string()); - assert_eq!(reader.read_float().unwrap(), 12.34f32); - assert_eq!(reader.get_read(), "12.34"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_float_negative() { - let mut reader = StringReader::from("-123".to_string()); - assert_eq!(reader.read_float().unwrap(), -123.0f32); - assert_eq!(reader.get_read(), "-123"); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_float_invalid() { - let mut reader = StringReader::from("12.34.56".to_string()); - let result = reader.read_float(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidFloat { - value: "12.34.56".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_float_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_float(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_float_with_remaining() { - let mut reader = StringReader::from("12.34 foo bar".to_string()); - assert_eq!(reader.read_float().unwrap(), 12.34f32); - assert_eq!(reader.get_read(), "12.34"); - assert_eq!(reader.remaining(), " foo bar"); - } - - #[test] - fn read_float_with_remaining_immediate() { - let mut reader = StringReader::from("12.34foo bar".to_string()); - assert_eq!(reader.read_float().unwrap(), 12.34f32); - assert_eq!(reader.get_read(), "12.34"); - assert_eq!(reader.remaining(), "foo bar"); - } - - #[test] - fn expect_correct() { - let mut reader = StringReader::from("abc".to_string()); - reader.expect('a').unwrap(); - assert_eq!(reader.cursor(), 1); - } - - #[test] - fn expect_incorrect() { - let mut reader = StringReader::from("bcd".to_string()); - let result = reader.expect('a'); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn expect_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.expect('a'); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_boolean_correct() { - let mut reader = StringReader::from("true".to_string()); - assert_eq!(reader.read_boolean().unwrap(), true); - assert_eq!(reader.get_read(), "true"); - } - - #[test] - fn read_boolean_incorrect() { - let mut reader = StringReader::from("tuesday".to_string()); - let result = reader.read_boolean(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidBool { - value: "tuesday".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_boolean_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_boolean(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool); - assert_eq!(e.cursor(), Some(0)); - } - } -} diff --git a/azalea-brigadier/tests/arguments/bool_argument_type_test.rs b/azalea-brigadier/tests/arguments/bool_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/double_argument_type_test.rs b/azalea-brigadier/tests/arguments/double_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/float_argument_type_test.rs b/azalea-brigadier/tests/arguments/float_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/integer_argument_type_test.rs b/azalea-brigadier/tests/arguments/integer_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/long_argument_type_test.rs b/azalea-brigadier/tests/arguments/long_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/string_argument_type_test.rs b/azalea-brigadier/tests/arguments/string_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/builder/argument_builder_test.rs b/azalea-brigadier/tests/builder/argument_builder_test.rs new file mode 100644 index 00000000..e570c988 --- /dev/null +++ b/azalea-brigadier/tests/builder/argument_builder_test.rs @@ -0,0 +1,75 @@ +use std::rc::Rc; + +use crate::{ + arguments::integer_argument_type::integer, + builder::{literal_argument_builder::literal, required_argument_builder::argument}, +}; + +use super::ArgumentBuilder; + +// public class ArgumentBuilderTest { +// private TestableArgumentBuilder builder; + +// @Before +// public void setUp() throws Exception { +// builder = new TestableArgumentBuilder<>(); +// } + +// @Test +// public void testArguments() throws Exception { +// final RequiredArgumentBuilder argument = argument("bar", integer()); + +// builder.then(argument); + +// assertThat(builder.getArguments(), hasSize(1)); +// assertThat(builder.getArguments(), hasItem((CommandNode) argument.build())); +// } + +#[test] +fn test_arguments() { + let mut builder: ArgumentBuilder<()> = literal("foo"); + + let argument: ArgumentBuilder<()> = argument("bar", integer()); + builder.then(argument.clone()); + assert_eq!(builder.arguments.children.len(), 1); + let built_argument = Rc::new(argument.build()); + assert!(builder + .arguments + .children + .values() + .any(|e| *e.borrow() == *built_argument)); +} + +// @Test +// public void testRedirect() throws Exception { +// final CommandNode target = mock(CommandNode.class); +// builder.redirect(target); +// assertThat(builder.getRedirect(), is(target)); +// } + +// @Test(expected = IllegalStateException.class) +// public void testRedirect_withChild() throws Exception { +// final CommandNode target = mock(CommandNode.class); +// builder.then(literal("foo")); +// builder.redirect(target); +// } + +// @Test(expected = IllegalStateException.class) +// public void testThen_withRedirect() throws Exception { +// final CommandNode target = mock(CommandNode.class); +// builder.redirect(target); +// builder.then(literal("foo")); +// } + +// private static class TestableArgumentBuilder extends ArgumentBuilder> { +// @Override +// protected TestableArgumentBuilder getThis() { +// return this; +// } + +// @Override +// public CommandNode build() { +// return null; +// } +// } +// } diff --git a/azalea-brigadier/tests/builder/literal_argument_builder_test.rs b/azalea-brigadier/tests/builder/literal_argument_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/builder/required_argument_builder_test.rs b/azalea-brigadier/tests/builder/required_argument_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs new file mode 100644 index 00000000..cb33ac73 --- /dev/null +++ b/azalea-brigadier/tests/command_dispatcher_test.rs @@ -0,0 +1,410 @@ +use std::rc::Rc; + +use azalea_brigadier::{ + arguments::integer_argument_type::integer, + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + command_dispatcher::CommandDispatcher, + context::CommandContext, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + string_reader::StringReader, +}; + +#[derive(Debug, PartialEq)] +struct CommandSource {} + +fn input_with_offset(input: &str, offset: usize) -> StringReader { + let mut result: StringReader = input.into(); + result.cursor = offset; + result +} + +#[test] +fn create_and_execute_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute("foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); +} + +#[test] +fn create_and_execute_offset_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute(input_with_offset("/foo", 1), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); +} + +#[test] +fn create_and_merge_commands() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("base").then(literal("foo").executes(|_| 42))); + subject.register(literal("base").then(literal("bar").executes(|_| 42))); + + assert_eq!( + subject + .execute("base foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + assert_eq!( + subject + .execute("base bar".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); +} + +#[test] +fn execute_unknown_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("bar")); + subject.register(literal("baz")); + + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); +} + +#[test] +fn execute_impermissible_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").requires(|_| false)); + + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); +} + +#[test] +fn execute_empty_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("")); + + let execute_result = subject.execute("".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); +} + +#[test] +fn execute_unknown_subcommand() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + let execute_result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); +} + +#[test] +fn execute_incorrect_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42).then(literal("bar"))); + + let execute_result = subject.execute("foo baz".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); +} + +#[test] +fn execute_ambiguous_incorrect_argument() { + let mut subject = CommandDispatcher::new(); + subject.register( + literal("foo") + .executes(|_| 42) + .then(literal("bar")) + .then(literal("baz")), + ); + + let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); +} + +#[test] +fn execute_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(literal("a")) + .then(literal("=").executes(|_| 100)) + .then(literal("c")) + .executes(|_| 42), + ); + + assert_eq!( + subject + .execute("foo =".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); +} + +#[test] +fn parse_incomplete_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(literal("bar").executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); +} + +#[test] +fn parse_incomplete_argument() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); +} + +#[test] +fn execute_ambiguious_parent_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then(argument("right", integer()).then(argument("sub", integer()).executes(|_| 100))), + ); + + assert_eq!( + subject + .execute("test 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); +} + +#[test] +fn execute_ambiguious_parent_subcommand_via_redirect() { + let mut subject = CommandDispatcher::new(); + + let real = subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then(argument("right", integer()).then(argument("sub", integer()).executes(|_| 100))), + ); + + subject.register(literal("redirect").redirect(real)); + + assert_eq!( + subject + .execute("redirect 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); +} + +#[test] +fn execute_redirected_multiple_times() { + let mut subject = CommandDispatcher::new(); + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let root = subject.root.clone(); + let redirect_node = subject.register(literal("redirected").redirect(root.clone())); + + let input = "redirected redirected actual"; + + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let child1 = parse.context.child.clone(); + assert!(child1.is_some()); + assert_eq!(child1.clone().unwrap().range.get(input), "redirected"); + assert_eq!(child1.clone().unwrap().nodes.len(), 1); + assert_eq!(child1.clone().unwrap().root, root); + assert_eq!( + child1.clone().unwrap().nodes[0].range, + child1.clone().unwrap().range + ); + assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node); + + let child2 = child1.unwrap().child.clone(); + assert!(child2.is_some()); + assert_eq!(child2.clone().unwrap().range.get(input), "actual"); + assert_eq!(child2.clone().unwrap().nodes.len(), 1); + assert_eq!(child2.clone().unwrap().root, root); + assert_eq!( + child2.clone().unwrap().nodes[0].range, + child2.clone().unwrap().range + ); + assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); +} + +#[test] +fn execute_redirected() { + let mut subject = CommandDispatcher::new(); + + let source1 = Rc::new(CommandSource {}); + let source2 = Rc::new(CommandSource {}); + + let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { + Ok(vec![source1.clone(), source2.clone()]) + }; + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let redirect_node = + subject.register(literal("redirected").fork(subject.root.clone(), Rc::new(modifier))); + + let input = "redirected actual"; + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let parent = parse.context.child.clone(); + assert!(parent.is_some()); + let parent = parent.unwrap(); + assert_eq!(parent.range.get(input), "actual"); + assert_eq!(parent.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parent.nodes[0].range, parent.range); + assert_eq!(parent.nodes[0].node, concrete_node); + assert_eq!(parent.source, Rc::new(CommandSource {})); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); +} + +#[test] +fn execute_orphaned_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo 5".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(5)); +} + +#[test] +fn execute_invalid_other() { + let mut subject = CommandDispatcher::new(); + + subject.register(literal("w").executes(|_| panic!("This should not run"))); + subject.register(literal("world").executes(|_| 42)); + + assert_eq!( + subject + .execute("world".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); +} + +#[test] +fn parse_no_space_separator() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo$".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(0)); +} + +#[test] +fn execute_invalid_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + // this fails for some reason, i blame mojang + // assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt); + assert_eq!(result.cursor(), Some(4)); +} + +#[test] +fn get_path() { + let mut subject = CommandDispatcher::<()>::new(); + + let bar = literal("bar").build(); + subject.register(literal("foo").then_built(bar.clone())); + + assert_eq!( + subject.get_path(bar), + vec!["foo".to_string(), "bar".to_string()] + ); +} + +#[test] +fn find_node_doesnt_exist() { + let subject = CommandDispatcher::<()>::new(); + + assert_eq!(subject.find_node(&vec!["foo", "bar"]), None) +} diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/azalea-brigadier/tests/command_dispatcher_usages_test.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/azalea-brigadier/tests/command_suggestions_test.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/tests/context/command_context_test.rs b/azalea-brigadier/tests/context/command_context_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/context/parsed_argument_test.rs b/azalea-brigadier/tests/context/parsed_argument_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs new file mode 100644 index 00000000..5008eff8 --- /dev/null +++ b/azalea-brigadier/tests/string_reader_test.rs @@ -0,0 +1,612 @@ +use azalea_brigadier::{exceptions::BuiltInExceptions, string_reader::StringReader}; + +#[test] +fn can_read() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.can_read(), true); + reader.skip(); // 'a' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'b' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'c' + assert_eq!(reader.can_read(), false); +} + +#[test] +fn get_remaining_length() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.remaining_length(), 3); + reader.cursor = 1; + assert_eq!(reader.remaining_length(), 2); + reader.cursor = 2; + assert_eq!(reader.remaining_length(), 1); + reader.cursor = 3; + assert_eq!(reader.remaining_length(), 0); +} + +#[test] +fn can_read_length() { + let reader = StringReader::from("abc".to_string()); + assert_eq!(reader.can_read_length(1), true); + assert_eq!(reader.can_read_length(2), true); + assert_eq!(reader.can_read_length(3), true); + assert_eq!(reader.can_read_length(4), false); + assert_eq!(reader.can_read_length(5), false); +} + +#[test] +fn peek() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.peek(), 'a'); + assert_eq!(reader.cursor(), 0); + reader.cursor = 2; + assert_eq!(reader.peek(), 'c'); + assert_eq!(reader.cursor(), 2); +} + +#[test] +fn peek_length() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.peek_offset(0), 'a'); + assert_eq!(reader.peek_offset(2), 'c'); + assert_eq!(reader.cursor(), 0); + reader.cursor = 1; + assert_eq!(reader.peek_offset(1), 'c'); + assert_eq!(reader.cursor(), 1); +} + +#[test] +fn read() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.read(), 'a'); + assert_eq!(reader.read(), 'b'); + assert_eq!(reader.read(), 'c'); + assert_eq!(reader.cursor(), 3); +} + +#[test] +fn skip() { + let mut reader = StringReader::from("abc".to_string()); + reader.skip(); + assert_eq!(reader.cursor(), 1); +} + +#[test] +fn get_remaining() { + let mut reader = StringReader::from("Hello!".to_string()); + assert_eq!(reader.remaining(), "Hello!"); + reader.cursor = 3; + assert_eq!(reader.remaining(), "lo!"); + reader.cursor = 6; + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn get_read() { + let mut reader = StringReader::from("Hello!".to_string()); + assert_eq!(reader.get_read(), ""); + reader.cursor = 3; + assert_eq!(reader.get_read(), "Hel"); + reader.cursor = 6; + assert_eq!(reader.get_read(), "Hello!"); +} + +#[test] +fn skip_whitespace_none() { + let mut reader = StringReader::from("Hello!".to_string()); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 0); +} + +#[test] +fn skip_whitespace_mixed() { + let mut reader = StringReader::from(" \t \t\nHello!".to_string()); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 5); +} + +#[test] +fn skip_whitespace_empty() { + let mut reader = StringReader::from("".to_string()); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 0); +} + +#[test] +fn read_unquoted_string() { + let mut reader = StringReader::from("hello world".to_string()); + assert_eq!(reader.read_unquoted_string(), "hello"); + assert_eq!(reader.get_read(), "hello"); + assert_eq!(reader.remaining(), " world"); +} + +#[test] +fn read_unquoted_string_empty() { + let mut reader = StringReader::from("".to_string()); + assert_eq!(reader.read_unquoted_string(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_unquoted_string_empty_with_remaining() { + let mut reader = StringReader::from(" hello world".to_string()); + assert_eq!(reader.read_unquoted_string(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), " hello world"); +} + +#[test] +fn read_quoted_string() { + let mut reader = StringReader::from("\"hello world\"".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_single_quoted_string() { + let mut reader = StringReader::from("'hello world'".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "'hello world'"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_mixed_quoted_string_double_inside_single() { + let mut reader = StringReader::from("'hello \"world\"'".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); + assert_eq!(reader.get_read(), "'hello \"world\"'"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_mixed_quoted_string_single_inside_double() { + let mut reader = StringReader::from("\"hello 'world'\"".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "hello 'world'"); + assert_eq!(reader.get_read(), "\"hello 'world'\""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_quoted_string_empty_quoted() { + let mut reader = StringReader::from("".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_quoted_string_empty_quoted_with_remaining() { + let mut reader = StringReader::from("\"\" hello world".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), ""); + assert_eq!(reader.get_read(), "\"\""); + assert_eq!(reader.remaining(), " hello world"); +} + +#[test] +fn read_quoted_string_with_escaped_quote() { + let mut reader = StringReader::from("\"hello \\\"world\\\"\"".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); + assert_eq!(reader.get_read(), "\"hello \\\"world\\\"\""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_quoted_string_with_escaped_escapes() { + let mut reader = StringReader::from("\"\\\\o/\"".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "\\o/"); + assert_eq!(reader.get_read(), "\"\\\\o/\""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_quoted_string_with_remaining() { + let mut reader = StringReader::from("\"hello world\" foo bar".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), " foo bar"); +} + +#[test] +fn read_quoted_string_with_immediate_remaining() { + let mut reader = StringReader::from("\"hello world\"foo bar".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), "foo bar"); +} + +#[test] +fn read_quoted_string_no_open() { + let mut reader = StringReader::from("hello world\"".to_string()); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedStartOfQuote); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_quoted_string_no_close() { + let mut reader = StringReader::from("\"hello world".to_string()); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedEndOfQuote); + assert_eq!(e.cursor(), Some(12)); + } +} + +#[test] +fn read_quoted_string_invalid_escape() { + let mut reader = StringReader::from("\"hello\\nworld\"".to_string()); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: 'n' } + ); + assert_eq!(e.cursor(), Some(7)); + } +} + +#[test] +fn read_quoted_string_invalid_quote_escape() { + let mut reader = StringReader::from("'hello\\\"\'world".to_string()); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: '"' } + ); + assert_eq!(e.cursor(), Some(7)); + } +} + +#[test] +fn read_string_no_quotes() { + let mut reader = StringReader::from("hello world".to_string()); + assert_eq!(reader.read_string().unwrap(), "hello"); + assert_eq!(reader.get_read(), "hello"); + assert_eq!(reader.remaining(), " world"); +} + +#[test] +fn read_string_single_quotes() { + let mut reader = StringReader::from("'hello world'".to_string()); + assert_eq!(reader.read_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "'hello world'"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_string_double_quotes() { + let mut reader = StringReader::from("\"hello world\"".to_string()); + assert_eq!(reader.read_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_int() { + let mut reader = StringReader::from("1234567890".to_string()); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_int_negative() { + let mut reader = StringReader::from("-1234567890".to_string()); + assert_eq!(reader.read_int().unwrap(), -1234567890); + assert_eq!(reader.get_read(), "-1234567890"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_int_invalid() { + let mut reader = StringReader::from("12.34".to_string()); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidInt { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_int_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_int_with_remaining() { + let mut reader = StringReader::from("1234567890 foo bar".to_string()); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), " foo bar"); +} + +#[test] +fn read_int_with_remaining_immediate() { + let mut reader = StringReader::from("1234567890foo bar".to_string()); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), "foo bar"); +} + +#[test] +fn read_long() { + let mut reader = StringReader::from("1234567890".to_string()); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_long_negative() { + let mut reader = StringReader::from("-1234567890".to_string()); + assert_eq!(reader.read_long().unwrap(), -1234567890); + assert_eq!(reader.get_read(), "-1234567890"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_long_invalid() { + let mut reader = StringReader::from("12.34".to_string()); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidLong { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_long_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_long_with_remaining() { + let mut reader = StringReader::from("1234567890 foo bar".to_string()); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), " foo bar"); +} + +#[test] +fn read_long_with_remaining_immediate() { + let mut reader = StringReader::from("1234567890foo bar".to_string()); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), "foo bar"); +} + +#[test] +fn read_double() { + let mut reader = StringReader::from("123".to_string()); + assert_eq!(reader.read_double().unwrap(), 123.0); + assert_eq!(reader.get_read(), "123"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_double_with_decimal() { + let mut reader = StringReader::from("12.34".to_string()); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_double_negative() { + let mut reader = StringReader::from("-123".to_string()); + assert_eq!(reader.read_double().unwrap(), -123.0); + assert_eq!(reader.get_read(), "-123"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_double_invalid() { + let mut reader = StringReader::from("12.34.56".to_string()); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidDouble { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_double_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_double_with_remaining() { + let mut reader = StringReader::from("12.34 foo bar".to_string()); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), " foo bar"); +} + +#[test] +fn read_double_with_remaining_immediate() { + let mut reader = StringReader::from("12.34foo bar".to_string()); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), "foo bar"); +} + +#[test] +fn read_float() { + let mut reader = StringReader::from("123".to_string()); + assert_eq!(reader.read_float().unwrap(), 123.0f32); + assert_eq!(reader.get_read(), "123"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_float_with_decimal() { + let mut reader = StringReader::from("12.34".to_string()); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_float_negative() { + let mut reader = StringReader::from("-123".to_string()); + assert_eq!(reader.read_float().unwrap(), -123.0f32); + assert_eq!(reader.get_read(), "-123"); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_float_invalid() { + let mut reader = StringReader::from("12.34.56".to_string()); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidFloat { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_float_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_float_with_remaining() { + let mut reader = StringReader::from("12.34 foo bar".to_string()); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), " foo bar"); +} + +#[test] +fn read_float_with_remaining_immediate() { + let mut reader = StringReader::from("12.34foo bar".to_string()); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), "foo bar"); +} + +#[test] +fn expect_correct() { + let mut reader = StringReader::from("abc".to_string()); + reader.expect('a').unwrap(); + assert_eq!(reader.cursor(), 1); +} + +#[test] +fn expect_incorrect() { + let mut reader = StringReader::from("bcd".to_string()); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn expect_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_boolean_correct() { + let mut reader = StringReader::from("true".to_string()); + assert_eq!(reader.read_boolean().unwrap(), true); + assert_eq!(reader.get_read(), "true"); +} + +#[test] +fn read_boolean_incorrect() { + let mut reader = StringReader::from("tuesday".to_string()); + let result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidBool { + value: "tuesday".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_boolean_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool); + assert_eq!(e.cursor(), Some(0)); + } +} diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs b/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/abstract_command_node_test.rs b/azalea-brigadier/tests/tree/abstract_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/argument_command_node_test.rs b/azalea-brigadier/tests/tree/argument_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/literal_command_node_test.rs b/azalea-brigadier/tests/tree/literal_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/root_command_node_test.rs b/azalea-brigadier/tests/tree/root_command_node_test.rs new file mode 100644 index 00000000..e69de29b From 751098b636c9aee54b9ca7a465fdaa769f10be4d Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 22:38:53 -0500 Subject: [PATCH 50/83] start working on declare commands packet --- Cargo.lock | 1 + README.md | 6 +- azalea-client/src/connect.rs | 3 + azalea-protocol/Cargo.toml | 1 + azalea-protocol/src/mc_buf/read.rs | 1 - .../clientbound_declare_commands_packet.rs | 119 +++++++++++++----- azalea-protocol/src/packets/game/mod.rs | 9 ++ 7 files changed, 106 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67259fef..8d94c26c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,7 @@ dependencies = [ "async-recursion", "async-trait", "azalea-auth", + "azalea-brigadier", "azalea-chat", "azalea-core", "azalea-nbt", diff --git a/README.md b/README.md index 3ae4b307..ed6ed46f 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ I named this Azalea because it sounds like a cool word and this is a cool librar ## Goals -- Bypass most anticheats -- Only support the latest Minecraft version - Do everything a vanilla client can do -- Be fast - Be easy to use +- Bypass most/all anticheats +- Support the latest Minecraft version +- Be fast diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 7b1da525..bc45a121 100644 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -73,6 +73,9 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { GamePacket::ClientboundChangeDifficultyPacket(p) => { println!("Got difficulty packet {:?}", p); } + GamePacket::ClientboundDeclareCommandsPacket(p) => { + println!("Got declare commands packet {:?}", p); + } }, Err(e) => { println!("Error: {:?}", e); diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index ff3bd9d4..37df8697 100644 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -10,6 +10,7 @@ async-compression = {version = "^0.3.8", features = ["tokio", "zlib"]} async-recursion = "^0.3.2" async-trait = "0.1.51" azalea-auth = {path = "../azalea-auth"} +azalea-brigadier = {path = "../azalea-brigadier"} azalea-chat = {path = "../azalea-chat"} azalea-core = {path = "../azalea-core"} azalea-nbt = {path = "../azalea-nbt"} diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index 0fa1d099..20b69238 100644 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use azalea_core::{ difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, }; -use num_traits::FromPrimitive; use tokio::io::{AsyncRead, AsyncReadExt}; use super::MAX_STRING_LENGTH; diff --git a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs index 1bcf0dd4..1403630d 100644 --- a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs @@ -1,37 +1,96 @@ -// use std::hash::Hash; +use std::hash::Hash; -// use crate::mc_buf::Readable; +use async_trait::async_trait; +use tokio::io::AsyncRead; -// use super::LoginPacket; +use crate::mc_buf::{McBufReadable, Readable}; -// #[derive(Hash, Clone, Debug)] -// pub struct ClientboundDeclareCommandsPacket { -// pub root: RootCommandNode, -// pub public_key: Vec, -// pub nonce: Vec, -// } +use super::GamePacket; -// impl ClientboundHelloPacket { -// pub fn get(self) -> LoginPacket { -// LoginPacket::ClientboundHelloPacket(self) -// } +#[derive(Hash, Clone, Debug)] +pub struct ClientboundDeclareCommandsPacket { + pub entries: Vec, + pub root_index: i32, +} -// pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { -// panic!("ClientboundHelloPacket::write not implemented") -// } +impl ClientboundDeclareCommandsPacket { + pub fn get(self) -> GamePacket { + GamePacket::ClientboundDeclareCommandsPacket(self) + } -// pub async fn read( -// buf: &mut T, -// ) -> Result { -// let server_id = buf.read_utf_with_len(20).await?; -// let public_key = buf.read_byte_array().await?; -// let nonce = buf.read_byte_array().await?; + pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { + panic!("ClientboundDeclareCommandsPacket::write not implemented") + } -// Ok(ClientboundHelloPacket { -// server_id, -// public_key, -// nonce, -// } -// .get()) -// } -// } + pub async fn read( + buf: &mut T, + ) -> Result { + 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?; + nodes.push(node); + } + let root_index = buf.read_varint().await?; + Ok(GamePacket::ClientboundDeclareCommandsPacket( + ClientboundDeclareCommandsPacket { + entries: nodes, + root_index, + }, + )) + } +} + +#[derive(Hash, Debug, Clone)] +pub struct BrigadierNodeStub {} + +// azalea_brigadier::tree::CommandNode +#[async_trait] +impl McBufReadable for BrigadierNodeStub { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let flags = u8::read_into(buf).await?; + + 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); + + if node_type == 2 { + let name = buf.read_utf().await?; + println!("name: {}", name); + + let resource_location = if has_suggestions_type { + Some(buf.read_resource_location().await?) + } else { + None + }; + println!( + "node_type=2, flags={}, name={}, resource_location={:?}", + flags, name, resource_location + ); + return Ok(BrigadierNodeStub {}); + } + if node_type == 1 { + let name = buf.read_utf().await?; + println!("node_type=1, flags={}, name={}", flags, name); + return Ok(BrigadierNodeStub {}); + } + println!("node_type={}, flags={}", node_type, flags); + Ok(BrigadierNodeStub {}) + // return Err("Unknown node type".to_string()); + } +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 4efe72fb..1fd47132 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -1,5 +1,6 @@ pub mod clientbound_change_difficulty_packet; pub mod clientbound_custom_payload_packet; +pub mod clientbound_declare_commands_packet; pub mod clientbound_login_packet; pub mod clientbound_update_view_distance_packet; @@ -22,6 +23,9 @@ where ClientboundChangeDifficultyPacket( clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, ), + ClientboundDeclareCommandsPacket( + clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket, + ), } #[async_trait] @@ -32,6 +36,7 @@ impl ProtocolPacket for GamePacket { GamePacket::ClientboundCustomPayloadPacket(_packet) => 0x18, GamePacket::ClientboundLoginPacket(_packet) => 0x26, GamePacket::ClientboundUpdateViewDistancePacket(_packet) => 0x4a, + GamePacket::ClientboundDeclareCommandsPacket(_packet) => 0x12, } } @@ -41,6 +46,7 @@ impl ProtocolPacket for GamePacket { GamePacket::ClientboundCustomPayloadPacket(packet) => packet.write(buf), GamePacket::ClientboundLoginPacket(packet) => packet.write(buf), GamePacket::ClientboundUpdateViewDistancePacket(packet) => packet.write(buf), + GamePacket::ClientboundDeclareCommandsPacket(packet) => packet.write(buf), } } @@ -63,6 +69,9 @@ impl ProtocolPacket for GamePacket { 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket ::read(buf) .await?, + 0x12 => clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket + ::read(buf) + .await?, // _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), _ => panic!("Unknown ServerToClient game packet id: {}", id), }, From f60426cd5a8a276adcb88988cef38d27f48de912 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 00:48:13 -0500 Subject: [PATCH 51/83] start adding declare_state_packets --- azalea-protocol/packet-macros/src/lib.rs | 135 ++++++++++++++++++++-- azalea-protocol/src/packets/game/mod.rs | 139 +++++++++++++---------- 2 files changed, 201 insertions(+), 73 deletions(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 957515c4..0e6ad0e1 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -1,10 +1,14 @@ -use quote::{quote, ToTokens}; -use syn::{self, parse_macro_input, DeriveInput, FieldsNamed}; +use std::collections::{BTreeMap, HashMap}; -fn as_packet_derive( - input: proc_macro::TokenStream, - state: proc_macro2::TokenStream, -) -> proc_macro::TokenStream { +use proc_macro::TokenStream; +use quote::{quote, ToTokens}; +use syn::{ + self, braced, + parse::{Parse, ParseStream, Result}, + parse_macro_input, DeriveInput, Expr, FieldsNamed, Ident, LitInt, Token, Type, Visibility, +}; + +fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); let fields = match data { @@ -99,21 +103,132 @@ fn as_packet_derive( } #[proc_macro_derive(GamePacket, attributes(varint))] -pub fn derive_game_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn derive_game_packet(input: TokenStream) -> TokenStream { as_packet_derive(input, quote! {crate::packets::game::GamePacket}) } #[proc_macro_derive(HandshakePacket, attributes(varint))] -pub fn derive_handshake_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn derive_handshake_packet(input: TokenStream) -> TokenStream { as_packet_derive(input, quote! {crate::packets::handshake::HandshakePacket}) } #[proc_macro_derive(LoginPacket, attributes(varint))] -pub fn derive_login_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn derive_login_packet(input: TokenStream) -> TokenStream { as_packet_derive(input, quote! {crate::packets::login::LoginPacket}) } #[proc_macro_derive(StatusPacket, attributes(varint))] -pub fn derive_status_packet(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn derive_status_packet(input: TokenStream) -> TokenStream { as_packet_derive(input, quote! {crate::packets::status::StatusPacket}) } + +#[derive(Debug)] +struct PacketIdPair { + id: u32, + module: Ident, + name: Ident, +} +#[derive(Debug)] +struct PacketIdMap { + packets: Vec, +} + +impl Parse for PacketIdMap { + fn parse(input: ParseStream) -> Result { + let mut packets = vec![]; + loop { + // 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, + // 0x0e + let packet_id: LitInt = match input.parse() { + Ok(i) => i, + Err(_) => break, + }; + let packet_id = packet_id.base10_parse::()?; + // : + input.parse::()?; + // clientbound_change_difficulty_packet + let module: Ident = input.parse()?; + // :: + input.parse::()?; + // ClientboundChangeDifficultyPacket + let name: Ident = input.parse()?; + input.parse::()?; + + packets.push(PacketIdPair { + id: packet_id, + module, + name, + }); + } + + Ok(PacketIdMap { packets }) + } +} + +#[derive(Debug)] +struct DeclareStatePackets { + name: Ident, + serverbound: PacketIdMap, + clientbound: PacketIdMap, +} + +impl Parse for DeclareStatePackets { + fn parse(input: ParseStream) -> Result { + let name = input.parse()?; + input.parse::()?; + + let serverbound_token: Ident = input.parse()?; + if serverbound_token != "Serverbound" { + return Err(syn::Error::new( + serverbound_token.span(), + "Expected `Serverbound`", + )); + } + input.parse::]>()?; + let content; + braced!(content in input); + let serverbound = content.parse()?; + + input.parse::()?; + + let clientbound_token: Ident = input.parse()?; + if clientbound_token != "Clientbound" { + return Err(syn::Error::new( + clientbound_token.span(), + "Expected `Clientbound`", + )); + } + input.parse::]>()?; + let content; + braced!(content in input); + let clientbound = content.parse()?; + + Ok(DeclareStatePackets { + name, + serverbound, + clientbound, + }) + } +} +#[proc_macro] +pub fn declare_state_packets(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeclareStatePackets); + + let name = input.name; + + let mut enum_contents = quote!(); + for PacketIdPair { id, module, name } in input.serverbound.packets { + enum_contents.extend(quote! {#name(#module::#name)}); + } + + quote! { + #[derive(Clone, Debug)] + pub enum #name + where + Self: Sized, + { + #enum_contents + } + } + .into() +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 4efe72fb..cb07cf97 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -6,70 +6,83 @@ pub mod clientbound_update_view_distance_packet; use super::ProtocolPacket; use crate::connect::PacketFlow; use async_trait::async_trait; +use packet_macros::declare_state_packets; -#[derive(Clone, Debug)] -pub enum GamePacket -where - Self: Sized, -{ - ClientboundLoginPacket(clientbound_login_packet::ClientboundLoginPacket), - ClientboundUpdateViewDistancePacket( - clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, - ), - ClientboundCustomPayloadPacket( - clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, - ), - ClientboundChangeDifficultyPacket( - clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, - ), -} - -#[async_trait] -impl ProtocolPacket for GamePacket { - fn id(&self) -> u32 { - match self { - GamePacket::ClientboundChangeDifficultyPacket(_packet) => 0x0e, - GamePacket::ClientboundCustomPayloadPacket(_packet) => 0x18, - GamePacket::ClientboundLoginPacket(_packet) => 0x26, - GamePacket::ClientboundUpdateViewDistancePacket(_packet) => 0x4a, - } +declare_state_packets!( + GamePacket, + // no serverbound packets implemented yet + Serverbound => {}, + Clientbound => { + 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, + 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, + 0x26: clientbound_login_packet::ClientboundLoginPacket, + 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, } +); - fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - match self { - GamePacket::ClientboundChangeDifficultyPacket(packet) => packet.write(buf), - GamePacket::ClientboundCustomPayloadPacket(packet) => packet.write(buf), - GamePacket::ClientboundLoginPacket(packet) => packet.write(buf), - GamePacket::ClientboundUpdateViewDistancePacket(packet) => packet.write(buf), - } - } +// #[derive(Clone, Debug)] +// pub enum GamePacket +// where +// Self: Sized, +// { +// ClientboundLoginPacket(clientbound_login_packet::ClientboundLoginPacket), +// ClientboundUpdateViewDistancePacket( +// clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, +// ), +// ClientboundCustomPayloadPacket( +// clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, +// ), +// ClientboundChangeDifficultyPacket( +// clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, +// ), +// } - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read( - id: u32, - flow: &PacketFlow, - buf: &mut T, - ) -> Result - where - Self: Sized, - { - Ok(match flow { - PacketFlow::ServerToClient => match id { - 0x0e => clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket - ::read(buf) - .await?, - 0x18 => clientbound_custom_payload_packet::ClientboundCustomPayloadPacket::read(buf).await?, - 0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?, - 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket - ::read(buf) - .await?, - // _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), - _ => panic!("Unknown ServerToClient game packet id: {}", id), - }, - PacketFlow::ClientToServer => match id { - // 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, - _ => return Err(format!("Unknown ClientToServer game packet id: {}", id)), - }, - }) - } -} +// #[async_trait] +// impl ProtocolPacket for GamePacket { +// fn id(&self) -> u32 { +// match self { +// GamePacket::ClientboundChangeDifficultyPacket(_packet) => 0x0e, +// GamePacket::ClientboundCustomPayloadPacket(_packet) => 0x18, +// GamePacket::ClientboundLoginPacket(_packet) => 0x26, +// GamePacket::ClientboundUpdateViewDistancePacket(_packet) => 0x4a, +// } +// } + +// fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { +// match self { +// GamePacket::ClientboundChangeDifficultyPacket(packet) => packet.write(buf), +// GamePacket::ClientboundCustomPayloadPacket(packet) => packet.write(buf), +// GamePacket::ClientboundLoginPacket(packet) => packet.write(buf), +// GamePacket::ClientboundUpdateViewDistancePacket(packet) => packet.write(buf), +// } +// } + +// /// Read a packet by its id, ConnectionProtocol, and flow +// async fn read( +// id: u32, +// flow: &PacketFlow, +// buf: &mut T, +// ) -> Result +// where +// Self: Sized, +// { +// Ok(match flow { +// PacketFlow::ServerToClient => match id { +// 0x0e => clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket +// ::read(buf) +// .await?, +// 0x18 => clientbound_custom_payload_packet::ClientboundCustomPayloadPacket::read(buf).await?, +// 0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?, +// 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket +// ::read(buf) +// .await?, +// // _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), +// _ => panic!("Unknown ServerToClient game packet id: {}", id), +// }, +// PacketFlow::ClientToServer => match id { +// // 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, +// _ => return Err(format!("Unknown ClientToServer game packet id: {}", id)), +// }, +// }) +// } +// } From c04a28621e53129c03f8aee8cbee89556abf137f Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 00:53:51 -0500 Subject: [PATCH 52/83] start adding more stuff --- azalea-protocol/packet-macros/src/lib.rs | 71 +++++++++++++++++++++++- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 0e6ad0e1..8442b921 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -214,21 +214,86 @@ impl Parse for DeclareStatePackets { pub fn declare_state_packets(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeclareStatePackets); - let name = input.name; + let state_name = input.name; let mut enum_contents = quote!(); + let mut id_match_contents = quote!(); + let mut write_match_contents = quote!(); for PacketIdPair { id, module, name } in input.serverbound.packets { - enum_contents.extend(quote! {#name(#module::#name)}); + enum_contents.extend(quote! { + #name(#module::#name) + }); + id_match_contents.extend(quote! { + #state_name::#name(_packet) => #id + }); + write_match_contents.extend(quote! { + #state_name::#name(packet) => packet.write(buf) + }); + } + for PacketIdPair { id, module, name } in input.clientbound.packets { + enum_contents.extend(quote! { + #name(#module::#name) + }); + id_match_contents.extend(quote! { + #state_name::#name(_packet) => #id + }); + write_match_contents.extend(quote! { + #state_name::#name(packet) => packet.write(buf) + }); } quote! { #[derive(Clone, Debug)] - pub enum #name + pub enum #state_name where Self: Sized, { #enum_contents } + + #[async_trait] + impl ProtocolPacket for GamePacket { + fn id(&self) -> u32 { + match self { + #id_match_contents + } + } + + fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + match self { + #write_match_contents + } + } + + /// Read a packet by its id, ConnectionProtocol, and flow + async fn read( + id: u32, + flow: &PacketFlow, + buf: &mut T, + ) -> Result + where + Self: Sized, + { + Ok(match flow { + PacketFlow::ServerToClient => match id { + 0x0e => clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket + ::read(buf) + .await?, + 0x18 => clientbound_custom_payload_packet::ClientboundCustomPayloadPacket::read(buf).await?, + 0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?, + 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket + ::read(buf) + .await?, + // _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), + _ => panic!("Unknown ServerToClient game packet id: {}", id), + }, + PacketFlow::ClientToServer => match id { + // 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, + _ => return Err(format!("Unknown ClientToServer game packet id: {}", id)), + }, + }) + } + } } .into() } From 9633874d23f3baa8e5d5c33f3fd51ae6aa880a88 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 18:53:13 -0500 Subject: [PATCH 53/83] finish declare_state_packets implementation --- azalea-protocol/packet-macros/src/lib.rs | 35 ++++++------- azalea-protocol/src/packets/game/mod.rs | 67 ------------------------ 2 files changed, 17 insertions(+), 85 deletions(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 8442b921..ab062550 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -1,11 +1,9 @@ -use std::collections::{BTreeMap, HashMap}; - use proc_macro::TokenStream; use quote::{quote, ToTokens}; use syn::{ self, braced, parse::{Parse, ParseStream, Result}, - parse_macro_input, DeriveInput, Expr, FieldsNamed, Ident, LitInt, Token, Type, Visibility, + parse_macro_input, DeriveInput, FieldsNamed, Ident, LitInt, Token, }; fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream { @@ -215,10 +213,13 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeclareStatePackets); let state_name = input.name; + let state_name_litstr = syn::LitStr::new(&state_name.to_string(), state_name.span()); let mut enum_contents = quote!(); let mut id_match_contents = quote!(); let mut write_match_contents = quote!(); + let mut serverbound_read_match_contents = quote!(); + let mut clientbound_read_match_contents = quote!(); for PacketIdPair { id, module, name } in input.serverbound.packets { enum_contents.extend(quote! { #name(#module::#name) @@ -229,16 +230,22 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { write_match_contents.extend(quote! { #state_name::#name(packet) => packet.write(buf) }); + serverbound_read_match_contents.extend(quote! { + #id => #module::#name::read(buf).await?, + }); } for PacketIdPair { id, module, name } in input.clientbound.packets { enum_contents.extend(quote! { - #name(#module::#name) + #name(#module::#name), }); id_match_contents.extend(quote! { - #state_name::#name(_packet) => #id + #state_name::#name(_packet) => #id, }); write_match_contents.extend(quote! { - #state_name::#name(packet) => packet.write(buf) + #state_name::#name(packet) => packet.write(buf), + }); + clientbound_read_match_contents.extend(quote! { + #id => #module::#name::read(buf).await?, }); } @@ -276,20 +283,12 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { { Ok(match flow { PacketFlow::ServerToClient => match id { - 0x0e => clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket - ::read(buf) - .await?, - 0x18 => clientbound_custom_payload_packet::ClientboundCustomPayloadPacket::read(buf).await?, - 0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?, - 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket - ::read(buf) - .await?, - // _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), - _ => panic!("Unknown ServerToClient game packet id: {}", id), + #serverbound_read_match_contents + _ => panic!("Unknown ServerToClient {} packet id: {}", #state_name_litstr, id), }, PacketFlow::ClientToServer => match id { - // 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, - _ => return Err(format!("Unknown ClientToServer game packet id: {}", id)), + #clientbound_read_match_contents + _ => return Err(format!("Unknown ClientToServer {} packet id: {}", #state_name_litstr, id)), }, }) } diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index cb07cf97..2affd49e 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -19,70 +19,3 @@ declare_state_packets!( 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, } ); - -// #[derive(Clone, Debug)] -// pub enum GamePacket -// where -// Self: Sized, -// { -// ClientboundLoginPacket(clientbound_login_packet::ClientboundLoginPacket), -// ClientboundUpdateViewDistancePacket( -// clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, -// ), -// ClientboundCustomPayloadPacket( -// clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, -// ), -// ClientboundChangeDifficultyPacket( -// clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, -// ), -// } - -// #[async_trait] -// impl ProtocolPacket for GamePacket { -// fn id(&self) -> u32 { -// match self { -// GamePacket::ClientboundChangeDifficultyPacket(_packet) => 0x0e, -// GamePacket::ClientboundCustomPayloadPacket(_packet) => 0x18, -// GamePacket::ClientboundLoginPacket(_packet) => 0x26, -// GamePacket::ClientboundUpdateViewDistancePacket(_packet) => 0x4a, -// } -// } - -// fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { -// match self { -// GamePacket::ClientboundChangeDifficultyPacket(packet) => packet.write(buf), -// GamePacket::ClientboundCustomPayloadPacket(packet) => packet.write(buf), -// GamePacket::ClientboundLoginPacket(packet) => packet.write(buf), -// GamePacket::ClientboundUpdateViewDistancePacket(packet) => packet.write(buf), -// } -// } - -// /// Read a packet by its id, ConnectionProtocol, and flow -// async fn read( -// id: u32, -// flow: &PacketFlow, -// buf: &mut T, -// ) -> Result -// where -// Self: Sized, -// { -// Ok(match flow { -// PacketFlow::ServerToClient => match id { -// 0x0e => clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket -// ::read(buf) -// .await?, -// 0x18 => clientbound_custom_payload_packet::ClientboundCustomPayloadPacket::read(buf).await?, -// 0x26 => clientbound_login_packet::ClientboundLoginPacket::read(buf).await?, -// 0x4a => clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket -// ::read(buf) -// .await?, -// // _ => return Err(format!("Unknown ServerToClient game packet id: {}", id)), -// _ => panic!("Unknown ServerToClient game packet id: {}", id), -// }, -// PacketFlow::ClientToServer => match id { -// // 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, -// _ => return Err(format!("Unknown ClientToServer game packet id: {}", id)), -// }, -// }) -// } -// } From cbdab27cb555e38b39fe0f600ff945090ec10dcb Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 19:17:36 -0500 Subject: [PATCH 54/83] add declare_state_packets to the other states --- azalea-protocol/packet-macros/src/lib.rs | 18 ++-- azalea-protocol/src/packets/game/mod.rs | 4 - azalea-protocol/src/packets/handshake/mod.rs | 51 ++---------- azalea-protocol/src/packets/login/mod.rs | 83 +++---------------- .../clientbound_status_response_packet.rs | 2 +- azalea-protocol/src/packets/status/mod.rs | 69 ++------------- 6 files changed, 38 insertions(+), 189 deletions(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index ab062550..3cc3677a 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -222,13 +222,13 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { let mut clientbound_read_match_contents = quote!(); for PacketIdPair { id, module, name } in input.serverbound.packets { enum_contents.extend(quote! { - #name(#module::#name) + #name(#module::#name), }); id_match_contents.extend(quote! { - #state_name::#name(_packet) => #id + #state_name::#name(_packet) => #id, }); write_match_contents.extend(quote! { - #state_name::#name(packet) => packet.write(buf) + #state_name::#name(packet) => packet.write(buf), }); serverbound_read_match_contents.extend(quote! { #id => #module::#name::read(buf).await?, @@ -258,8 +258,8 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { #enum_contents } - #[async_trait] - impl ProtocolPacket for GamePacket { + #[async_trait::async_trait] + impl crate::packets::ProtocolPacket for #state_name { fn id(&self) -> u32 { match self { #id_match_contents @@ -275,18 +275,18 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { /// Read a packet by its id, ConnectionProtocol, and flow async fn read( id: u32, - flow: &PacketFlow, + flow: &crate::connect::PacketFlow, buf: &mut T, - ) -> Result + ) -> Result<#state_name, String> where Self: Sized, { Ok(match flow { - PacketFlow::ServerToClient => match id { + crate::connect::PacketFlow::ServerToClient => match id { #serverbound_read_match_contents _ => panic!("Unknown ServerToClient {} packet id: {}", #state_name_litstr, id), }, - PacketFlow::ClientToServer => match id { + crate::connect::PacketFlow::ClientToServer => match id { #clientbound_read_match_contents _ => return Err(format!("Unknown ClientToServer {} packet id: {}", #state_name_litstr, id)), }, diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 2affd49e..a4304d9f 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -3,14 +3,10 @@ pub mod clientbound_custom_payload_packet; pub mod clientbound_login_packet; pub mod clientbound_update_view_distance_packet; -use super::ProtocolPacket; -use crate::connect::PacketFlow; -use async_trait::async_trait; use packet_macros::declare_state_packets; declare_state_packets!( GamePacket, - // no serverbound packets implemented yet Serverbound => {}, Clientbound => { 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, diff --git a/azalea-protocol/src/packets/handshake/mod.rs b/azalea-protocol/src/packets/handshake/mod.rs index 17465fca..88d9939b 100644 --- a/azalea-protocol/src/packets/handshake/mod.rs +++ b/azalea-protocol/src/packets/handshake/mod.rs @@ -1,48 +1,11 @@ pub mod client_intention_packet; -use async_trait::async_trait; +use packet_macros::declare_state_packets; -use crate::connect::PacketFlow; - -use super::ProtocolPacket; - -#[derive(Clone, Debug)] -pub enum HandshakePacket -where - Self: Sized, -{ - ClientIntentionPacket(client_intention_packet::ClientIntentionPacket), -} - -#[async_trait] -impl ProtocolPacket for HandshakePacket { - fn id(&self) -> u32 { - match self { - HandshakePacket::ClientIntentionPacket(_packet) => 0x00, - } +declare_state_packets!( + HandshakePacket, + Serverbound => {}, + Clientbound => { + 0x00: client_intention_packet::ClientIntentionPacket, } - - fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - match self { - HandshakePacket::ClientIntentionPacket(packet) => packet.write(buf), - } - } - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read( - id: u32, - flow: &PacketFlow, - buf: &mut T, - ) -> Result - where - Self: Sized, - { - match flow { - PacketFlow::ServerToClient => Err("HandshakePacket::read not implemented".to_string()), - PacketFlow::ClientToServer => match id { - 0x00 => Ok(client_intention_packet::ClientIntentionPacket::read(buf).await?), - _ => Err(format!("Unknown ClientToServer status packet id: {}", id)), - }, - } - } -} +); diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs index b1f61746..ef5f15c1 100644 --- a/azalea-protocol/src/packets/login/mod.rs +++ b/azalea-protocol/src/packets/login/mod.rs @@ -4,76 +4,17 @@ pub mod clientbound_hello_packet; pub mod clientbound_login_compression_packet; pub mod serverbound_hello_packet; -use super::ProtocolPacket; -use crate::connect::PacketFlow; -use async_trait::async_trait; +use packet_macros::declare_state_packets; -#[derive(Clone, Debug)] -pub enum LoginPacket -where - Self: Sized, -{ - ClientboundCustomQueryPacket(clientbound_custom_query_packet::ClientboundCustomQueryPacket), - ClientboundGameProfilePacket(clientbound_game_profile_packet::ClientboundGameProfilePacket), - ClientboundHelloPacket(clientbound_hello_packet::ClientboundHelloPacket), - ClientboundLoginCompressionPacket( - clientbound_login_compression_packet::ClientboundLoginCompressionPacket, - ), - ServerboundHelloPacket(serverbound_hello_packet::ServerboundHelloPacket), -} - -#[async_trait] -impl ProtocolPacket for LoginPacket { - fn id(&self) -> u32 { - match self { - LoginPacket::ClientboundCustomQueryPacket(_packet) => 0x04, - LoginPacket::ClientboundGameProfilePacket(_packet) => 0x02, - LoginPacket::ClientboundHelloPacket(_packet) => 0x01, - LoginPacket::ClientboundLoginCompressionPacket(_packet) => 0x03, - LoginPacket::ServerboundHelloPacket(_packet) => 0x00, - } +declare_state_packets!( + LoginPacket, + Serverbound => { + 0x00: serverbound_hello_packet::ServerboundHelloPacket, + }, + Clientbound => { + 0x00: clientbound_hello_packet::ClientboundHelloPacket, + 0x02: clientbound_game_profile_packet::ClientboundGameProfilePacket, + 0x03: clientbound_login_compression_packet::ClientboundLoginCompressionPacket, + 0x04: clientbound_custom_query_packet::ClientboundCustomQueryPacket, } - - fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - match self { - LoginPacket::ClientboundCustomQueryPacket(packet) => packet.write(buf), - LoginPacket::ClientboundGameProfilePacket(packet) => packet.write(buf), - LoginPacket::ClientboundHelloPacket(packet) => packet.write(buf), - LoginPacket::ClientboundLoginCompressionPacket(packet) => packet.write(buf), - LoginPacket::ServerboundHelloPacket(packet) => packet.write(buf), - } - } - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read( - id: u32, - flow: &PacketFlow, - buf: &mut T, - ) -> Result - where - Self: Sized, - { - Ok(match flow { - PacketFlow::ServerToClient => match id { - 0x01 => clientbound_hello_packet::ClientboundHelloPacket::read(buf).await?, - 0x02 => { - clientbound_game_profile_packet::ClientboundGameProfilePacket::read(buf).await? - } - 0x04 => { - clientbound_custom_query_packet::ClientboundCustomQueryPacket::read(buf).await? - } - 0x03 => { - clientbound_login_compression_packet::ClientboundLoginCompressionPacket::read( - buf, - ) - .await? - } - _ => return Err(format!("Unknown ServerToClient login packet id: {}", id)), - }, - PacketFlow::ClientToServer => match id { - 0x00 => serverbound_hello_packet::ServerboundHelloPacket::read(buf).await?, - _ => return Err(format!("Unknown ClientToServer login packet id: {}", id)), - }, - }) - } -} +); diff --git a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs index 58f5b701..884cf408 100644 --- a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs +++ b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs @@ -36,7 +36,7 @@ pub struct ClientboundStatusResponsePacket { impl ClientboundStatusResponsePacket { pub fn get(self) -> StatusPacket { - StatusPacket::ClientboundStatusResponsePacket(Box::new(self)) + StatusPacket::ClientboundStatusResponsePacket(self) } pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { diff --git a/azalea-protocol/src/packets/status/mod.rs b/azalea-protocol/src/packets/status/mod.rs index 31fedfb9..56aa577e 100644 --- a/azalea-protocol/src/packets/status/mod.rs +++ b/azalea-protocol/src/packets/status/mod.rs @@ -1,65 +1,14 @@ pub mod clientbound_status_response_packet; pub mod serverbound_status_request_packet; -use async_trait::async_trait; +use packet_macros::declare_state_packets; -use crate::connect::PacketFlow; - -use super::ProtocolPacket; - -#[derive(Clone, Debug)] -pub enum StatusPacket -where - Self: Sized, -{ - ServerboundStatusRequestPacket( - serverbound_status_request_packet::ServerboundStatusRequestPacket, - ), - ClientboundStatusResponsePacket( - Box, - ), -} - -#[async_trait] -impl ProtocolPacket for StatusPacket { - fn id(&self) -> u32 { - match self { - StatusPacket::ServerboundStatusRequestPacket(_packet) => 0x00, - StatusPacket::ClientboundStatusResponsePacket(_packet) => 0x00, - } +declare_state_packets!( + StatusPacket, + Serverbound => { + 0x00: serverbound_status_request_packet::ServerboundStatusRequestPacket, + }, + Clientbound => { + 0x00: clientbound_status_response_packet::ClientboundStatusResponsePacket, } - - fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - match self { - StatusPacket::ServerboundStatusRequestPacket(packet) => packet.write(buf), - StatusPacket::ClientboundStatusResponsePacket(packet) => packet.write(buf), - } - } - - /// Read a packet by its id, ConnectionProtocol, and flow - async fn read( - id: u32, - flow: &PacketFlow, - buf: &mut T, - ) -> Result - where - Self: Sized, - { - match flow { - PacketFlow::ServerToClient => match id { - 0x00 => Ok( - clientbound_status_response_packet::ClientboundStatusResponsePacket::read(buf) - .await?, - ), - _ => Err(format!("Unknown ServerToClient status packet id: {}", id)), - }, - PacketFlow::ClientToServer => match id { - 0x00 => Ok( - serverbound_status_request_packet::ServerboundStatusRequestPacket::read(buf) - .await?, - ), - _ => Err(format!("Unknown ClientToServer status packet id: {}", id)), - }, - } - } -} +); From bc22faf3b0ba6cc8cc8de6276ff998690ba87bab Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 20:33:17 -0500 Subject: [PATCH 55/83] remove a couple of imports --- azalea-protocol/src/mc_buf/read.rs | 1 - azalea-protocol/src/mc_buf/write.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index 0fa1d099..20b69238 100644 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use azalea_core::{ difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, }; -use num_traits::FromPrimitive; use tokio::io::{AsyncRead, AsyncReadExt}; use super::MAX_STRING_LENGTH; diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index fd9faeb4..26c0ef35 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -3,7 +3,6 @@ use azalea_core::{ difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, }; use byteorder::{BigEndian, WriteBytesExt}; -use num_traits::FromPrimitive; use std::io::Write; use super::MAX_STRING_LENGTH; From a45eb6deb27b8623c3d3ba9b5e21cc638272f5c8 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 20:37:42 -0500 Subject: [PATCH 56/83] allow trailing commas in declare_state_packets --- azalea-protocol/packet-macros/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 3cc3677a..d7f54d84 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -150,13 +150,16 @@ impl Parse for PacketIdMap { input.parse::()?; // ClientboundChangeDifficultyPacket let name: Ident = input.parse()?; - input.parse::()?; packets.push(PacketIdPair { id: packet_id, module, name, }); + + if input.parse::().is_err() { + break; + } } Ok(PacketIdMap { packets }) From a3fad4765b7ef2077fde0b5c87a5cf657f9f6584 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 21:03:10 -0500 Subject: [PATCH 57/83] reorder some packets --- azalea-protocol/src/packets/game/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 904b38c8..2dbf7f24 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -11,9 +11,9 @@ declare_state_packets!( Serverbound => {}, Clientbound => { 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, + 0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket, 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, 0x26: clientbound_login_packet::ClientboundLoginPacket, - 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, - 0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket + 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket } ); From cafa4dd76fecc9e331f35145e10539e1f5ac85f6 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 21:24:42 -0500 Subject: [PATCH 58/83] Fix declare_state_packets --- azalea-client/src/ping.rs | 2 +- azalea-protocol/packet-macros/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/azalea-client/src/ping.rs b/azalea-client/src/ping.rs index 87ccdf66..8ecff7ca 100644 --- a/azalea-client/src/ping.rs +++ b/azalea-client/src/ping.rs @@ -38,7 +38,7 @@ pub async fn ping_server( let packet = conn.read().await.unwrap(); match packet { - StatusPacket::ClientboundStatusResponsePacket(p) => Ok(*p), + StatusPacket::ClientboundStatusResponsePacket(p) => Ok(p), _ => Err("Invalid packet type".to_string()), } } diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index d7f54d84..bdb83871 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -286,11 +286,11 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { { Ok(match flow { crate::connect::PacketFlow::ServerToClient => match id { - #serverbound_read_match_contents + #clientbound_read_match_contents _ => panic!("Unknown ServerToClient {} packet id: {}", #state_name_litstr, id), }, crate::connect::PacketFlow::ClientToServer => match id { - #clientbound_read_match_contents + #serverbound_read_match_contents _ => return Err(format!("Unknown ClientToServer {} packet id: {}", #state_name_litstr, id)), }, }) From 5306d99e33b0247f7deb28da6c68fccc3c49c2ba Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 20:37:42 -0500 Subject: [PATCH 59/83] allow trailing commas in declare_state_packets --- azalea-protocol/packet-macros/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 3cc3677a..d7f54d84 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -150,13 +150,16 @@ impl Parse for PacketIdMap { input.parse::()?; // ClientboundChangeDifficultyPacket let name: Ident = input.parse()?; - input.parse::()?; packets.push(PacketIdPair { id: packet_id, module, name, }); + + if input.parse::().is_err() { + break; + } } Ok(PacketIdMap { packets }) From df318dba73750ce38afea675854a8ce6ccbc65e5 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Apr 2022 21:24:42 -0500 Subject: [PATCH 60/83] Fix declare_state_packets --- azalea-client/src/ping.rs | 2 +- azalea-protocol/packet-macros/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/azalea-client/src/ping.rs b/azalea-client/src/ping.rs index 87ccdf66..8ecff7ca 100644 --- a/azalea-client/src/ping.rs +++ b/azalea-client/src/ping.rs @@ -38,7 +38,7 @@ pub async fn ping_server( let packet = conn.read().await.unwrap(); match packet { - StatusPacket::ClientboundStatusResponsePacket(p) => Ok(*p), + StatusPacket::ClientboundStatusResponsePacket(p) => Ok(p), _ => Err("Invalid packet type".to_string()), } } diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index d7f54d84..bdb83871 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -286,11 +286,11 @@ pub fn declare_state_packets(input: TokenStream) -> TokenStream { { Ok(match flow { crate::connect::PacketFlow::ServerToClient => match id { - #serverbound_read_match_contents + #clientbound_read_match_contents _ => panic!("Unknown ServerToClient {} packet id: {}", #state_name_litstr, id), }, crate::connect::PacketFlow::ClientToServer => match id { - #clientbound_read_match_contents + #serverbound_read_match_contents _ => return Err(format!("Unknown ClientToServer {} packet id: {}", #state_name_litstr, id)), }, }) From c5f10af09d1d2fee640b946da9ec5ca1bcd1e62d Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 20 Apr 2022 15:24:25 +0000 Subject: [PATCH 61/83] write some readmes --- azalea-chat/README.md | 3 +++ azalea-client/README.md | 3 +++ azalea-protocol/README.md | 3 +++ 3 files changed, 9 insertions(+) create mode 100644 azalea-chat/README.md create mode 100644 azalea-client/README.md create mode 100644 azalea-protocol/README.md diff --git a/azalea-chat/README.md b/azalea-chat/README.md new file mode 100644 index 00000000..2bc9d418 --- /dev/null +++ b/azalea-chat/README.md @@ -0,0 +1,3 @@ +# Azalea Chat + +Parse Minecraft chat messages. diff --git a/azalea-client/README.md b/azalea-client/README.md new file mode 100644 index 00000000..328eb0d5 --- /dev/null +++ b/azalea-client/README.md @@ -0,0 +1,3 @@ +# Azalea Client + +A lower level Minecraft bot library. diff --git a/azalea-protocol/README.md b/azalea-protocol/README.md new file mode 100644 index 00000000..6063ed39 --- /dev/null +++ b/azalea-protocol/README.md @@ -0,0 +1,3 @@ +# Azalea Protocol + +Sent and receive Minecraft packets. From 8bd97c6c96f2c3c1ef4edfc1ff9d59f9af664972 Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 20 Apr 2022 16:28:31 +0000 Subject: [PATCH 62/83] add player abilities packet --- azalea-client/src/connect.rs | 4 ++++ azalea-protocol/src/mc_buf/read.rs | 19 +++++++++++++++++++ azalea-protocol/src/mc_buf/write.rs | 12 ++++++++++++ .../clientbound_player_abilities_packet.rs | 11 +++++++++++ azalea-protocol/src/packets/game/mod.rs | 2 ++ 5 files changed, 48 insertions(+) create mode 100644 azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index bc45a121..f19c67c2 100644 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -76,6 +76,10 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { GamePacket::ClientboundDeclareCommandsPacket(p) => { println!("Got declare commands packet {:?}", p); } + + GamePacket::ClientboundPlayerAbilitiesPacket(p) => { + println!("Got player abilities packet {:?}", p); + } }, Err(e) => { println!("Error: {:?}", e); diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index 20b69238..e036643e 100644 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -24,6 +24,7 @@ pub trait Readable { async fn read_long(&mut self) -> Result; async fn read_resource_location(&mut self) -> Result; async fn read_short(&mut self) -> Result; + async fn read_float(&mut self) -> Result; } #[async_trait] @@ -189,6 +190,13 @@ where Err(_) => Err("Error reading short".to_string()), } } + + async fn read_float(&mut self) -> Result { + match AsyncReadExt::read_f32(self).await { + Ok(r) => Ok(r), + Err(_) => Err("Error reading float".to_string()), + } + } } #[async_trait] @@ -362,6 +370,17 @@ impl McBufReadable for i8 { } } +// f32 +#[async_trait] +impl McBufReadable for f32 { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_float().await + } +} + // GameType #[async_trait] impl McBufReadable for GameType { diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index 26c0ef35..07605b16 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -41,6 +41,7 @@ pub trait Writable { &mut self, location: &ResourceLocation, ) -> Result<(), std::io::Error>; + fn write_float(&mut self, n: f32) -> Result<(), std::io::Error>; } #[async_trait] @@ -147,6 +148,10 @@ impl Writable for Vec { WriteBytesExt::write_i64::(self, n) } + fn write_float(&mut self, n: f32) -> Result<(), std::io::Error> { + WriteBytesExt::write_f32::(self, n) + } + fn write_resource_location( &mut self, location: &ResourceLocation, @@ -264,6 +269,13 @@ impl McBufWritable for i8 { } } +// i8 +impl McBufWritable for f32 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_float(*self) + } +} + // GameType impl McBufWritable for GameType { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { diff --git a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs new file mode 100644 index 00000000..6136fff5 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs @@ -0,0 +1,11 @@ +// i don't know the actual name of this packet, i couldn't find it in the source code + +use packet_macros::GamePacket; + +#[derive(Clone, Debug, GamePacket)] +pub struct ClientboundPlayerAbilitiesPacket { + pub flags: u8, + pub flying_speed: f32, + /// Used for the fov + pub walking_speed: f32, +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 2dbf7f24..38630a0e 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -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_login_packet; +pub mod clientbound_player_abilities_packet; pub mod clientbound_update_view_distance_packet; use packet_macros::declare_state_packets; @@ -14,6 +15,7 @@ declare_state_packets!( 0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket, 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, 0x26: clientbound_login_packet::ClientboundLoginPacket, + 0x32: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket } ); From 298d30ad08d5efe8c94f1865909bafc220a8cfac Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 20 Apr 2022 18:03:42 +0000 Subject: [PATCH 63/83] make PlayerAbilitiesFlags its own thing --- .../clientbound_player_abilities_packet.rs | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs index 6136fff5..402923c9 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs @@ -1,11 +1,59 @@ // i don't know the actual name of this packet, i couldn't find it in the source code +use crate::mc_buf::{McBufReadable, McBufWritable, Readable}; +use async_trait::async_trait; use packet_macros::GamePacket; +use tokio::io::AsyncRead; #[derive(Clone, Debug, GamePacket)] pub struct ClientboundPlayerAbilitiesPacket { - pub flags: u8, + pub flags: PlayerAbilitiesFlags, pub flying_speed: f32, /// Used for the fov pub walking_speed: f32, } + +#[derive(Clone, Debug)] +pub struct PlayerAbilitiesFlags { + pub invulnerable: bool, + pub flying: bool, + pub can_fly: bool, + pub instant_break: bool, +} + +// Difficulty +#[async_trait] +impl McBufReadable for PlayerAbilitiesFlags { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let byte = buf.read_byte().await?; + Ok(PlayerAbilitiesFlags { + invulnerable: byte & 1 != 0, + flying: byte & 2 != 0, + can_fly: byte & 4 != 0, + instant_break: byte & 8 != 0, + }) + } +} + +// Difficulty +impl McBufWritable for PlayerAbilitiesFlags { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + let mut byte = 0; + if self.invulnerable { + byte = byte | 1; + } + if self.flying { + byte = byte | 2; + } + if self.can_fly { + byte = byte | 4; + } + if self.instant_break { + byte = byte | 8; + } + u8::write_into(&byte, buf) + } +} From 2c848ebaa59781462aba6da428c8dfcc51dbacf8 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 21 Apr 2022 15:25:46 +0000 Subject: [PATCH 64/83] Set carried item and update tags packets --- azalea-client/src/connect.rs | 7 ++++++- azalea-protocol/packet-macros/src/lib.rs | 7 +++---- azalea-protocol/src/mc_buf/write.rs | 4 ++-- .../clientbound_player_abilities_packet.rs | 2 -- azalea-protocol/src/packets/game/mod.rs | 6 +++++- .../serverbound_status_request_packet.rs | 21 ++----------------- bot/src/main.rs | 6 ++++-- 7 files changed, 22 insertions(+), 31 deletions(-) diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index f19c67c2..3f0a8560 100644 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -76,10 +76,15 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { GamePacket::ClientboundDeclareCommandsPacket(p) => { println!("Got declare commands packet {:?}", p); } - GamePacket::ClientboundPlayerAbilitiesPacket(p) => { println!("Got player abilities packet {:?}", p); } + GamePacket::ClientboundSetCarriedItemPacket(p) => { + println!("Got set carried item packet {:?}", p); + } + GamePacket::ClientboundUpdateTagsPacket(p) => { + println!("Got update tags packet {:?}", p); + } }, Err(e) => { println!("Error: {:?}", e); diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index bdb83871..45df7e81 100644 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -75,7 +75,7 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke .collect::>(); let read_field_names = named.iter().map(|f| &f.ident).collect::>(); - let gen = quote! { + quote! { impl #ident { pub fn get(self) -> #state { #state::#ident(self) @@ -95,9 +95,8 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke }.get()) } } - }; - - gen.into() + } + .into() } #[proc_macro_derive(GamePacket, attributes(varint))] diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index 07605b16..9330dccb 100644 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -14,7 +14,7 @@ pub trait Writable { F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, T: Sized, Self: Sized; - fn write_int_id_list(&mut self, list: Vec) -> Result<(), std::io::Error>; + fn write_int_id_list(&mut self, list: &Vec) -> Result<(), std::io::Error>; fn write_map( &mut self, map: Vec<(KT, VT)>, @@ -58,7 +58,7 @@ impl Writable for Vec { Ok(()) } - fn write_int_id_list(&mut self, list: Vec) -> Result<(), std::io::Error> { + fn write_int_id_list(&mut self, list: &Vec) -> Result<(), std::io::Error> { self.write_list(&list, |buf, n| buf.write_varint(*n)) } diff --git a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs index 402923c9..f4f528cf 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs @@ -21,7 +21,6 @@ pub struct PlayerAbilitiesFlags { pub instant_break: bool, } -// Difficulty #[async_trait] impl McBufReadable for PlayerAbilitiesFlags { async fn read_into(buf: &mut R) -> Result @@ -38,7 +37,6 @@ impl McBufReadable for PlayerAbilitiesFlags { } } -// Difficulty impl McBufWritable for PlayerAbilitiesFlags { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { let mut byte = 0; diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 38630a0e..e150606c 100644 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -3,6 +3,8 @@ pub mod clientbound_custom_payload_packet; pub mod clientbound_declare_commands_packet; pub mod clientbound_login_packet; pub mod clientbound_player_abilities_packet; +pub mod clientbound_set_carried_item_packet; +pub mod clientbound_update_tags_packet; pub mod clientbound_update_view_distance_packet; use packet_macros::declare_state_packets; @@ -16,6 +18,8 @@ declare_state_packets!( 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, 0x26: clientbound_login_packet::ClientboundLoginPacket, 0x32: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, - 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket + 0x48: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, + 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, + 0x67: clientbound_update_tags_packet::ClientboundUpdateTagsPacket } ); diff --git a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs index af98f7cb..e77687ec 100644 --- a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs +++ b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs @@ -1,22 +1,5 @@ +use packet_macros::StatusPacket; use std::hash::Hash; -use super::StatusPacket; - -#[derive(Hash, Clone, Debug)] +#[derive(Clone, Debug, StatusPacket)] pub struct ServerboundStatusRequestPacket {} - -impl ServerboundStatusRequestPacket { - pub fn get(self) -> StatusPacket { - StatusPacket::ServerboundStatusRequestPacket(self) - } - - pub fn write(&self, _buf: &mut Vec) -> Result<(), std::io::Error> { - panic!("ServerboundStatusRequestPacket::write not implemented") - } - - pub async fn read( - _buf: &mut T, - ) -> Result { - Err("ServerboundStatusRequestPacket::read not implemented".to_string()) - } -} diff --git a/bot/src/main.rs b/bot/src/main.rs index 1041e765..957b3cbc 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -1,4 +1,5 @@ use azalea_client::connect::join_server; +use azalea_client::ping::ping_server; #[tokio::main] async fn main() { @@ -6,7 +7,8 @@ async fn main() { let address = "95.111.249.143:10000"; // let address = "localhost:63482"; - let _response = join_server(&address.try_into().unwrap()).await.unwrap(); - // println!("{}", response.description.to_ansi(None)); + let response = ping_server(&address.try_into().unwrap()).await.unwrap(); + // let _response = join_server(&address.try_into().unwrap()).await.unwrap(); + println!("{}", response.description.to_ansi(None)); println!("connected"); } From 3beb58189bdb9e129cbde3a5f250d525f3b8694f Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 21 Apr 2022 17:24:20 +0000 Subject: [PATCH 65/83] tokio features in az-nbt --- azalea-nbt/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml index 5d28d136..89a1efd8 100644 --- a/azalea-nbt/Cargo.toml +++ b/azalea-nbt/Cargo.toml @@ -16,7 +16,7 @@ tokio = "^1.15.0" [dev-dependencies] criterion = {version = "^0.3.5", features = ["html_reports", "async_tokio"]} -tokio = {version = "^1.15.0", features = ["fs"]} +tokio = {version = "^1.15.0", features = ["fs", "io-util", "macros"]} [profile.release] lto = true From 03d14e13f2f06528a377c4508aefb2a7a044c193 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 21 Apr 2022 18:08:33 +0000 Subject: [PATCH 66/83] make benchmarks work again --- azalea-nbt/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml index 89a1efd8..d4817233 100644 --- a/azalea-nbt/Cargo.toml +++ b/azalea-nbt/Cargo.toml @@ -16,11 +16,14 @@ tokio = "^1.15.0" [dev-dependencies] criterion = {version = "^0.3.5", features = ["html_reports", "async_tokio"]} -tokio = {version = "^1.15.0", features = ["fs", "io-util", "macros"]} +tokio = {version = "^1.15.0", features = ["fs", "io-util", "macros", "rt", "rt-multi-thread"]} [profile.release] lto = true +[profile.bench] +debug = true + [[bench]] harness = false name = "my_benchmark" From c31c6fa7d003bc8440c749063c109b58669bdb80 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 00:10:24 +0000 Subject: [PATCH 67/83] change to not ping --- azalea-client/src/connect.rs | 2 +- bot/src/main.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 3f0a8560..7e6dba51 100644 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -87,7 +87,7 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { } }, Err(e) => { - println!("Error: {:?}", e); + panic!("Error: {:?}", e); } } } diff --git a/bot/src/main.rs b/bot/src/main.rs index 957b3cbc..011c7d0d 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -1,14 +1,15 @@ -use azalea_client::connect::join_server; -use azalea_client::ping::ping_server; - #[tokio::main] async fn main() { println!("Hello, world!"); let address = "95.111.249.143:10000"; // let address = "localhost:63482"; - let response = ping_server(&address.try_into().unwrap()).await.unwrap(); - // let _response = join_server(&address.try_into().unwrap()).await.unwrap(); - println!("{}", response.description.to_ansi(None)); + // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) + // .await + // .unwrap(); + // println!("{}", response.description.to_ansi(None)); + let _response = azalea_client::connect::join_server(&address.try_into().unwrap()) + .await + .unwrap(); println!("connected"); } From 7cdd4171453ead8645b082f0cf5879f7d1a39660 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 00:13:45 +0000 Subject: [PATCH 68/83] add files --- .../clientbound_set_carried_item_packet.rs | 7 ++ .../game/clientbound_update_tags_packet.rs | 105 ++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs diff --git a/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs new file mode 100644 index 00000000..4f0bf575 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs @@ -0,0 +1,7 @@ +use packet_macros::GamePacket; + +/// Sent to change the player's slot selection. +#[derive(Clone, Debug, GamePacket)] +pub struct ClientboundSetCarriedItemPacket { + pub slot: u8, +} diff --git a/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs new file mode 100644 index 00000000..a2f17370 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs @@ -0,0 +1,105 @@ +use std::collections::HashMap; + +use async_trait::async_trait; +use azalea_core::resource_location::ResourceLocation; +use packet_macros::GamePacket; +use tokio::io::AsyncRead; + +use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; + +#[derive(Clone, Debug, GamePacket)] +pub struct ClientboundUpdateTagsPacket { + pub tags: HashMap>, +} + +#[derive(Clone, Debug)] +pub struct Tags { + pub name: ResourceLocation, + pub elements: Vec, +} + +#[async_trait] +impl McBufReadable for HashMap> { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + println!("reading tags!"); + let length = buf.read_varint().await? as usize; + println!("length: {}", length); + let mut data = HashMap::with_capacity(length); + for _ in 0..length { + let tag_type = buf.read_resource_location().await?; + println!("read tag type {}", tag_type); + let tags_count = buf.read_varint().await? as usize; + let mut tags_vec = Vec::with_capacity(tags_count); + for _ in 0..tags_count { + let tags = Tags::read_into(buf).await?; + tags_vec.push(tags); + } + println!("tags: {} {:?}", tag_type, tags_vec); + data.insert(tag_type, tags_vec); + } + Ok(data) + } +} + +impl McBufWritable for HashMap> { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(self.len() as i32)?; + for (k, v) in self { + k.write_into(buf)?; + v.write_into(buf)?; + } + Ok(()) + } +} + +#[async_trait] +impl McBufReadable for Vec { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let tags_count = buf.read_varint().await? as usize; + let mut tags_vec = Vec::with_capacity(tags_count); + for _ in 0..tags_count { + let tags = Tags::read_into(buf).await?; + tags_vec.push(tags); + } + Ok(tags_vec) + } +} + +impl McBufWritable for Vec { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_varint(self.len() as i32)?; + for tag in self { + tag.write_into(buf)?; + } + Ok(()) + } +} + +#[async_trait] +impl McBufReadable for Tags { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + println!("reading tags."); + let name = buf.read_resource_location().await?; + println!("tags name: {}", name); + let elements = buf.read_int_id_list().await?; + println!("elements: {:?}", elements); + Ok(Tags { name, elements }) + } +} + +impl McBufWritable for Tags { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + self.name.write_into(buf)?; + buf.write_int_id_list(&self.elements)?; + Ok(()) + } +} From 248f752748a0033db7f8242ee0ecd73ea8ce8ec9 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 04:33:58 +0000 Subject: [PATCH 69/83] simplify error handling --- .gitignore | 0 .gitpod.yml | 0 .vscode/settings.json | 0 Cargo.lock | 0 Cargo.toml | 0 README.md | 0 azalea-auth/Cargo.toml | 0 azalea-auth/src/game_profile.rs | 0 azalea-auth/src/lib.rs | 0 azalea-brigadier/Cargo.toml | 0 azalea-brigadier/README.md | 0 .../src/arguments/argument_type.rs | 0 .../src/arguments/integer_argument_type.rs | 0 azalea-brigadier/src/arguments/mod.rs | 0 .../src/builder/argument_builder.rs | 0 .../src/builder/literal_argument_builder.rs | 0 azalea-brigadier/src/builder/mod.rs | 0 .../src/builder/required_argument_builder.rs | 0 azalea-brigadier/src/command_dispatcher.rs | 0 .../src/context/command_context.rs | 0 .../src/context/command_context_builder.rs | 0 azalea-brigadier/src/context/mod.rs | 0 .../src/context/parsed_argument.rs | 0 .../src/context/parsed_command_node.rs | 0 azalea-brigadier/src/context/string_range.rs | 0 .../src/exceptions/builtin_exceptions.rs | 0 .../exceptions/command_syntax_exception.rs | 0 azalea-brigadier/src/exceptions/mod.rs | 0 azalea-brigadier/src/lib.rs | 0 azalea-brigadier/src/message.rs | 0 azalea-brigadier/src/modifier.rs | 0 azalea-brigadier/src/parse_results.rs | 0 azalea-brigadier/src/string_reader.rs | 0 azalea-brigadier/src/tree/mod.rs | 0 .../arguments/bool_argument_type_test.rs | 0 .../arguments/double_argument_type_test.rs | 0 .../arguments/float_argument_type_test.rs | 0 .../arguments/integer_argument_type_test.rs | 0 .../arguments/long_argument_type_test.rs | 0 .../arguments/string_argument_type_test.rs | 0 .../tests/builder/argument_builder_test.rs | 0 .../builder/literal_argument_builder_test.rs | 0 .../builder/required_argument_builder_test.rs | 0 .../tests/command_dispatcher_test.rs | 0 .../tests/command_dispatcher_usages_test.rs | 0 .../tests/command_suggestions_test.rs | 0 .../tests/context/command_context_test.rs | 0 .../tests/context/parsed_argument_test.rs | 0 ...amic_command_syntax_exception_type_test.rs | 0 ...mple_command_syntax_exception_type_test.rs | 0 azalea-brigadier/tests/string_reader_test.rs | 0 .../tests/suggestion/suggestion_test.rs | 0 .../suggestion/suggestions_builder_test.rs | 0 .../tests/suggestion/suggestions_test.rs | 0 .../tests/tree/abstract_command_node_test.rs | 0 .../tests/tree/argument_command_node_test.rs | 0 .../tests/tree/literal_command_node_test.rs | 0 .../tests/tree/root_command_node_test.rs | 0 azalea-chat/Cargo.toml | 0 azalea-chat/README.md | 0 azalea-chat/src/base_component.rs | 0 azalea-chat/src/component.rs | 0 azalea-chat/src/events.rs | 0 azalea-chat/src/lib.rs | 0 azalea-chat/src/style.rs | 0 azalea-chat/src/text_component.rs | 0 azalea-chat/src/translatable_component.rs | 0 azalea-chat/tests/integration_test.rs | 0 azalea-client/Cargo.toml | 0 azalea-client/README.md | 0 azalea-client/src/connect.rs | 0 azalea-client/src/crypt.rs | 0 azalea-client/src/lib.rs | 0 azalea-client/src/ping.rs | 0 azalea-core/Cargo.toml | 0 azalea-core/src/difficulty.rs | 0 azalea-core/src/game_type.rs | 0 azalea-core/src/lib.rs | 0 azalea-core/src/resource_location.rs | 0 azalea-core/src/serializable_uuid.rs | 0 azalea-nbt/Cargo.toml | 0 azalea-nbt/README.md | 0 azalea-nbt/benches/my_benchmark.rs | 0 azalea-nbt/src/decode.rs | 34 +++++++++--------- azalea-nbt/src/encode.rs | 0 azalea-nbt/src/error.rs | 11 ++++++ azalea-nbt/src/lib.rs | 0 azalea-nbt/src/tag.rs | 0 azalea-nbt/tests/bigtest.nbt | Bin azalea-nbt/tests/complex_player.dat | Bin azalea-nbt/tests/hello_world.nbt | Bin azalea-nbt/tests/inttest.nbt | Bin azalea-nbt/tests/level.dat | Bin azalea-nbt/tests/simple_player.dat | Bin azalea-nbt/tests/stringtest.nbt | Bin azalea-nbt/tests/tests.rs | 0 azalea-protocol/Cargo.toml | 0 azalea-protocol/README.md | 0 azalea-protocol/packet-macros/Cargo.toml | 0 azalea-protocol/packet-macros/src/lib.rs | 0 azalea-protocol/src/connect.rs | 0 azalea-protocol/src/lib.rs | 0 azalea-protocol/src/mc_buf/mod.rs | 0 azalea-protocol/src/mc_buf/read.rs | 0 azalea-protocol/src/mc_buf/write.rs | 0 .../clientbound_change_difficulty_packet.rs | 0 .../game/clientbound_custom_payload_packet.rs | 0 .../clientbound_declare_commands_packet.rs | 0 .../packets/game/clientbound_login_packet.rs | 0 .../clientbound_player_abilities_packet.rs | 0 .../clientbound_set_carried_item_packet.rs | 0 .../game/clientbound_update_tags_packet.rs | 0 ...clientbound_update_view_distance_packet.rs | 0 azalea-protocol/src/packets/game/mod.rs | 0 .../handshake/client_intention_packet.rs | 0 azalea-protocol/src/packets/handshake/mod.rs | 0 .../login/clientbound_custom_query_packet.rs | 0 .../login/clientbound_game_profile_packet.rs | 0 .../packets/login/clientbound_hello_packet.rs | 0 .../clientbound_login_compression_packet.rs | 0 azalea-protocol/src/packets/login/mod.rs | 0 .../packets/login/serverbound_hello_packet.rs | 0 azalea-protocol/src/packets/mod.rs | 0 .../clientbound_status_response_packet.rs | 0 azalea-protocol/src/packets/status/mod.rs | 0 .../serverbound_status_request_packet.rs | 0 azalea-protocol/src/read.rs | 0 azalea-protocol/src/resolver.rs | 0 azalea-protocol/src/write.rs | 0 bot/Cargo.toml | 0 bot/src/main.rs | 0 131 files changed, 28 insertions(+), 17 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .gitpod.yml mode change 100644 => 100755 .vscode/settings.json mode change 100644 => 100755 Cargo.lock mode change 100644 => 100755 Cargo.toml mode change 100644 => 100755 README.md mode change 100644 => 100755 azalea-auth/Cargo.toml mode change 100644 => 100755 azalea-auth/src/game_profile.rs mode change 100644 => 100755 azalea-auth/src/lib.rs mode change 100644 => 100755 azalea-brigadier/Cargo.toml mode change 100644 => 100755 azalea-brigadier/README.md mode change 100644 => 100755 azalea-brigadier/src/arguments/argument_type.rs mode change 100644 => 100755 azalea-brigadier/src/arguments/integer_argument_type.rs mode change 100644 => 100755 azalea-brigadier/src/arguments/mod.rs mode change 100644 => 100755 azalea-brigadier/src/builder/argument_builder.rs mode change 100644 => 100755 azalea-brigadier/src/builder/literal_argument_builder.rs mode change 100644 => 100755 azalea-brigadier/src/builder/mod.rs mode change 100644 => 100755 azalea-brigadier/src/builder/required_argument_builder.rs mode change 100644 => 100755 azalea-brigadier/src/command_dispatcher.rs mode change 100644 => 100755 azalea-brigadier/src/context/command_context.rs mode change 100644 => 100755 azalea-brigadier/src/context/command_context_builder.rs mode change 100644 => 100755 azalea-brigadier/src/context/mod.rs mode change 100644 => 100755 azalea-brigadier/src/context/parsed_argument.rs mode change 100644 => 100755 azalea-brigadier/src/context/parsed_command_node.rs mode change 100644 => 100755 azalea-brigadier/src/context/string_range.rs mode change 100644 => 100755 azalea-brigadier/src/exceptions/builtin_exceptions.rs mode change 100644 => 100755 azalea-brigadier/src/exceptions/command_syntax_exception.rs mode change 100644 => 100755 azalea-brigadier/src/exceptions/mod.rs mode change 100644 => 100755 azalea-brigadier/src/lib.rs mode change 100644 => 100755 azalea-brigadier/src/message.rs mode change 100644 => 100755 azalea-brigadier/src/modifier.rs mode change 100644 => 100755 azalea-brigadier/src/parse_results.rs mode change 100644 => 100755 azalea-brigadier/src/string_reader.rs mode change 100644 => 100755 azalea-brigadier/src/tree/mod.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/bool_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/double_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/float_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/integer_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/long_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/string_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/builder/argument_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/builder/literal_argument_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/builder/required_argument_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/command_dispatcher_test.rs mode change 100644 => 100755 azalea-brigadier/tests/command_dispatcher_usages_test.rs mode change 100644 => 100755 azalea-brigadier/tests/command_suggestions_test.rs mode change 100644 => 100755 azalea-brigadier/tests/context/command_context_test.rs mode change 100644 => 100755 azalea-brigadier/tests/context/parsed_argument_test.rs mode change 100644 => 100755 azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/string_reader_test.rs mode change 100644 => 100755 azalea-brigadier/tests/suggestion/suggestion_test.rs mode change 100644 => 100755 azalea-brigadier/tests/suggestion/suggestions_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/suggestion/suggestions_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/abstract_command_node_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/argument_command_node_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/literal_command_node_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/root_command_node_test.rs mode change 100644 => 100755 azalea-chat/Cargo.toml mode change 100644 => 100755 azalea-chat/README.md mode change 100644 => 100755 azalea-chat/src/base_component.rs mode change 100644 => 100755 azalea-chat/src/component.rs mode change 100644 => 100755 azalea-chat/src/events.rs mode change 100644 => 100755 azalea-chat/src/lib.rs mode change 100644 => 100755 azalea-chat/src/style.rs mode change 100644 => 100755 azalea-chat/src/text_component.rs mode change 100644 => 100755 azalea-chat/src/translatable_component.rs mode change 100644 => 100755 azalea-chat/tests/integration_test.rs mode change 100644 => 100755 azalea-client/Cargo.toml mode change 100644 => 100755 azalea-client/README.md mode change 100644 => 100755 azalea-client/src/connect.rs mode change 100644 => 100755 azalea-client/src/crypt.rs mode change 100644 => 100755 azalea-client/src/lib.rs mode change 100644 => 100755 azalea-client/src/ping.rs mode change 100644 => 100755 azalea-core/Cargo.toml mode change 100644 => 100755 azalea-core/src/difficulty.rs mode change 100644 => 100755 azalea-core/src/game_type.rs mode change 100644 => 100755 azalea-core/src/lib.rs mode change 100644 => 100755 azalea-core/src/resource_location.rs mode change 100644 => 100755 azalea-core/src/serializable_uuid.rs mode change 100644 => 100755 azalea-nbt/Cargo.toml mode change 100644 => 100755 azalea-nbt/README.md mode change 100644 => 100755 azalea-nbt/benches/my_benchmark.rs mode change 100644 => 100755 azalea-nbt/src/decode.rs mode change 100644 => 100755 azalea-nbt/src/encode.rs mode change 100644 => 100755 azalea-nbt/src/error.rs mode change 100644 => 100755 azalea-nbt/src/lib.rs mode change 100644 => 100755 azalea-nbt/src/tag.rs mode change 100644 => 100755 azalea-nbt/tests/bigtest.nbt mode change 100644 => 100755 azalea-nbt/tests/complex_player.dat mode change 100644 => 100755 azalea-nbt/tests/hello_world.nbt mode change 100644 => 100755 azalea-nbt/tests/inttest.nbt mode change 100644 => 100755 azalea-nbt/tests/level.dat mode change 100644 => 100755 azalea-nbt/tests/simple_player.dat mode change 100644 => 100755 azalea-nbt/tests/stringtest.nbt mode change 100644 => 100755 azalea-nbt/tests/tests.rs mode change 100644 => 100755 azalea-protocol/Cargo.toml mode change 100644 => 100755 azalea-protocol/README.md mode change 100644 => 100755 azalea-protocol/packet-macros/Cargo.toml mode change 100644 => 100755 azalea-protocol/packet-macros/src/lib.rs mode change 100644 => 100755 azalea-protocol/src/connect.rs mode change 100644 => 100755 azalea-protocol/src/lib.rs mode change 100644 => 100755 azalea-protocol/src/mc_buf/mod.rs mode change 100644 => 100755 azalea-protocol/src/mc_buf/read.rs mode change 100644 => 100755 azalea-protocol/src/mc_buf/write.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_login_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/handshake/client_intention_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/handshake/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_hello_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/serverbound_hello_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/status/clientbound_status_response_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/status/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/status/serverbound_status_request_packet.rs mode change 100644 => 100755 azalea-protocol/src/read.rs mode change 100644 => 100755 azalea-protocol/src/resolver.rs mode change 100644 => 100755 azalea-protocol/src/write.rs mode change 100644 => 100755 bot/Cargo.toml mode change 100644 => 100755 bot/src/main.rs diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.gitpod.yml b/.gitpod.yml old mode 100644 new mode 100755 diff --git a/.vscode/settings.json b/.vscode/settings.json old mode 100644 new mode 100755 diff --git a/Cargo.lock b/Cargo.lock old mode 100644 new mode 100755 diff --git a/Cargo.toml b/Cargo.toml old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-auth/src/game_profile.rs b/azalea-auth/src/game_profile.rs old mode 100644 new mode 100755 diff --git a/azalea-auth/src/lib.rs b/azalea-auth/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/mod.rs b/azalea-brigadier/src/builder/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/bool_argument_type_test.rs b/azalea-brigadier/tests/arguments/bool_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/double_argument_type_test.rs b/azalea-brigadier/tests/arguments/double_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/float_argument_type_test.rs b/azalea-brigadier/tests/arguments/float_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/integer_argument_type_test.rs b/azalea-brigadier/tests/arguments/integer_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/long_argument_type_test.rs b/azalea-brigadier/tests/arguments/long_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/string_argument_type_test.rs b/azalea-brigadier/tests/arguments/string_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/builder/argument_builder_test.rs b/azalea-brigadier/tests/builder/argument_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/builder/literal_argument_builder_test.rs b/azalea-brigadier/tests/builder/literal_argument_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/builder/required_argument_builder_test.rs b/azalea-brigadier/tests/builder/required_argument_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/context/command_context_test.rs b/azalea-brigadier/tests/context/command_context_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/context/parsed_argument_test.rs b/azalea-brigadier/tests/context/parsed_argument_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs b/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/abstract_command_node_test.rs b/azalea-brigadier/tests/tree/abstract_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/argument_command_node_test.rs b/azalea-brigadier/tests/tree/argument_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/literal_command_node_test.rs b/azalea-brigadier/tests/tree/literal_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/root_command_node_test.rs b/azalea-brigadier/tests/tree/root_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/Cargo.toml b/azalea-chat/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-chat/README.md b/azalea-chat/README.md old mode 100644 new mode 100755 diff --git a/azalea-chat/src/base_component.rs b/azalea-chat/src/base_component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/events.rs b/azalea-chat/src/events.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/lib.rs b/azalea-chat/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/style.rs b/azalea-chat/src/style.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/text_component.rs b/azalea-chat/src/text_component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/translatable_component.rs b/azalea-chat/src/translatable_component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/tests/integration_test.rs b/azalea-chat/tests/integration_test.rs old mode 100644 new mode 100755 diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-client/README.md b/azalea-client/README.md old mode 100644 new mode 100755 diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs old mode 100644 new mode 100755 diff --git a/azalea-client/src/crypt.rs b/azalea-client/src/crypt.rs old mode 100644 new mode 100755 diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-client/src/ping.rs b/azalea-client/src/ping.rs old mode 100644 new mode 100755 diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-core/src/difficulty.rs b/azalea-core/src/difficulty.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/serializable_uuid.rs b/azalea-core/src/serializable_uuid.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-nbt/README.md b/azalea-nbt/README.md old mode 100644 new mode 100755 diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs old mode 100644 new mode 100755 index 41689a46..e4968811 --- a/azalea-nbt/src/decode.rs +++ b/azalea-nbt/src/decode.rs @@ -11,13 +11,13 @@ async fn read_string(stream: &mut R) -> Result where R: AsyncRead + std::marker::Unpin, { - let length = stream.read_u16().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_u16().await?; let mut buf = Vec::with_capacity(length as usize); for _ in 0..length { - buf.push(stream.read_u8().await.map_err(|_| Error::InvalidTag)?); + buf.push(stream.read_u8().await?); } - String::from_utf8(buf).map_err(|_| Error::InvalidTag) + Ok(String::from_utf8(buf)?) } impl Tag { @@ -31,26 +31,26 @@ impl Tag { // a TAG_Compound, and is not named despite being in a TAG_Compound 0 => Tag::End, // A single signed byte - 1 => Tag::Byte(stream.read_i8().await.map_err(|_| Error::InvalidTag)?), + 1 => Tag::Byte(stream.read_i8().await?), // A single signed, big endian 16 bit integer - 2 => Tag::Short(stream.read_i16().await.map_err(|_| Error::InvalidTag)?), + 2 => Tag::Short(stream.read_i16().await?), // A single signed, big endian 32 bit integer - 3 => Tag::Int(stream.read_i32().await.map_err(|_| Error::InvalidTag)?), + 3 => Tag::Int(stream.read_i32().await?), // A single signed, big endian 64 bit integer - 4 => Tag::Long(stream.read_i64().await.map_err(|_| Error::InvalidTag)?), + 4 => Tag::Long(stream.read_i64().await?), // A single, big endian IEEE-754 single-precision floating point // number (NaN possible) - 5 => Tag::Float(stream.read_f32().await.map_err(|_| Error::InvalidTag)?), + 5 => Tag::Float(stream.read_f32().await?), // A single, big endian IEEE-754 double-precision floating point // number (NaN possible) - 6 => Tag::Double(stream.read_f64().await.map_err(|_| Error::InvalidTag)?), + 6 => Tag::Double(stream.read_f64().await?), // A length-prefixed array of signed bytes. The prefix is a signed // integer (thus 4 bytes) 7 => { - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_i32().await?; let mut bytes = Vec::with_capacity(length as usize); for _ in 0..length { - bytes.push(stream.read_i8().await.map_err(|_| Error::InvalidTag)?); + bytes.push(stream.read_i8().await?); } Tag::ByteArray(bytes) } @@ -67,8 +67,8 @@ impl Tag { // another reference implementation by Mojang uses 1 instead; // parsers should accept any type if the length is <= 0). 9 => { - let type_id = stream.read_u8().await.map_err(|_| Error::InvalidTag)?; - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let type_id = stream.read_u8().await?; + let length = stream.read_i32().await?; let mut list = Vec::with_capacity(length as usize); for _ in 0..length { list.push(Tag::read_known(stream, type_id).await?); @@ -94,20 +94,20 @@ impl Tag { // signed integer (thus 4 bytes) and indicates the number of 4 byte // integers. 11 => { - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_i32().await?; let mut ints = Vec::with_capacity(length as usize); for _ in 0..length { - ints.push(stream.read_i32().await.map_err(|_| Error::InvalidTag)?); + ints.push(stream.read_i32().await?); } Tag::IntArray(ints) } // A length-prefixed array of signed longs. The prefix is a signed // integer (thus 4 bytes) and indicates the number of 8 byte longs. 12 => { - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_i32().await?; let mut longs = Vec::with_capacity(length as usize); for _ in 0..length { - longs.push(stream.read_i64().await.map_err(|_| Error::InvalidTag)?); + longs.push(stream.read_i64().await?); } Tag::LongArray(longs) } diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/src/error.rs b/azalea-nbt/src/error.rs old mode 100644 new mode 100755 index 05ff15e0..278d2770 --- a/azalea-nbt/src/error.rs +++ b/azalea-nbt/src/error.rs @@ -14,3 +14,14 @@ impl std::fmt::Display for Error { } } } + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::WriteError + } +} +impl From for Error { + fn from(err: std::string::FromUtf8Error) -> Self { + Error::WriteError + } +} \ No newline at end of file diff --git a/azalea-nbt/src/lib.rs b/azalea-nbt/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/bigtest.nbt b/azalea-nbt/tests/bigtest.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/complex_player.dat b/azalea-nbt/tests/complex_player.dat old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/hello_world.nbt b/azalea-nbt/tests/hello_world.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/inttest.nbt b/azalea-nbt/tests/inttest.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/level.dat b/azalea-nbt/tests/level.dat old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/simple_player.dat b/azalea-nbt/tests/simple_player.dat old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/stringtest.nbt b/azalea-nbt/tests/stringtest.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/tests.rs b/azalea-nbt/tests/tests.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-protocol/README.md b/azalea-protocol/README.md old mode 100644 new mode 100755 diff --git a/azalea-protocol/packet-macros/Cargo.toml b/azalea-protocol/packet-macros/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/mc_buf/mod.rs b/azalea-protocol/src/mc_buf/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs b/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/handshake/mod.rs b/azalea-protocol/src/packets/handshake/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/status/mod.rs b/azalea-protocol/src/packets/status/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/resolver.rs b/azalea-protocol/src/resolver.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/write.rs b/azalea-protocol/src/write.rs old mode 100644 new mode 100755 diff --git a/bot/Cargo.toml b/bot/Cargo.toml old mode 100644 new mode 100755 diff --git a/bot/src/main.rs b/bot/src/main.rs old mode 100644 new mode 100755 From 39d77475d383cea2aaee5deaf7235306b64b6d1d Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 04:37:45 +0000 Subject: [PATCH 70/83] remove some useless code --- azalea-nbt/src/encode.rs | 68 +++++++++++++--------------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index 9ce4faf4..51c5968c 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -6,12 +6,8 @@ use std::io::Write; #[inline] fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { - writer - .write_i16::(string.len() as i16) - .map_err(|_| Error::WriteError)?; - writer - .write_all(string.as_bytes()) - .map_err(|_| Error::WriteError)?; + writer.write_i16::(string.len() as i16)?; + writer.write_all(string.as_bytes())?; Ok(()) } @@ -20,28 +16,16 @@ impl Tag { pub fn write_without_end(&self, writer: &mut dyn Write) -> Result<(), Error> { match self { Tag::End => {} - Tag::Byte(value) => writer.write_i8(*value).map_err(|_| Error::WriteError)?, - Tag::Short(value) => writer - .write_i16::(*value) - .map_err(|_| Error::WriteError)?, - Tag::Int(value) => writer - .write_i32::(*value) - .map_err(|_| Error::WriteError)?, - Tag::Long(value) => writer - .write_i64::(*value) - .map_err(|_| Error::WriteError)?, - Tag::Float(value) => writer - .write_f32::(*value) - .map_err(|_| Error::WriteError)?, - Tag::Double(value) => writer - .write_f64::(*value) - .map_err(|_| Error::WriteError)?, + Tag::Byte(value) => writer.write_i8(*value)?, + Tag::Short(value) => writer.write_i16::(*value)?, + Tag::Int(value) => writer.write_i32::(*value)?, + Tag::Long(value) => writer.write_i64::(*value)?, + Tag::Float(value) => writer.write_f32::(*value)?, + Tag::Double(value) => writer.write_f64::(*value)?, Tag::ByteArray(value) => { - writer - .write_i32::(value.len() as i32) - .map_err(|_| Error::WriteError)?; + writer.write_i32::(value.len() as i32)?; for &byte in value { - writer.write_i8(byte).map_err(|_| Error::WriteError)?; + writer.write_i8(byte)?; } } Tag::String(value) => { @@ -50,14 +34,12 @@ impl Tag { Tag::List(value) => { // we just get the type from the first item, or default the type to END if value.is_empty() { - writer.write_i8(0).map_err(|_| Error::WriteError)?; - writer.write_i32::(0).map_err(|_| Error::WriteError)?; + writer.write_i8(0)?; + writer.write_i32::(0)?; } else { let type_id = value[0].id(); - writer.write_u8(type_id).map_err(|_| Error::WriteError)?; - writer - .write_i32::(value.len() as i32) - .map_err(|_| Error::WriteError)?; + writer.write_u8(type_id)?; + writer.write_i32::(value.len() as i32)?; for tag in value { tag.write_without_end(writer)?; } @@ -65,30 +47,22 @@ impl Tag { } Tag::Compound(value) => { for (key, tag) in value { - writer.write_u8(tag.id()).map_err(|_| Error::WriteError)?; + writer.write_u8(tag.id())?; write_string(writer, key)?; tag.write_without_end(writer)?; } - writer - .write_u8(Tag::End.id()) - .map_err(|_| Error::WriteError)?; + writer.write_u8(Tag::End.id())?; } Tag::IntArray(value) => { - writer - .write_i32::(value.len() as i32) - .map_err(|_| Error::WriteError)?; + writer.write_i32::(value.len() as i32)?; for &int in value { - writer.write_i32::(int).map_err(|_| Error::WriteError)?; + writer.write_i32::(int)?; } } Tag::LongArray(value) => { - writer - .write_i32::(value.len() as i32) - .map_err(|_| Error::WriteError)?; + writer.write_i32::(value.len() as i32)?; for &long in value { - writer - .write_i64::(long) - .map_err(|_| Error::WriteError)?; + writer.write_i64::(long)?; } } } @@ -100,7 +74,7 @@ impl Tag { match self { Tag::Compound(value) => { for (key, tag) in value { - writer.write_u8(tag.id()).map_err(|_| Error::WriteError)?; + writer.write_u8(tag.id())?; write_string(writer, key)?; tag.write_without_end(writer)?; } From 1bbd8b99b39a9c75f9d902d50852d91b3d04add9 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 05:02:45 +0000 Subject: [PATCH 71/83] nbt optimizations --- azalea-nbt/src/encode.rs | 63 +++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index 51c5968c..ebd1070f 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -2,6 +2,7 @@ use crate::Error; use crate::Tag; use byteorder::{WriteBytesExt, BE}; use flate2::write::{GzEncoder, ZlibEncoder}; +use std::collections::HashMap; use std::io::Write; #[inline] @@ -11,8 +12,19 @@ fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { Ok(()) } +#[inline] +fn write_compound(writer: &mut dyn Write, value: &HashMap) -> Result<(), Error> { + for (key, tag) in value { + writer.write_u8(tag.id())?; + write_string(writer, key)?; + tag.write_without_end(writer)?; + } + writer.write_u8(Tag::End.id())?; + Ok(()) +} impl Tag { + #[inline] pub fn write_without_end(&self, writer: &mut dyn Write) -> Result<(), Error> { match self { Tag::End => {} @@ -34,25 +46,48 @@ impl Tag { Tag::List(value) => { // we just get the type from the first item, or default the type to END if value.is_empty() { - writer.write_i8(0)?; - writer.write_i32::(0)?; + writer.write_all(&[0; 5])?; } else { - let type_id = value[0].id(); - writer.write_u8(type_id)?; + let first_tag = &value[0]; + writer.write_u8(first_tag.id())?; writer.write_i32::(value.len() as i32)?; - for tag in value { - tag.write_without_end(writer)?; + match first_tag { + Self::Int(_) => { + for i in value { + if let Tag::Int(v) = i { + writer.write_i32::(*v)? + } else { + panic!("List of Ints should only contain Ints") + } + } + } + Self::String(_) => { + for i in value { + if let Tag::String(v) = i { + write_string(writer, v)?; + } else { + panic!("List of Strings should only contain Strings") + } + } + } + &Self::Compound(_) => { + for i in value { + if let Tag::Compound(v) = i { + write_compound(writer, v)?; + } else { + panic!("List of Compounds should only contain Compounds") + } + } + } + _ => { + for tag in value { + tag.write_without_end(writer)?; + } + } } } } - Tag::Compound(value) => { - for (key, tag) in value { - writer.write_u8(tag.id())?; - write_string(writer, key)?; - tag.write_without_end(writer)?; - } - writer.write_u8(Tag::End.id())?; - } + Tag::Compound(value) => write_compound(writer, value)?, Tag::IntArray(value) => { writer.write_i32::(value.len() as i32)?; for &int in value { From 67c6e333445686e5d7d128e433699ead3a31016a Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 00:52:22 -0500 Subject: [PATCH 72/83] fix nbt decode benchmark --- azalea-nbt/benches/my_benchmark.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs index 7b44d610..df619c3d 100755 --- a/azalea-nbt/benches/my_benchmark.rs +++ b/azalea-nbt/benches/my_benchmark.rs @@ -29,12 +29,14 @@ fn bench_serialize(filename: &str, c: &mut Criterion) { group.throughput(Throughput::Bytes(decoded_src.len() as u64)); // idk if this is criterion's fault or rust's fault but the async benchmark doesn't compile - // group.bench_function("Decode", |b| { - // b.to_async(tokio::runtime::Runtime::new().unwrap();).iter(|| async { - // decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); - // Tag::read(&mut decoded_src_stream).await.unwrap(); - // }) - // }); + group.bench_function("Decode", |b| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { + let mut owned_decoded_src_stream = decoded_src_stream.clone(); + owned_decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); + Tag::read(&mut owned_decoded_src_stream).await.unwrap(); + }) + }); group.bench_function("Encode", |b| { b.iter(|| { From 7df6522489b17059a1033ff17245495aa39253d2 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 14:51:03 +0000 Subject: [PATCH 73/83] remove irrelevant comment --- azalea-nbt/benches/my_benchmark.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs index df619c3d..51e574e3 100755 --- a/azalea-nbt/benches/my_benchmark.rs +++ b/azalea-nbt/benches/my_benchmark.rs @@ -28,7 +28,6 @@ fn bench_serialize(filename: &str, c: &mut Criterion) { group.throughput(Throughput::Bytes(decoded_src.len() as u64)); - // idk if this is criterion's fault or rust's fault but the async benchmark doesn't compile group.bench_function("Decode", |b| { b.to_async(tokio::runtime::Runtime::new().unwrap()) .iter(|| async { From 99652200aa2840e9a3075f238d9eb001aadb7e80 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 18:03:57 +0000 Subject: [PATCH 74/83] simplify nbt list optimization --- azalea-nbt/README.md | 5 +---- azalea-nbt/benches/my_benchmark.rs | 17 +++++++++-------- azalea-nbt/src/encode.rs | 28 +++++++++++++--------------- azalea-nbt/src/tag.rs | 27 +++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/azalea-nbt/README.md b/azalea-nbt/README.md index 0573bce2..19498cf3 100755 --- a/azalea-nbt/README.md +++ b/azalea-nbt/README.md @@ -1,6 +1,3 @@ # Azalea NBT -Deserialize Minecraft NBT. This is somewhat based on [Hermatite NBT](https://github.com/PistonDevelopers/hematite_nbt). - - - +A fast NBT serializer and deserializer. diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs index 51e574e3..528475d3 100755 --- a/azalea-nbt/benches/my_benchmark.rs +++ b/azalea-nbt/benches/my_benchmark.rs @@ -25,17 +25,18 @@ fn bench_serialize(filename: &str, c: &mut Criterion) { .block_on(async { Tag::read(&mut decoded_src_stream).await.unwrap() }); let mut group = c.benchmark_group(filename); + group.sample_size(1000); group.throughput(Throughput::Bytes(decoded_src.len() as u64)); - group.bench_function("Decode", |b| { - b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { - let mut owned_decoded_src_stream = decoded_src_stream.clone(); - owned_decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); - Tag::read(&mut owned_decoded_src_stream).await.unwrap(); - }) - }); + // group.bench_function("Decode", |b| { + // b.to_async(tokio::runtime::Runtime::new().unwrap()) + // .iter(|| async { + // let mut owned_decoded_src_stream = decoded_src_stream.clone(); + // owned_decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); + // Tag::read(&mut owned_decoded_src_stream).await.unwrap(); + // }) + // }); group.bench_function("Encode", |b| { b.iter(|| { diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index ebd1070f..ef72dce2 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -54,29 +54,27 @@ impl Tag { match first_tag { Self::Int(_) => { for i in value { - if let Tag::Int(v) = i { - writer.write_i32::(*v)? - } else { - panic!("List of Ints should only contain Ints") - } + writer.write_i32::( + *i.as_int().expect("List of Int should only contains Int"), + )?; } } Self::String(_) => { for i in value { - if let Tag::String(v) = i { - write_string(writer, v)?; - } else { - panic!("List of Strings should only contain Strings") - } + write_string( + writer, + i.as_string() + .expect("List of String should only contain String"), + )?; } } &Self::Compound(_) => { for i in value { - if let Tag::Compound(v) = i { - write_compound(writer, v)?; - } else { - panic!("List of Compounds should only contain Compounds") - } + write_compound( + writer, + i.as_compound() + .expect("List of Compound should only contain Compound"), + )?; } } _ => { diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs index f11b8889..705dff6a 100755 --- a/azalea-nbt/src/tag.rs +++ b/azalea-nbt/src/tag.rs @@ -36,4 +36,31 @@ impl Tag { Tag::LongArray(_) => 12, } } + + #[inline] + pub fn as_int(&self) -> Option<&i32> { + if let Tag::Int(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_string(&self) -> Option<&str> { + if let Tag::String(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_compound(&self) -> Option<&HashMap> { + if let Tag::Compound(v) = self { + Some(v) + } else { + None + } + } } From b03d3da65937aebcba1cb38573d02267e49bfe19 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 18:23:29 +0000 Subject: [PATCH 75/83] inline reading in nbt decoding --- azalea-nbt/benches/my_benchmark.rs | 26 +++++++++++++------------- azalea-nbt/src/decode.rs | 1 + 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs index 528475d3..00d12dc8 100755 --- a/azalea-nbt/benches/my_benchmark.rs +++ b/azalea-nbt/benches/my_benchmark.rs @@ -29,20 +29,20 @@ fn bench_serialize(filename: &str, c: &mut Criterion) { group.throughput(Throughput::Bytes(decoded_src.len() as u64)); - // group.bench_function("Decode", |b| { - // b.to_async(tokio::runtime::Runtime::new().unwrap()) - // .iter(|| async { - // let mut owned_decoded_src_stream = decoded_src_stream.clone(); - // owned_decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); - // Tag::read(&mut owned_decoded_src_stream).await.unwrap(); - // }) - // }); - - group.bench_function("Encode", |b| { - b.iter(|| { - nbt.write(&mut io::sink()).unwrap(); - }) + group.bench_function("Decode", |b| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { + let mut owned_decoded_src_stream = decoded_src_stream.clone(); + owned_decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); + Tag::read(&mut owned_decoded_src_stream).await.unwrap(); + }) }); + + // group.bench_function("Encode", |b| { + // b.iter(|| { + // nbt.write(&mut io::sink()).unwrap(); + // }) + // }); group.finish(); } diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs index e4968811..3e2f7adb 100755 --- a/azalea-nbt/src/decode.rs +++ b/azalea-nbt/src/decode.rs @@ -21,6 +21,7 @@ where } impl Tag { + #[inline] #[async_recursion] async fn read_known(stream: &mut R, id: u8) -> Result where From 3057ae8b4ad616258f9f4fd777d85fb20a6eef9c Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 20:49:42 -0500 Subject: [PATCH 76/83] maybe optimization --- azalea-nbt/benches/my_benchmark.rs | 27 +++++++++++++-------------- azalea-nbt/src/encode.rs | 5 ++++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs index 00d12dc8..fcb44f90 100755 --- a/azalea-nbt/benches/my_benchmark.rs +++ b/azalea-nbt/benches/my_benchmark.rs @@ -25,24 +25,23 @@ fn bench_serialize(filename: &str, c: &mut Criterion) { .block_on(async { Tag::read(&mut decoded_src_stream).await.unwrap() }); let mut group = c.benchmark_group(filename); - group.sample_size(1000); group.throughput(Throughput::Bytes(decoded_src.len() as u64)); - group.bench_function("Decode", |b| { - b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { - let mut owned_decoded_src_stream = decoded_src_stream.clone(); - owned_decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); - Tag::read(&mut owned_decoded_src_stream).await.unwrap(); - }) - }); - - // group.bench_function("Encode", |b| { - // b.iter(|| { - // nbt.write(&mut io::sink()).unwrap(); - // }) + // group.bench_function("Decode", |b| { + // b.to_async(tokio::runtime::Runtime::new().unwrap()) + // .iter(|| async { + // let mut owned_decoded_src_stream = decoded_src_stream.clone(); + // owned_decoded_src_stream.seek(SeekFrom::Start(0)).unwrap(); + // Tag::read(&mut owned_decoded_src_stream).await.unwrap(); + // }) // }); + + group.bench_function("Encode", |b| { + b.iter(|| { + nbt.write(&mut io::sink()).unwrap(); + }) + }); group.finish(); } diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index ef72dce2..d1126ee0 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -54,6 +54,7 @@ impl Tag { match first_tag { Self::Int(_) => { for i in value { + assert!(matches!(i, Tag::Int(_))); writer.write_i32::( *i.as_int().expect("List of Int should only contains Int"), )?; @@ -61,6 +62,7 @@ impl Tag { } Self::String(_) => { for i in value { + assert!(matches!(i, Tag::String(_))); write_string( writer, i.as_string() @@ -68,8 +70,9 @@ impl Tag { )?; } } - &Self::Compound(_) => { + Self::Compound(_) => { for i in value { + assert!(matches!(i, Tag::Compound(_))); write_compound( writer, i.as_compound() From 3a6810ca1dd86d45e5102a839a916e16e6a52130 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 22:38:42 -0500 Subject: [PATCH 77/83] vroom vroom --- azalea-nbt/src/encode.rs | 246 +++++++++++++++++++++++++++------------ azalea-nbt/src/tag.rs | 81 +++++++++++++ 2 files changed, 250 insertions(+), 77 deletions(-) diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index d1126ee0..27cfe3af 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -12,14 +12,171 @@ fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { Ok(()) } + +// who needs friends when you've got code that runs in nanoseconds? + #[inline] -fn write_compound(writer: &mut dyn Write, value: &HashMap) -> Result<(), Error> { +fn write_compound( + writer: &mut dyn Write, + value: &HashMap, + end_tag: bool, +) -> Result<(), Error> { for (key, tag) in value { - writer.write_u8(tag.id())?; - write_string(writer, key)?; - tag.write_without_end(writer)?; + match tag { + Tag::End => {} + Tag::Byte(value) => { + writer.write_u8(1)?; + write_string(writer, key)?; + writer.write_i8(*value)? + } + Tag::Short(value) => { + writer.write_u8(2)?; + write_string(writer, key)?; + writer.write_i16::(*value)? + } + Tag::Int(value) => { + writer.write_u8(3)?; + write_string(writer, key)?; + writer.write_i32::(*value)? + } + Tag::Long(value) => { + writer.write_u8(4)?; + write_string(writer, key)?; + writer.write_i64::(*value)? + } + Tag::Float(value) => { + writer.write_u8(5)?; + write_string(writer, key)?; + writer.write_f32::(*value)? + } + Tag::Double(value) => { + writer.write_u8(6)?; + write_string(writer, key)?; + writer.write_f64::(*value)? + } + Tag::ByteArray(value) => { + writer.write_u8(7)?; + write_string(writer, key)?; + writer.write_i32::(value.len() as i32)?; + for &byte in value { + writer.write_i8(byte)?; + } + } + Tag::String(value) => { + writer.write_u8(9)?; + write_string(writer, key)?; + write_string(writer, value)? + } + Tag::List(value) => { + writer.write_u8(10)?; + write_string(writer, key)?; + write_list(writer, value)? + } + Tag::Compound(value) => { + writer.write_u8(11)?; + write_string(writer, key)?; + write_compound(writer, value, true)? + } + Tag::IntArray(value) => { + writer.write_u8(12)?; + write_string(writer, key)?; + writer.write_i32::(value.len() as i32)?; + for &int in value { + writer.write_i32::(int)?; + } + } + Tag::LongArray(value) => { + writer.write_u8(13)?; + write_string(writer, key)?; + writer.write_i32::(value.len() as i32)?; + for &long in value { + writer.write_i64::(long)?; + } + } + } + } + if end_tag { + writer.write_u8(Tag::End.id())?; + } + return Ok(()); +} + +#[inline] +fn write_list(writer: &mut dyn Write, value: &Vec) -> Result<(), Error> { + // we just get the type from the first item, or default the type to END + if value.is_empty() { + writer.write_all(&[0; 5])?; + } else { + let first_tag = &value[0]; + writer.write_u8(first_tag.id())?; + writer.write_i32::(value.len() as i32)?; + match first_tag { + Tag::Int(_) => { + // assert all items are the same variant + // assert_eq!(value.iter().all(|tag| tag.id() == 1), true); + for i in value { + assert!(matches!(i, Tag::Int(_))); + writer.write_i32::( + *i.as_int().expect("List of Int should only contains Int"), + )?; + } + } + Tag::String(_) => { + for i in value { + assert!(matches!(i, Tag::String(_))); + write_string( + writer, + i.as_string() + .expect("List of String should only contain String"), + )?; + } + } + Tag::Compound(_) => { + for i in value { + assert!(matches!(i, Tag::Compound(_))); + write_compound( + writer, + i.as_compound() + .expect("List of Compound should only contain Compound"), + true, + )?; + } + } + _ => { + for i in value { + i.write_without_end(writer)?; + } + } + } + } + + Ok(()) +} + +#[inline] +fn write_bytearray(writer: &mut dyn Write, value: &Vec) -> Result<(), Error> { + writer.write_i32::(value.len() as i32)?; + for &byte in value { + writer.write_i8(byte)?; + } + Ok(()) +} + +#[inline] +fn write_intarray(writer: &mut dyn Write, value: &Vec) -> Result<(), Error> { + writer.write_i32::(value.len() as i32)?; + for &int in value { + writer.write_i32::(int)?; + } + Ok(()) +} + +#[inline] +fn write_longarray(writer: &mut dyn Write, value: &Vec) -> Result<(), Error> { + writer.write_i32::(value.len() as i32)?; + for &long in value { + writer.write_i64::(long)?; } - writer.write_u8(Tag::End.id())?; Ok(()) } @@ -34,73 +191,12 @@ impl Tag { Tag::Long(value) => writer.write_i64::(*value)?, Tag::Float(value) => writer.write_f32::(*value)?, Tag::Double(value) => writer.write_f64::(*value)?, - Tag::ByteArray(value) => { - writer.write_i32::(value.len() as i32)?; - for &byte in value { - writer.write_i8(byte)?; - } - } - Tag::String(value) => { - write_string(writer, value)?; - } - Tag::List(value) => { - // we just get the type from the first item, or default the type to END - if value.is_empty() { - writer.write_all(&[0; 5])?; - } else { - let first_tag = &value[0]; - writer.write_u8(first_tag.id())?; - writer.write_i32::(value.len() as i32)?; - match first_tag { - Self::Int(_) => { - for i in value { - assert!(matches!(i, Tag::Int(_))); - writer.write_i32::( - *i.as_int().expect("List of Int should only contains Int"), - )?; - } - } - Self::String(_) => { - for i in value { - assert!(matches!(i, Tag::String(_))); - write_string( - writer, - i.as_string() - .expect("List of String should only contain String"), - )?; - } - } - Self::Compound(_) => { - for i in value { - assert!(matches!(i, Tag::Compound(_))); - write_compound( - writer, - i.as_compound() - .expect("List of Compound should only contain Compound"), - )?; - } - } - _ => { - for tag in value { - tag.write_without_end(writer)?; - } - } - } - } - } - Tag::Compound(value) => write_compound(writer, value)?, - Tag::IntArray(value) => { - writer.write_i32::(value.len() as i32)?; - for &int in value { - writer.write_i32::(int)?; - } - } - Tag::LongArray(value) => { - writer.write_i32::(value.len() as i32)?; - for &long in value { - writer.write_i64::(long)?; - } - } + Tag::ByteArray(value) => write_bytearray(writer, value)?, + Tag::String(value) => write_string(writer, value)?, + Tag::List(value) => write_list(writer, value)?, + Tag::Compound(value) => write_compound(writer, value, true)?, + Tag::IntArray(value) => write_intarray(writer, value)?, + Tag::LongArray(value) => write_longarray(writer, value)?, } Ok(()) @@ -109,11 +205,7 @@ impl Tag { pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> { match self { Tag::Compound(value) => { - for (key, tag) in value { - writer.write_u8(tag.id())?; - write_string(writer, key)?; - tag.write_without_end(writer)?; - } + write_compound(writer, value, false)?; Ok(()) } _ => Err(Error::InvalidTag), diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs index 705dff6a..f8960aa6 100755 --- a/azalea-nbt/src/tag.rs +++ b/azalea-nbt/src/tag.rs @@ -37,6 +37,24 @@ impl Tag { } } + #[inline] + pub fn as_byte(&self) -> Option<&i8> { + if let Tag::Byte(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_short(&self) -> Option<&i16> { + if let Tag::Short(v) = self { + Some(v) + } else { + None + } + } + #[inline] pub fn as_int(&self) -> Option<&i32> { if let Tag::Int(v) = self { @@ -46,6 +64,33 @@ impl Tag { } } + #[inline] + pub fn as_long(&self) -> Option<&i64> { + if let Tag::Long(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_float(&self) -> Option<&f32> { + if let Tag::Float(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_double(&self) -> Option<&f64> { + if let Tag::Double(v) = self { + Some(v) + } else { + None + } + } + #[inline] pub fn as_string(&self) -> Option<&str> { if let Tag::String(v) = self { @@ -63,4 +108,40 @@ impl Tag { None } } + + #[inline] + pub fn as_bytearray(&self) -> Option<&Vec> { + if let Tag::ByteArray(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_intarray(&self) -> Option<&Vec> { + if let Tag::IntArray(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_longarray(&self) -> Option<&Vec> { + if let Tag::LongArray(v) = self { + Some(v) + } else { + None + } + } + + #[inline] + pub fn as_list(&self) -> Option<&Vec> { + if let Tag::List(v) = self { + Some(v) + } else { + None + } + } } From 46454793e041dcecb252084b6616bd8f7011b2e1 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 23 Apr 2022 04:14:19 +0000 Subject: [PATCH 78/83] clean up some code --- azalea-nbt/src/encode.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index 27cfe3af..08d41e89 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -112,39 +112,34 @@ fn write_list(writer: &mut dyn Write, value: &Vec) -> Result<(), Error> { writer.write_i32::(value.len() as i32)?; match first_tag { Tag::Int(_) => { - // assert all items are the same variant - // assert_eq!(value.iter().all(|tag| tag.id() == 1), true); - for i in value { - assert!(matches!(i, Tag::Int(_))); + for tag in value { writer.write_i32::( - *i.as_int().expect("List of Int should only contains Int"), + *tag.as_int().expect("List of Int should only contains Int"), )?; } } Tag::String(_) => { - for i in value { - assert!(matches!(i, Tag::String(_))); + for tag in value { write_string( writer, - i.as_string() + tag.as_string() .expect("List of String should only contain String"), )?; } } Tag::Compound(_) => { - for i in value { - assert!(matches!(i, Tag::Compound(_))); + for tag in value { write_compound( writer, - i.as_compound() + tag.as_compound() .expect("List of Compound should only contain Compound"), true, )?; } } _ => { - for i in value { - i.write_without_end(writer)?; + for tag in value { + tag.write_without_end(writer)?; } } } From 7a272c216bb69e6e720ba420372cdffce13413e1 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 23 Apr 2022 04:28:19 +0000 Subject: [PATCH 79/83] Vec to [Tag] --- azalea-nbt/src/encode.rs | 2 +- azalea-nbt/src/tag.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index 08d41e89..c765d4a7 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -102,7 +102,7 @@ fn write_compound( } #[inline] -fn write_list(writer: &mut dyn Write, value: &Vec) -> Result<(), Error> { +fn write_list(writer: &mut dyn Write, value: &[Tag]) -> Result<(), Error> { // we just get the type from the first item, or default the type to END if value.is_empty() { writer.write_all(&[0; 5])?; diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs index f8960aa6..61bd15ba 100755 --- a/azalea-nbt/src/tag.rs +++ b/azalea-nbt/src/tag.rs @@ -137,7 +137,7 @@ impl Tag { } #[inline] - pub fn as_list(&self) -> Option<&Vec> { + pub fn as_list(&self) -> Option<&[Tag]> { if let Tag::List(v) = self { Some(v) } else { From b43ad19636aa197f145d696f2bb01adf833ddec8 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 24 Apr 2022 14:44:02 -0500 Subject: [PATCH 80/83] remove an unused thing from dependencies --- Cargo.lock | 10 ---------- azalea-nbt/src/encode.rs | 4 ++-- azalea-protocol/packet-macros/Cargo.toml | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d94c26c..a0d60170 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,15 +180,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" -[[package]] -name = "casey" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabe85130dda9cf267715582ce6cf1ab581c8dfe3cb33f7065fee0f14e3fea14" -dependencies = [ - "syn", -] - [[package]] name = "cast" version = "0.2.7" @@ -697,7 +688,6 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" name = "packet-macros" version = "0.1.0" dependencies = [ - "casey", "proc-macro2", "quote", "syn", diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index c765d4a7..0a0237d3 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -5,6 +5,8 @@ use flate2::write::{GzEncoder, ZlibEncoder}; use std::collections::HashMap; use std::io::Write; +// who needs friends when you've got code that runs in nanoseconds? + #[inline] fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { writer.write_i16::(string.len() as i16)?; @@ -13,8 +15,6 @@ fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { Ok(()) } -// who needs friends when you've got code that runs in nanoseconds? - #[inline] fn write_compound( writer: &mut dyn Write, diff --git a/azalea-protocol/packet-macros/Cargo.toml b/azalea-protocol/packet-macros/Cargo.toml index 5a301756..2c0f36d7 100755 --- a/azalea-protocol/packet-macros/Cargo.toml +++ b/azalea-protocol/packet-macros/Cargo.toml @@ -8,7 +8,6 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -casey = "^0.3.3" proc-macro2 = "^1.0.36" quote = "^1.0.10" syn = "^1.0.82" From 59ef9450942b88cad14b5bfc93e0ed9f451d76be Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 24 Apr 2022 14:53:01 -0500 Subject: [PATCH 81/83] fix writing wrong ids in nbt --- azalea-nbt/src/encode.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index 0a0237d3..20d13793 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -63,22 +63,22 @@ fn write_compound( } } Tag::String(value) => { - writer.write_u8(9)?; + writer.write_u8(8)?; write_string(writer, key)?; write_string(writer, value)? } Tag::List(value) => { - writer.write_u8(10)?; + writer.write_u8(9)?; write_string(writer, key)?; write_list(writer, value)? } Tag::Compound(value) => { - writer.write_u8(11)?; + writer.write_u8(10)?; write_string(writer, key)?; write_compound(writer, value, true)? } Tag::IntArray(value) => { - writer.write_u8(12)?; + writer.write_u8(11)?; write_string(writer, key)?; writer.write_i32::(value.len() as i32)?; for &int in value { @@ -86,7 +86,7 @@ fn write_compound( } } Tag::LongArray(value) => { - writer.write_u8(13)?; + writer.write_u8(12)?; write_string(writer, key)?; writer.write_i32::(value.len() as i32)?; for &long in value { From 97d392f4e5721d8aa8940f253918965ff0b40348 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 24 Apr 2022 14:56:46 -0500 Subject: [PATCH 82/83] fix warnings in azalea-nbt --- azalea-nbt/benches/my_benchmark.rs | 2 +- azalea-nbt/src/error.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs index fcb44f90..2fc7ec2f 100755 --- a/azalea-nbt/benches/my_benchmark.rs +++ b/azalea-nbt/benches/my_benchmark.rs @@ -3,7 +3,7 @@ use criterion::{criterion_group, criterion_main, Criterion, Throughput}; use flate2::read::GzDecoder; use std::{ fs::File, - io::{self, Cursor, Read, Seek, SeekFrom}, + io::{self, Read, Seek, SeekFrom}, }; fn bench_serialize(filename: &str, c: &mut Criterion) { diff --git a/azalea-nbt/src/error.rs b/azalea-nbt/src/error.rs index 278d2770..219921e4 100755 --- a/azalea-nbt/src/error.rs +++ b/azalea-nbt/src/error.rs @@ -16,12 +16,12 @@ impl std::fmt::Display for Error { } impl From for Error { - fn from(err: std::io::Error) -> Self { + fn from(_: std::io::Error) -> Self { Error::WriteError } } impl From for Error { - fn from(err: std::string::FromUtf8Error) -> Self { + fn from(_: std::string::FromUtf8Error) -> Self { Error::WriteError } -} \ No newline at end of file +} From b7641ff308aab7840d2a2253ae50f8ee496b2a97 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 24 Apr 2022 16:18:51 -0500 Subject: [PATCH 83/83] 1.18.2 support --- azalea-client/src/connect.rs | 15 +++- azalea-protocol/src/mc_buf/mod.rs | 84 +++++++++++++++++-- azalea-protocol/src/mc_buf/read.rs | 21 ++++- azalea-protocol/src/mc_buf/write.rs | 33 +++++++- .../game/clientbound_disconnect_packet.rs | 9 ++ azalea-protocol/src/packets/game/mod.rs | 2 + .../handshake/client_intention_packet.rs | 24 +++++- .../clientbound_login_disconnect_packet.rs | 7 ++ azalea-protocol/src/packets/login/mod.rs | 5 +- azalea-protocol/src/packets/mod.rs | 6 +- bot/src/main.rs | 4 +- 11 files changed, 188 insertions(+), 22 deletions(-) create mode 100644 azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs create mode 100644 azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 7e6dba51..6e58bee6 100755 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -48,10 +48,18 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { println!("Got profile {:?}", p.game_profile); break conn.game(); } - _ => panic!("unhandled packet"), + LoginPacket::ServerboundHelloPacket(p) => { + println!("Got hello {:?}", p); + } + LoginPacket::ClientboundLoginDisconnectPacket(p) => { + println!("Got disconnect {:?}", p); + } + LoginPacket::ClientboundCustomQueryPacket(p) => { + println!("Got custom query {:?}", p); + } }, Err(e) => { - println!("Error: {:?}", e); + panic!("Error: {:?}", e); } } }; @@ -85,6 +93,9 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { GamePacket::ClientboundUpdateTagsPacket(p) => { println!("Got update tags packet {:?}", p); } + GamePacket::ClientboundDisconnectPacket(p) => { + println!("Got login disconnect packet {:?}", p); + } }, Err(e) => { panic!("Error: {:?}", e); diff --git a/azalea-protocol/src/mc_buf/mod.rs b/azalea-protocol/src/mc_buf/mod.rs index a924431e..4ecb65d1 100755 --- a/azalea-protocol/src/mc_buf/mod.rs +++ b/azalea-protocol/src/mc_buf/mod.rs @@ -19,21 +19,53 @@ mod tests { #[test] fn test_write_varint() { - let mut buf = Vec::new(); - buf.write_varint(123456).unwrap(); - assert_eq!(buf, vec![192, 196, 7]); - let mut buf = Vec::new(); buf.write_varint(0).unwrap(); assert_eq!(buf, vec![0]); + + let mut buf = Vec::new(); + buf.write_varint(1).unwrap(); + assert_eq!(buf, vec![1]); + + let mut buf = Vec::new(); + buf.write_varint(2).unwrap(); + assert_eq!(buf, vec![2]); + + let mut buf = Vec::new(); + buf.write_varint(127).unwrap(); + assert_eq!(buf, vec![127]); + + let mut buf = Vec::new(); + buf.write_varint(128).unwrap(); + assert_eq!(buf, vec![128, 1]); + + let mut buf = Vec::new(); + buf.write_varint(255).unwrap(); + assert_eq!(buf, vec![255, 1]); + + let mut buf = Vec::new(); + buf.write_varint(25565).unwrap(); + assert_eq!(buf, vec![221, 199, 1]); + + let mut buf = Vec::new(); + buf.write_varint(2097151).unwrap(); + assert_eq!(buf, vec![255, 255, 127]); + + let mut buf = Vec::new(); + buf.write_varint(2147483647).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 7]); + + let mut buf = Vec::new(); + buf.write_varint(-1).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 15]); + + let mut buf = Vec::new(); + buf.write_varint(-2147483648).unwrap(); + assert_eq!(buf, vec![128, 128, 128, 128, 8]); } #[tokio::test] async fn test_read_varint() { - let mut buf = BufReader::new(Cursor::new(vec![192, 196, 7])); - assert_eq!(buf.read_varint().await.unwrap(), 123456); - assert_eq!(buf.get_varint_size(123456), 3); - let mut buf = BufReader::new(Cursor::new(vec![0])); assert_eq!(buf.read_varint().await.unwrap(), 0); assert_eq!(buf.get_varint_size(0), 1); @@ -41,6 +73,42 @@ mod tests { let mut buf = BufReader::new(Cursor::new(vec![1])); assert_eq!(buf.read_varint().await.unwrap(), 1); assert_eq!(buf.get_varint_size(1), 1); + + let mut buf = BufReader::new(Cursor::new(vec![2])); + assert_eq!(buf.read_varint().await.unwrap(), 2); + assert_eq!(buf.get_varint_size(2), 1); + + let mut buf = BufReader::new(Cursor::new(vec![127])); + assert_eq!(buf.read_varint().await.unwrap(), 127); + assert_eq!(buf.get_varint_size(127), 1); + + let mut buf = BufReader::new(Cursor::new(vec![128, 1])); + assert_eq!(buf.read_varint().await.unwrap(), 128); + assert_eq!(buf.get_varint_size(128), 2); + + let mut buf = BufReader::new(Cursor::new(vec![255, 1])); + assert_eq!(buf.read_varint().await.unwrap(), 255); + assert_eq!(buf.get_varint_size(255), 2); + + let mut buf = BufReader::new(Cursor::new(vec![221, 199, 1])); + assert_eq!(buf.read_varint().await.unwrap(), 25565); + assert_eq!(buf.get_varint_size(25565), 3); + + let mut buf = BufReader::new(Cursor::new(vec![255, 255, 127])); + assert_eq!(buf.read_varint().await.unwrap(), 2097151); + assert_eq!(buf.get_varint_size(2097151), 3); + + let mut buf = BufReader::new(Cursor::new(vec![255, 255, 255, 255, 7])); + assert_eq!(buf.read_varint().await.unwrap(), 2147483647); + assert_eq!(buf.get_varint_size(2147483647), 5); + + let mut buf = BufReader::new(Cursor::new(vec![255, 255, 255, 255, 15])); + assert_eq!(buf.read_varint().await.unwrap(), -1); + assert_eq!(buf.get_varint_size(-1), 5); + + let mut buf = BufReader::new(Cursor::new(vec![128, 128, 128, 128, 8])); + assert_eq!(buf.read_varint().await.unwrap(), -2147483648); + assert_eq!(buf.get_varint_size(-2147483648), 5); } #[tokio::test] diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index e036643e..1e031916 100755 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -1,7 +1,9 @@ use async_trait::async_trait; +use azalea_chat::component::Component; use azalea_core::{ difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, }; +use serde::Deserialize; use tokio::io::{AsyncRead, AsyncReadExt}; use super::MAX_STRING_LENGTH; @@ -41,12 +43,12 @@ where Ok(list) } - // fast varints stolen from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 + // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 /// Read a single varint from the reader and return the value, along with the number of bytes read async fn read_varint(&mut self) -> Result { let mut buffer = [0]; let mut ans = 0; - for i in 0..4 { + for i in 0..5 { self.read_exact(&mut buffer) .await .map_err(|_| "Invalid VarInt".to_string())?; @@ -440,3 +442,18 @@ impl McBufReadable for Difficulty { Ok(Difficulty::by_id(u8::read_into(buf).await?)) } } + +// Component +#[async_trait] +impl McBufReadable for Component { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let string = buf.read_utf().await?; + let json: serde_json::Value = serde_json::from_str(string.as_str()) + .map_err(|e| "Component isn't valid JSON".to_string())?; + let component = Component::deserialize(json).map_err(|e| e.to_string())?; + Ok(component) + } +} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index 9330dccb..05f613d8 100755 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use azalea_chat::component::Component; use azalea_core::{ difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, }; @@ -209,7 +210,7 @@ impl McBufWritable for ResourceLocation { // u32 impl McBufWritable for u32 { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - i32::varint_write_into(&(*self as i32), buf) + i16::write_into(&(*self as i16), buf) } } @@ -223,7 +224,7 @@ impl McBufVarintWritable for u32 { // u16 impl McBufWritable for u16 { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - i32::varint_write_into(&(*self as i32), buf) + i16::write_into(&(*self as i16), buf) } } @@ -241,6 +242,13 @@ impl McBufWritable for u8 { } } +// i16 +impl McBufWritable for i16 { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + Writable::write_short(buf, *self) + } +} + // i64 impl McBufWritable for i64 { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { @@ -269,7 +277,7 @@ impl McBufWritable for i8 { } } -// i8 +// f32 impl McBufWritable for f32 { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { buf.write_float(*self) @@ -312,3 +320,22 @@ impl McBufWritable for Difficulty { u8::write_into(&self.id(), buf) } } + +// Component +#[async_trait] +impl McBufWritable for Component { + // async fn read_into(buf: &mut R) -> Result + // where + // R: AsyncRead + std::marker::Unpin + std::marker::Send, + // { + // let string = buf.read_utf().await?; + // let json: serde_json::Value = serde_json::from_str(string.as_str()) + // .map_err(|e| "Component isn't valid JSON".to_string())?; + // let component = Component::deserialize(json).map_err(|e| e.to_string())?; + // Ok(component) + // } + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + // component doesn't have serialize implemented yet + todo!() + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs b/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs new file mode 100644 index 00000000..74f5f72e --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs @@ -0,0 +1,9 @@ +use azalea_chat::component::Component; +use azalea_core::resource_location::ResourceLocation; +use packet_macros::GamePacket; +use serde::Deserialize; + +#[derive(Clone, Debug, GamePacket)] +pub struct ClientboundDisconnectPacket { + pub reason: Component, +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index e150606c..dde3f753 100755 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -1,6 +1,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_login_packet; pub mod clientbound_player_abilities_packet; pub mod clientbound_set_carried_item_packet; @@ -15,6 +16,7 @@ declare_state_packets!( Clientbound => { 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, 0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket, + 0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket, 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, 0x26: clientbound_login_packet::ClientboundLoginPacket, 0x32: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs index 6216ddc4..a92d65f6 100755 --- a/azalea-protocol/src/packets/handshake/client_intention_packet.rs +++ b/azalea-protocol/src/packets/handshake/client_intention_packet.rs @@ -1,7 +1,9 @@ -use crate::packets::ConnectionProtocol; +use crate::{mc_buf::Writable, packets::ConnectionProtocol}; use packet_macros::HandshakePacket; use std::hash::Hash; +use super::HandshakePacket; + #[derive(Hash, Clone, Debug, HandshakePacket)] pub struct ClientIntentionPacket { #[varint] @@ -10,3 +12,23 @@ pub struct ClientIntentionPacket { pub port: u16, pub intention: ConnectionProtocol, } + +// impl ClientIntentionPacket { +// pub fn get(self) -> HandshakePacket { +// HandshakePacket::ClientIntentionPacket(self) +// } + +// pub fn write(&self, buf: &mut Vec) -> Result<(), std::io::Error> { +// buf.write_varint(self.protocol_version as i32)?; +// buf.write_utf(&self.hostname)?; +// buf.write_short(self.port as i16)?; +// buf.write_varint(self.intention as i32)?; +// Ok(()) +// } + +// pub async fn read( +// buf: &mut T, +// ) -> Result { +// todo!() +// } +// } diff --git a/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs new file mode 100644 index 00000000..28d91c79 --- /dev/null +++ b/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs @@ -0,0 +1,7 @@ +use azalea_chat::component::Component; +use packet_macros::LoginPacket; + +#[derive(Clone, Debug, LoginPacket)] +pub struct ClientboundLoginDisconnectPacket { + pub reason: Component, +} diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs index ef5f15c1..ab68518c 100755 --- a/azalea-protocol/src/packets/login/mod.rs +++ b/azalea-protocol/src/packets/login/mod.rs @@ -2,6 +2,7 @@ pub mod clientbound_custom_query_packet; pub mod clientbound_game_profile_packet; pub mod clientbound_hello_packet; pub mod clientbound_login_compression_packet; +pub mod clientbound_login_disconnect_packet; pub mod serverbound_hello_packet; use packet_macros::declare_state_packets; @@ -12,7 +13,9 @@ declare_state_packets!( 0x00: serverbound_hello_packet::ServerboundHelloPacket, }, Clientbound => { - 0x00: clientbound_hello_packet::ClientboundHelloPacket, + // 0x00: clientbound_login_disconnect_packet::ClientboundLoginDisconnectPacket, + 26: clientbound_login_disconnect_packet::ClientboundLoginDisconnectPacket, + 0x01: clientbound_hello_packet::ClientboundHelloPacket, 0x02: clientbound_game_profile_packet::ClientboundGameProfilePacket, 0x03: clientbound_login_compression_packet::ClientboundLoginCompressionPacket, 0x04: clientbound_custom_query_packet::ClientboundCustomQueryPacket, diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs index f35451c6..98741a75 100755 --- a/azalea-protocol/src/packets/mod.rs +++ b/azalea-protocol/src/packets/mod.rs @@ -12,9 +12,9 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use tokio::io::AsyncRead; -pub const PROTOCOL_VERSION: u32 = 757; +pub const PROTOCOL_VERSION: u32 = 758; -#[derive(Debug, Clone, PartialEq, Eq, Hash, FromPrimitive)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive)] pub enum ConnectionProtocol { Handshake = -1, Game = 0, @@ -63,6 +63,6 @@ impl McBufReadable for ConnectionProtocol { impl McBufWritable for ConnectionProtocol { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_varint(self.clone() as i32) + buf.write_varint(*self as i32) } } diff --git a/bot/src/main.rs b/bot/src/main.rs index 011c7d0d..7d129478 100755 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -2,8 +2,8 @@ async fn main() { println!("Hello, world!"); - let address = "95.111.249.143:10000"; - // let address = "localhost:63482"; + // let address = "95.111.249.143:10000"; + let address = "localhost:52400"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap();