diff --git a/protocol-derive/src/lib.rs b/protocol-derive/src/lib.rs index 0fed335..9697908 100644 --- a/protocol-derive/src/lib.rs +++ b/protocol-derive/src/lib.rs @@ -5,6 +5,7 @@ use proc_macro2::Ident; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, TokenStreamExt}; use std::iter::FromIterator; +use syn::export::Span; use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, Lit, Meta, NestedMeta}; #[proc_macro_derive(Packet, attributes(packet))] @@ -34,10 +35,21 @@ fn impl_encoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 { let name = &field.ident; let unparsed_meta = get_packet_field_meta(field); - let parsed_meta = parse_field_meta(&unparsed_meta); + let parsed_meta = parse_packet_field_meta(&unparsed_meta); - quote! { - crate::Encoder::encode(&self.#name, writer)?; + match parsed_meta.module { + Some(module) => { + let module_ident = Ident::new(&module, Span::call_site()); + + quote! { + crate::#module_ident::encode(&self.#name, writer)?; + } + } + None => { + quote! { + crate::Encoder::encode(&self.#name, writer)?; + } + } } }); @@ -76,7 +88,7 @@ struct PacketFieldMeta { max_length: Option, } -fn parse_field_meta(meta_list: &Vec) -> PacketFieldMeta { +fn parse_packet_field_meta(meta_list: &Vec) -> PacketFieldMeta { let mut module = None; let mut max_length = None; diff --git a/protocol/src/game.rs b/protocol/src/game.rs index ac25f1a..44afedb 100644 --- a/protocol/src/game.rs +++ b/protocol/src/game.rs @@ -8,9 +8,6 @@ use minecraft_protocol_derive::Packet; use nbt::CompoundTag; use std::io::Read; -const SERVER_BOUND_CHAT_MESSAGE_MAX_LENGTH: u32 = 256; -const LEVEL_TYPE_MAX_LENGTH: u32 = 16; - pub enum GameServerBoundPacket { ServerBoundChatMessage(ServerBoundChatMessage), ServerBoundKeepAlive(ServerBoundKeepAlive), @@ -87,6 +84,7 @@ impl GameClientBoundPacket { #[derive(Packet, Debug)] pub struct ServerBoundChatMessage { + #[packet(max_length = 256)] pub message: String, } @@ -127,8 +125,10 @@ pub struct JoinGame { pub game_mode: GameMode, pub dimension: i32, pub max_players: u8, + #[packet(max_length = 16)] pub level_type: String, - pub view_distance: u8, + #[packet(with = "var_int")] + pub view_distance: i32, pub reduced_debug_info: bool, } @@ -150,7 +150,7 @@ impl JoinGame { dimension: i32, max_players: u8, level_type: String, - view_distance: u8, + view_distance: i32, reduced_debug_info: bool, ) -> GameClientBoundPacket { let join_game = JoinGame { @@ -198,6 +198,7 @@ pub struct ChunkData { pub x: i32, pub z: i32, pub full: bool, + #[packet(with = "var_int")] pub primary_mask: i32, pub heights: CompoundTag, pub data: Vec, diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 9a0c35f..11abe58 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -24,8 +24,9 @@ pub mod status; /// Current supported protocol version. pub const PROTOCOL_VERSION: u32 = 498; -/// String maximum length. +/// Protocol limits maximum string length. const STRING_MAX_LENGTH: u32 = 32_768; +const HYPHENATED_UUID_LENGTH: u32 = 36; /// Possible errors while encoding packet. #[derive(Debug)] @@ -477,3 +478,76 @@ macro_rules! impl_json_encoder_decoder ( } ); ); + +mod var_int { + use crate::{DecodeError, EncodeError, Encoder}; + use mc_varint::{VarIntRead, VarIntWrite}; + use std::io::{Read, Write}; + + pub fn encode(value: &i32, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i32(*value)?; + + Ok(()) + } + + pub fn decode(reader: &mut R) -> Result { + Ok(reader.read_var_i32()?) + } +} + +mod var_long { + use crate::{DecodeError, EncodeError, Encoder}; + use mc_varint::{VarIntRead, VarIntWrite}; + use std::io::{Read, Write}; + + pub fn encode(value: &i64, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i64(*value)?; + + Ok(()) + } + + pub fn decode(reader: &mut R) -> Result { + Ok(reader.read_var_i64()?) + } +} + +mod rest { + use crate::{DecodeError, Decoder, EncodeError, Encoder}; + use std::io::{Read, Write}; + + pub fn encode(value: &[u8], writer: &mut W) -> Result<(), EncodeError> { + writer.write_all(value)?; + + Ok(()) + } + + pub fn decode(reader: &mut R) -> Result, DecodeError> { + let mut data = Vec::new(); + reader.read_to_end(data.as_mut())?; + + Ok(data) + } +} + +mod uuid_hyp_str { + use crate::{ + DecodeError, Decoder, DecoderReadExt, EncodeError, Encoder, EncoderWriteExt, + HYPHENATED_UUID_LENGTH, + }; + use std::io::{Read, Write}; + use uuid::Uuid; + + pub fn encode(value: &Uuid, writer: &mut W) -> Result<(), EncodeError> { + let uuid_hyphenated_string = value.to_hyphenated().to_string(); + writer.write_string(&uuid_hyphenated_string, HYPHENATED_UUID_LENGTH)?; + + Ok(()) + } + + pub fn decode(reader: &mut R) -> Result { + let uuid_hyphenated_string = reader.read_string(HYPHENATED_UUID_LENGTH)?; + let uuid = Uuid::parse_str(&uuid_hyphenated_string)?; + + Ok(uuid) + } +} diff --git a/protocol/src/login.rs b/protocol/src/login.rs index 9482ac9..d18331e 100644 --- a/protocol/src/login.rs +++ b/protocol/src/login.rs @@ -6,10 +6,6 @@ use uuid::Uuid; use minecraft_protocol_derive::Packet; -const LOGIN_MAX_LENGTH: u32 = 16; -const SERVER_ID_MAX_LENGTH: u32 = 20; -const HYPHENATED_UUID_LENGTH: u32 = 36; - pub enum LoginServerBoundPacket { LoginStart(LoginStart), EncryptionResponse(EncryptionResponse), @@ -138,8 +134,10 @@ impl EncryptionResponse { #[derive(Packet, Debug)] pub struct LoginPluginResponse { + #[packet(with = "var_int")] pub message_id: i32, pub successful: bool, + #[packet(with = "rest")] pub data: Vec, } @@ -194,7 +192,9 @@ impl EncryptionRequest { #[derive(Packet, Debug)] pub struct LoginSuccess { + #[packet(with = "uuid_hyp_str")] pub uuid: Uuid, + #[packet(max_length = 16)] pub username: String, } @@ -208,7 +208,7 @@ impl LoginSuccess { #[derive(Packet, Debug)] pub struct SetCompression { - #[packet(with = "varint")] + #[packet(with = "var_int")] pub threshold: i32, } @@ -222,8 +222,10 @@ impl SetCompression { #[derive(Packet, Debug)] pub struct LoginPluginRequest { + #[packet(with = "var_int")] pub message_id: i32, pub channel: String, + #[packet(with = "rest")] pub data: Vec, }