diff --git a/pumpkin-data/build/damage_type.rs b/pumpkin-data/build/damage_type.rs index 09581e08e..1b304fa80 100644 --- a/pumpkin-data/build/damage_type.rs +++ b/pumpkin-data/build/damage_type.rs @@ -7,7 +7,7 @@ use syn::{Ident, LitInt}; #[derive(Deserialize)] struct DamageTypeEntry { - id: u32, + id: u8, components: DamageTypeData, } @@ -111,7 +111,7 @@ pub(crate) fn build() -> TokenStream { pub effects: Option, pub message_id: &'static str, pub scaling: DamageScaling, - pub id: u32, + pub id: u8, } #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/pumpkin-data/build/message_type.rs b/pumpkin-data/build/message_type.rs index 46a80a6b3..86b8ca9b5 100644 --- a/pumpkin-data/build/message_type.rs +++ b/pumpkin-data/build/message_type.rs @@ -34,16 +34,16 @@ pub(crate) fn build() -> TokenStream { let mut variants = TokenStream::new(); for (name, typee) in json.iter() { - let i = typee.id; + let i = typee.id as u8; let name = format_ident!("{}", name.to_uppercase()); variants.extend([quote! { - pub const #name: u32 = #i; + pub const #name: u8 = #i; }]); } - let raw_id = json.len() as u32; + let raw_id = json.len() as u8; variants.extend([quote! { - pub const RAW: u32 = #raw_id; // One higher than highest vanilla id + pub const RAW: u8 = #raw_id; // One higher than highest vanilla id }]); quote! { diff --git a/pumpkin-nbt/src/lib.rs b/pumpkin-nbt/src/lib.rs index a198e72d2..3acc96477 100644 --- a/pumpkin-nbt/src/lib.rs +++ b/pumpkin-nbt/src/lib.rs @@ -187,6 +187,7 @@ pub fn get_nbt_string(bytes: &mut NbtReadHelper) -> Result { pub known_packs: &'a [KnownPack<'a>], @@ -18,14 +15,3 @@ impl<'a> CKnownPacks<'a> { Self { known_packs } } } - -impl ClientPacket for CKnownPacks<'_> { - fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { - let mut write = write; - write.write_list::(self.known_packs, |p, v| { - p.write_string(v.namespace)?; - p.write_string(v.id)?; - p.write_string(v.version) - }) - } -} diff --git a/pumpkin-protocol/src/client/config/registry_data.rs b/pumpkin-protocol/src/client/config/registry_data.rs index eabe40086..62039a731 100644 --- a/pumpkin-protocol/src/client/config/registry_data.rs +++ b/pumpkin-protocol/src/client/config/registry_data.rs @@ -1,15 +1,10 @@ -use std::io::Write; - use pumpkin_data::packet::clientbound::CONFIG_REGISTRY_DATA; use pumpkin_macros::packet; use serde::Serialize; -use crate::{ - ClientPacket, - codec::identifier::Identifier, - ser::{NetworkWriteExt, WritingError}, -}; +use crate::{codec::identifier::Identifier, ser::network_serialize_no_prefix}; +#[derive(Serialize)] #[packet(CONFIG_REGISTRY_DATA)] pub struct CRegistryData<'a> { pub registry_id: &'a Identifier, @@ -25,11 +20,14 @@ impl<'a> CRegistryData<'a> { } } +#[derive(Serialize)] pub struct RegistryEntry { pub entry_id: Identifier, + #[serde(serialize_with = "network_serialize_no_prefix")] pub data: Option>, } +// TODO: No unwraps impl RegistryEntry { pub fn from_nbt(name: &str, nbt: &impl Serialize) -> Self { let mut data_buf = Vec::new(); @@ -48,14 +46,3 @@ impl RegistryEntry { } } } - -impl ClientPacket for CRegistryData<'_> { - fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { - let mut write = write; - write.write_identifier(self.registry_id)?; - write.write_list::(self.entries, |p, v| { - p.write_identifier(&v.entry_id)?; - p.write_option(&v.data, |p, v| p.write_slice(v)) - }) - } -} diff --git a/pumpkin-protocol/src/client/config/server_links.rs b/pumpkin-protocol/src/client/config/server_links.rs index 923493ff2..ce874d75c 100644 --- a/pumpkin-protocol/src/client/config/server_links.rs +++ b/pumpkin-protocol/src/client/config/server_links.rs @@ -1,4 +1,4 @@ -use crate::{Link, VarInt}; +use crate::Link; use pumpkin_data::packet::clientbound::CONFIG_SERVER_LINKS; use pumpkin_macros::packet; use serde::Serialize; @@ -6,12 +6,11 @@ use serde::Serialize; #[derive(Serialize)] #[packet(CONFIG_SERVER_LINKS)] pub struct CConfigServerLinks<'a> { - links_count: &'a VarInt, links: &'a [Link<'a>], } impl<'a> CConfigServerLinks<'a> { - pub fn new(links_count: &'a VarInt, links: &'a [Link<'a>]) -> Self { - Self { links_count, links } + pub fn new(links: &'a [Link<'a>]) -> Self { + Self { links } } } diff --git a/pumpkin-protocol/src/client/config/store_cookie.rs b/pumpkin-protocol/src/client/config/store_cookie.rs index 585062576..3245ec89f 100644 --- a/pumpkin-protocol/src/client/config/store_cookie.rs +++ b/pumpkin-protocol/src/client/config/store_cookie.rs @@ -1,4 +1,4 @@ -use crate::{VarInt, codec::identifier::Identifier}; +use crate::codec::identifier::Identifier; use pumpkin_data::packet::clientbound::CONFIG_STORE_COOKIE; use pumpkin_macros::packet; @@ -8,16 +8,11 @@ use pumpkin_macros::packet; /// The Notchian (vanilla) client only accepts cookies of up to 5 KiB in size. pub struct CStoreCookie<'a> { key: &'a Identifier, - payload_length: VarInt, payload: &'a [u8], // 5120, } impl<'a> CStoreCookie<'a> { pub fn new(key: &'a Identifier, payload: &'a [u8]) -> Self { - Self { - key, - payload_length: VarInt(payload.len() as i32), - payload, - } + Self { key, payload } } } diff --git a/pumpkin-protocol/src/client/config/update_tags.rs b/pumpkin-protocol/src/client/config/update_tags.rs index add94781a..3d809f46e 100644 --- a/pumpkin-protocol/src/client/config/update_tags.rs +++ b/pumpkin-protocol/src/client/config/update_tags.rs @@ -10,7 +10,7 @@ use pumpkin_world::block::registry; use crate::{ ClientPacket, - codec::{identifier::Identifier, var_int::VarInt}, + codec::identifier::Identifier, ser::{NetworkWriteExt, WritingError}, }; @@ -32,7 +32,10 @@ impl ClientPacket for CUpdateTags<'_> { p.write_identifier(&Identifier::vanilla(registry_key.identifier_string()))?; let values = get_registry_key_tags(registry_key); - p.write_var_int(&VarInt::from(values.len()))?; + p.write_var_int(&values.len().try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", values.len())) + })?)?; + for (key, values) in values.iter() { // This is technically an `Identifier` but same thing p.write_string_bounded(key, u16::MAX as usize)?; @@ -43,7 +46,7 @@ impl ClientPacket for CUpdateTags<'_> { _ => unimplemented!(), }; - p.write_var_int(&VarInt::from(id)) + p.write_var_int(&id.into()) })?; } diff --git a/pumpkin-protocol/src/client/login/encryption_request.rs b/pumpkin-protocol/src/client/login/encryption_request.rs index b67fdccbf..4705b03d0 100644 --- a/pumpkin-protocol/src/client/login/encryption_request.rs +++ b/pumpkin-protocol/src/client/login/encryption_request.rs @@ -2,15 +2,11 @@ use pumpkin_data::packet::clientbound::LOGIN_HELLO; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; -use crate::VarInt; - #[derive(Serialize, Deserialize)] #[packet(LOGIN_HELLO)] pub struct CEncryptionRequest<'a> { pub server_id: &'a str, // 20 - pub public_key_length: VarInt, pub public_key: &'a [u8], - pub verify_token_length: VarInt, pub verify_token: &'a [u8], pub should_authenticate: bool, } @@ -24,9 +20,7 @@ impl<'a> CEncryptionRequest<'a> { ) -> Self { Self { server_id, - public_key_length: public_key.len().into(), public_key, - verify_token_length: verify_token.len().into(), verify_token, should_authenticate, } diff --git a/pumpkin-protocol/src/client/login/login_success.rs b/pumpkin-protocol/src/client/login/login_success.rs index 391f08791..8a0d968ea 100644 --- a/pumpkin-protocol/src/client/login/login_success.rs +++ b/pumpkin-protocol/src/client/login/login_success.rs @@ -1,13 +1,10 @@ -use std::io::Write; - use pumpkin_data::packet::clientbound::LOGIN_LOGIN_FINISHED; use pumpkin_macros::packet; +use serde::Serialize; -use crate::{ - ClientPacket, Property, - ser::{NetworkWriteExt, WritingError}, -}; +use crate::Property; +#[derive(Serialize)] #[packet(LOGIN_LOGIN_FINISHED)] pub struct CLoginSuccess<'a> { pub uuid: &'a uuid::Uuid, @@ -24,16 +21,3 @@ impl<'a> CLoginSuccess<'a> { } } } - -impl ClientPacket for CLoginSuccess<'_> { - fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { - let mut write = write; - write.write_uuid(self.uuid)?; - write.write_string(self.username)?; - write.write_list::(self.properties, |p, v| { - p.write_string(&v.name)?; - p.write_string(&v.value)?; - p.write_option(&v.signature, |p, v| p.write_string(v)) - }) - } -} diff --git a/pumpkin-protocol/src/client/play/boss_event.rs b/pumpkin-protocol/src/client/play/boss_event.rs index 743f33b7f..a39c90b57 100644 --- a/pumpkin-protocol/src/client/play/boss_event.rs +++ b/pumpkin-protocol/src/client/play/boss_event.rs @@ -1,8 +1,8 @@ use std::io::Write; +use crate::ClientPacket; use crate::client::play::bossevent_action::BosseventAction; use crate::ser::{NetworkWriteExt, WritingError}; -use crate::{ClientPacket, VarInt}; use pumpkin_data::packet::clientbound::PLAY_BOSS_EVENT; use pumpkin_macros::packet; @@ -32,29 +32,29 @@ impl ClientPacket for CBossEvent<'_> { division, flags, } => { - write.write_var_int(&VarInt::from(0u8))?; + write.write_var_int(&0.into())?; write.write_slice(&title.encode())?; write.write_f32_be(*health)?; write.write_var_int(color)?; write.write_var_int(division)?; write.write_u8_be(*flags) } - BosseventAction::Remove => write.write_var_int(&VarInt::from(1u8)), + BosseventAction::Remove => write.write_var_int(&1.into()), BosseventAction::UpdateHealth(health) => { - write.write_var_int(&VarInt::from(2u8))?; + write.write_var_int(&2.into())?; write.write_f32_be(*health) } BosseventAction::UpdateTile(title) => { - write.write_var_int(&VarInt::from(3u8))?; + write.write_var_int(&3.into())?; write.write_slice(&title.encode()) } BosseventAction::UpdateStyle { color, dividers } => { - write.write_var_int(&VarInt::from(4u8))?; + write.write_var_int(&4.into())?; write.write_var_int(color)?; write.write_var_int(dividers) } BosseventAction::UpdateFlags(flags) => { - write.write_var_int(&VarInt::from(5u8))?; + write.write_var_int(&5.into())?; write.write_u8_be(*flags) } } diff --git a/pumpkin-protocol/src/client/play/chunk_batch_end.rs b/pumpkin-protocol/src/client/play/chunk_batch_end.rs index ce0ea035a..add44bf5f 100644 --- a/pumpkin-protocol/src/client/play/chunk_batch_end.rs +++ b/pumpkin-protocol/src/client/play/chunk_batch_end.rs @@ -11,7 +11,7 @@ pub struct CChunkBatchEnd { } impl CChunkBatchEnd { - pub fn new(count: usize) -> Self { + pub fn new(count: u16) -> Self { Self { batch_size: count.into(), } diff --git a/pumpkin-protocol/src/client/play/chunk_data.rs b/pumpkin-protocol/src/client/play/chunk_data.rs index 5d15785a5..60b38e0e6 100644 --- a/pumpkin-protocol/src/client/play/chunk_data.rs +++ b/pumpkin-protocol/src/client/play/chunk_data.rs @@ -53,7 +53,12 @@ impl ClientPacket for CChunkData<'_> { // TODO: Implement, currently default to full bright let chunk_light = vec![0xFFu8; chunk_light_len]; - light_buf.write_var_int(&chunk_light_len.into())?; + light_buf.write_var_int(&chunk_light_len.try_into().map_err(|_| { + WritingError::Message(format!( + "{} is not representable as a VarInt!", + chunk_light_len + )) + })?)?; light_buf.write_slice(&chunk_light)?; // Block count @@ -68,7 +73,12 @@ impl ClientPacket for CChunkData<'_> { data_buf.write_var_int(®istry_id.into())?; } NetworkPalette::Indirect(palette) => { - data_buf.write_var_int(&palette.len().into())?; + data_buf.write_var_int(&palette.len().try_into().map_err(|_| { + WritingError::Message(format!( + "{} is not representable as a VarInt!", + palette.len() + )) + })?)?; for registry_id in palette { data_buf.write_var_int(®istry_id.into())?; } @@ -89,7 +99,12 @@ impl ClientPacket for CChunkData<'_> { data_buf.write_var_int(®istry_id.into())?; } NetworkPalette::Indirect(palette) => { - data_buf.write_var_int(&palette.len().into())?; + data_buf.write_var_int(&palette.len().try_into().map_err(|_| { + WritingError::Message(format!( + "{} is not representable as a VarInt!", + palette.len() + )) + })?)?; for registry_id in palette { data_buf.write_var_int(®istry_id.into())?; } @@ -105,7 +120,12 @@ impl ClientPacket for CChunkData<'_> { } // Chunk data - write.write_var_int(&data_buf.len().into())?; + write.write_var_int(&data_buf.len().try_into().map_err(|_| { + WritingError::Message(format!( + "{} is not representable as a VarInt!", + data_buf.len() + )) + })?)?; write.write_slice(&data_buf)?; // TODO: block entities @@ -123,7 +143,12 @@ impl ClientPacket for CChunkData<'_> { write.write_bitset(&BitSet(Box::new([0])))?; // Sky light - write.write_var_int(&self.0.section.sections.len().into())?; + write.write_var_int(&self.0.section.sections.len().try_into().map_err(|_| { + WritingError::Message(format!( + "{} is not representable as a VarInt!", + self.0.section.sections.len() + )) + })?)?; write.write_slice(&light_buf)?; // Block Lighting diff --git a/pumpkin-protocol/src/client/play/command_suggestions.rs b/pumpkin-protocol/src/client/play/command_suggestions.rs index 587950f46..53448dc6d 100644 --- a/pumpkin-protocol/src/client/play/command_suggestions.rs +++ b/pumpkin-protocol/src/client/play/command_suggestions.rs @@ -1,24 +1,26 @@ -use std::io::Write; - use pumpkin_data::packet::clientbound::PLAY_COMMAND_SUGGESTIONS; use pumpkin_macros::packet; use pumpkin_util::text::TextComponent; +use serde::Serialize; -use crate::{ - ClientPacket, VarInt, - ser::{NetworkWriteExt, WritingError}, -}; +use crate::VarInt; +#[derive(Serialize)] #[packet(PLAY_COMMAND_SUGGESTIONS)] pub struct CCommandSuggestions { id: VarInt, start: VarInt, length: VarInt, - matches: Vec, + matches: Box<[CommandSuggestion]>, } impl CCommandSuggestions { - pub fn new(id: VarInt, start: VarInt, length: VarInt, matches: Vec) -> Self { + pub fn new( + id: VarInt, + start: VarInt, + length: VarInt, + matches: Box<[CommandSuggestion]>, + ) -> Self { Self { id, start, @@ -28,28 +30,7 @@ impl CCommandSuggestions { } } -impl ClientPacket for CCommandSuggestions { - fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { - let mut write = write; - write.write_var_int(&self.id)?; - write.write_var_int(&self.start)?; - write.write_var_int(&self.length)?; - - write.write_list(&self.matches, |write, suggestion| { - write.write_string(&suggestion.suggestion)?; - write.write_bool(suggestion.tooltip.is_some())?; - if let Some(tooltip) = &suggestion.tooltip { - write.write_slice(&tooltip.encode())?; - } - - Ok(()) - })?; - - Ok(()) - } -} - -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash, Debug, Serialize)] pub struct CommandSuggestion { pub suggestion: String, pub tooltip: Option, diff --git a/pumpkin-protocol/src/client/play/commands.rs b/pumpkin-protocol/src/client/play/commands.rs index e13c286ff..d76c556e9 100644 --- a/pumpkin-protocol/src/client/play/commands.rs +++ b/pumpkin-protocol/src/client/play/commands.rs @@ -10,12 +10,12 @@ use crate::{ #[packet(PLAY_COMMANDS)] pub struct CCommands<'a> { - pub nodes: Vec>, + pub nodes: Box<[ProtoNode<'a>]>, pub root_node_index: VarInt, } impl<'a> CCommands<'a> { - pub fn new(nodes: Vec>, root_node_index: VarInt) -> Self { + pub fn new(nodes: Box<[ProtoNode<'a>]>, root_node_index: VarInt) -> Self { Self { nodes, root_node_index, @@ -34,7 +34,7 @@ impl ClientPacket for CCommands<'_> { } pub struct ProtoNode<'a> { - pub children: Vec, + pub children: Box<[VarInt]>, pub node_type: ProtoNodeType<'a>, } diff --git a/pumpkin-protocol/src/client/play/entity_metadata.rs b/pumpkin-protocol/src/client/play/entity_metadata.rs index f3d9e5f47..63e6f3a26 100644 --- a/pumpkin-protocol/src/client/play/entity_metadata.rs +++ b/pumpkin-protocol/src/client/play/entity_metadata.rs @@ -2,17 +2,19 @@ use pumpkin_data::packet::clientbound::PLAY_SET_ENTITY_DATA; use pumpkin_macros::packet; use serde::Serialize; -use crate::VarInt; +use crate::{VarInt, ser::network_serialize_no_prefix}; #[derive(Serialize)] #[packet(PLAY_SET_ENTITY_DATA)] pub struct CSetEntityMetadata { entity_id: VarInt, - metadata: Vec, + // TODO: We should migrate the serialization of this into this file + #[serde(serialize_with = "network_serialize_no_prefix")] + metadata: Box<[u8]>, } impl CSetEntityMetadata { - pub fn new(entity_id: VarInt, metadata: Vec) -> Self { + pub fn new(entity_id: VarInt, metadata: Box<[u8]>) -> Self { Self { entity_id, metadata, diff --git a/pumpkin-protocol/src/client/play/login.rs b/pumpkin-protocol/src/client/play/login.rs index 908d64445..c2d88833f 100644 --- a/pumpkin-protocol/src/client/play/login.rs +++ b/pumpkin-protocol/src/client/play/login.rs @@ -11,7 +11,6 @@ use crate::{VarInt, codec::identifier::Identifier}; pub struct CLogin<'a> { entity_id: i32, is_hardcore: bool, - dimension_count: VarInt, dimension_names: &'a [Identifier], max_players: VarInt, view_distance: VarInt, @@ -61,7 +60,6 @@ impl<'a> CLogin<'a> { Self { entity_id, is_hardcore, - dimension_count: VarInt(dimension_names.len() as i32), dimension_names, max_players, view_distance, diff --git a/pumpkin-protocol/src/client/play/multi_block_update.rs b/pumpkin-protocol/src/client/play/multi_block_update.rs index c64e90a2d..7c694d649 100644 --- a/pumpkin-protocol/src/client/play/multi_block_update.rs +++ b/pumpkin-protocol/src/client/play/multi_block_update.rs @@ -36,7 +36,14 @@ impl Serialize for CMultiBlockUpdate { let mut tuple = serializer.serialize_tuple(2 + self.positions_to_state_ids.len())?; tuple.serialize_element(&vector3::packed_chunk_pos(&self.chunk_section))?; - tuple.serialize_element(&VarInt::from(self.positions_to_state_ids.len() as i32))?; + tuple.serialize_element(&VarInt( + self.positions_to_state_ids.len().try_into().map_err(|_| { + serde::ser::Error::custom(format!( + "{} is not representable as a VarInt!", + self.positions_to_state_ids.len() + )) + })?, + ))?; for (position, state_id) in &self.positions_to_state_ids { let long = ((*state_id as u64) << 12) | (*position as u64); diff --git a/pumpkin-protocol/src/client/play/player_chat_message.rs b/pumpkin-protocol/src/client/play/player_chat_message.rs index 83eaf49e8..5f0ddce2e 100644 --- a/pumpkin-protocol/src/client/play/player_chat_message.rs +++ b/pumpkin-protocol/src/client/play/player_chat_message.rs @@ -65,6 +65,7 @@ impl CPlayerChatMessage { } } +//TODO: Check if we need this custom impl impl ClientPacket for CPlayerChatMessage { fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { let mut write = write; diff --git a/pumpkin-protocol/src/client/play/player_info_update.rs b/pumpkin-protocol/src/client/play/player_info_update.rs index 3d56f846f..3730f1b34 100644 --- a/pumpkin-protocol/src/client/play/player_info_update.rs +++ b/pumpkin-protocol/src/client/play/player_info_update.rs @@ -42,6 +42,7 @@ impl<'a> CPlayerInfoUpdate<'a> { } } +// TODO: Check if we need this custom impl impl ClientPacket for CPlayerInfoUpdate<'_> { fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { let mut write = write; @@ -63,9 +64,19 @@ impl ClientPacket for CPlayerInfoUpdate<'_> { p.write_option(init_chat, |p, v| { p.write_uuid(&v.session_id)?; p.write_i64_be(v.expires_at)?; - p.write_var_int(&v.public_key.len().into())?; + p.write_var_int(&v.public_key.len().try_into().map_err(|_| { + WritingError::Message(format!( + "{} isn't representable as a VarInt", + v.public_key.len() + )) + })?)?; p.write_slice(&v.public_key)?; - p.write_var_int(&v.signature.len().into())?; + p.write_var_int(&v.signature.len().try_into().map_err(|_| { + WritingError::Message(format!( + "{} isn't representable as a VarInt", + v.signature.len() + )) + })?)?; p.write_slice(&v.signature) })?; } diff --git a/pumpkin-protocol/src/client/play/player_position.rs b/pumpkin-protocol/src/client/play/player_position.rs index 4a960d257..47654bade 100644 --- a/pumpkin-protocol/src/client/play/player_position.rs +++ b/pumpkin-protocol/src/client/play/player_position.rs @@ -39,6 +39,7 @@ impl<'a> CPlayerPosition<'a> { } } +// TODO: Do we need a custom impl? impl ClientPacket for CPlayerPosition<'_> { fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { let mut write = write; diff --git a/pumpkin-protocol/src/client/play/player_remove.rs b/pumpkin-protocol/src/client/play/player_remove.rs index 0eba59792..aa8de6c0b 100644 --- a/pumpkin-protocol/src/client/play/player_remove.rs +++ b/pumpkin-protocol/src/client/play/player_remove.rs @@ -2,22 +2,16 @@ use pumpkin_data::packet::clientbound::PLAY_PLAYER_INFO_REMOVE; use pumpkin_macros::packet; use serde::{Serialize, ser::SerializeSeq}; -use crate::VarInt; - #[derive(Serialize)] #[packet(PLAY_PLAYER_INFO_REMOVE)] pub struct CRemovePlayerInfo<'a> { - players_count: VarInt, #[serde(serialize_with = "serialize_slice_uuids")] players: &'a [uuid::Uuid], } impl<'a> CRemovePlayerInfo<'a> { - pub fn new(players_count: VarInt, players: &'a [uuid::Uuid]) -> Self { - Self { - players_count, - players, - } + pub fn new(players: &'a [uuid::Uuid]) -> Self { + Self { players } } } diff --git a/pumpkin-protocol/src/client/play/remove_entities.rs b/pumpkin-protocol/src/client/play/remove_entities.rs index 2e2c39362..2d13df6a8 100644 --- a/pumpkin-protocol/src/client/play/remove_entities.rs +++ b/pumpkin-protocol/src/client/play/remove_entities.rs @@ -7,15 +7,11 @@ use crate::VarInt; #[derive(Serialize)] #[packet(PLAY_REMOVE_ENTITIES)] pub struct CRemoveEntities<'a> { - entity_count: VarInt, entity_ids: &'a [VarInt], } impl<'a> CRemoveEntities<'a> { pub fn new(entity_ids: &'a [VarInt]) -> Self { - Self { - entity_count: entity_ids.len().into(), - entity_ids, - } + Self { entity_ids } } } diff --git a/pumpkin-protocol/src/client/play/server_links.rs b/pumpkin-protocol/src/client/play/server_links.rs index 8e0d53bb5..b342fb8e0 100644 --- a/pumpkin-protocol/src/client/play/server_links.rs +++ b/pumpkin-protocol/src/client/play/server_links.rs @@ -1,4 +1,4 @@ -use crate::{Link, VarInt}; +use crate::Link; use pumpkin_data::packet::clientbound::PLAY_SERVER_LINKS; use pumpkin_macros::packet; use serde::Serialize; @@ -6,12 +6,11 @@ use serde::Serialize; #[derive(Serialize)] #[packet(PLAY_SERVER_LINKS)] pub struct CPlayServerLinks<'a> { - links_count: &'a VarInt, links: &'a [Link<'a>], } impl<'a> CPlayServerLinks<'a> { - pub fn new(links_count: &'a VarInt, links: &'a [Link<'a>]) -> Self { - Self { links_count, links } + pub fn new(links: &'a [Link<'a>]) -> Self { + Self { links } } } diff --git a/pumpkin-protocol/src/client/play/set_container_content.rs b/pumpkin-protocol/src/client/play/set_container_content.rs index c9b104efa..93e3cb906 100644 --- a/pumpkin-protocol/src/client/play/set_container_content.rs +++ b/pumpkin-protocol/src/client/play/set_container_content.rs @@ -10,7 +10,6 @@ use serde::Serialize; pub struct CSetContainerContent<'a> { window_id: VarInt, state_id: VarInt, - count: VarInt, slot_data: &'a [Slot], carried_item: &'a Slot, } @@ -25,7 +24,6 @@ impl<'a> CSetContainerContent<'a> { Self { window_id, state_id, - count: slots.len().into(), slot_data: slots, carried_item, } diff --git a/pumpkin-protocol/src/client/play/store_cookie.rs b/pumpkin-protocol/src/client/play/store_cookie.rs index 077ab36c2..1dc3a4b98 100644 --- a/pumpkin-protocol/src/client/play/store_cookie.rs +++ b/pumpkin-protocol/src/client/play/store_cookie.rs @@ -1,4 +1,4 @@ -use crate::{VarInt, codec::identifier::Identifier}; +use crate::codec::identifier::Identifier; use pumpkin_data::packet::clientbound::PLAY_STORE_COOKIE; use pumpkin_macros::packet; use serde::Serialize; @@ -9,16 +9,11 @@ use serde::Serialize; #[packet(PLAY_STORE_COOKIE)] pub struct CStoreCookie<'a> { key: &'a Identifier, - payload_length: VarInt, payload: &'a [u8], // 5120, } impl<'a> CStoreCookie<'a> { pub fn new(key: &'a Identifier, payload: &'a [u8]) -> Self { - Self { - key, - payload_length: VarInt(payload.len() as i32), - payload, - } + Self { key, payload } } } diff --git a/pumpkin-protocol/src/client/play/teleport_entity.rs b/pumpkin-protocol/src/client/play/teleport_entity.rs index b26281fdf..082159c8e 100644 --- a/pumpkin-protocol/src/client/play/teleport_entity.rs +++ b/pumpkin-protocol/src/client/play/teleport_entity.rs @@ -42,6 +42,7 @@ impl<'a> CTeleportEntity<'a> { } } +// TODO: Do we need a custom impl? impl ClientPacket for CTeleportEntity<'_> { fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> { let mut write = write; diff --git a/pumpkin-protocol/src/codec/bit_set.rs b/pumpkin-protocol/src/codec/bit_set.rs index c1b526a46..17fa2d260 100644 --- a/pumpkin-protocol/src/codec/bit_set.rs +++ b/pumpkin-protocol/src/codec/bit_set.rs @@ -12,7 +12,10 @@ pub struct BitSet(pub Box<[i64]>); impl BitSet { pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> { - write.write_var_int(&self.0.len().into())?; + write.write_var_int(&self.0.len().try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", self.0.len())) + })?)?; + for b in &self.0 { write.write_i64_be(*b)?; } diff --git a/pumpkin-protocol/src/codec/slot.rs b/pumpkin-protocol/src/codec/slot.rs index 1967c6f3d..f38dbcbc0 100644 --- a/pumpkin-protocol/src/codec/slot.rs +++ b/pumpkin-protocol/src/codec/slot.rs @@ -1,20 +1,21 @@ use crate::VarInt; use pumpkin_data::item::Item; use pumpkin_world::item::ItemStack; -use serde::ser::SerializeSeq; use serde::{ Deserialize, Serialize, Serializer, de::{self, SeqAccess}, + ser, }; #[derive(Debug, Clone)] -pub struct Slot { - pub item_count: VarInt, - item_id: Option, - num_components_to_add: Option, - num_components_to_remove: Option, - components_to_add: Option>, // The second type depends on the varint - components_to_remove: Option>, +pub enum Slot { + NoItem, + Item { + // This also handles items on the ground which can have >64 items + item_count: u32, + item_id: u16, + // TODO: Implement item components + }, } impl<'de> Deserialize<'de> for Slot { @@ -27,7 +28,7 @@ impl<'de> Deserialize<'de> for Slot { type Value = Slot; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") + formatter.write_str("a valid Slot encoded in a byte sequence") } fn visit_seq(self, mut seq: A) -> Result @@ -37,39 +38,39 @@ impl<'de> Deserialize<'de> for Slot { let item_count = seq .next_element::()? .ok_or(de::Error::custom("Failed to decode VarInt"))?; - if item_count.0 == 0 { - return Ok(Slot { - item_count: 0.into(), - item_id: None, - num_components_to_add: None, - num_components_to_remove: None, - components_to_add: None, - components_to_remove: None, - }); - } - let item_id = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode VarInt"))?; - let num_components_to_add = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode VarInt"))?; - let num_components_to_remove = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode VarInt"))?; - if num_components_to_add.0 != 0 || num_components_to_remove.0 != 0 { - return Err(de::Error::custom( - "Slot components are currently unsupported", - )); - } - Ok(Slot { - item_count, - item_id: Some(item_id), - num_components_to_add: Some(num_components_to_add), - num_components_to_remove: Some(num_components_to_remove), - components_to_add: None, - components_to_remove: None, - }) + let slot = if item_count.0 == 0 { + Slot::NoItem + } else { + let item_id = seq + .next_element::()? + .ok_or(de::Error::custom("No item id VarInt!"))?; + let num_components_to_add = seq + .next_element::()? + .ok_or(de::Error::custom("No component add length VarInt!"))?; + let num_components_to_remove = seq + .next_element::()? + .ok_or(de::Error::custom("No component remove length VarInt!"))?; + + if num_components_to_add.0 != 0 || num_components_to_remove.0 != 0 { + return Err(de::Error::custom( + "Slot components are currently unsupported", + )); + } + + let item_id: u16 = item_id + .0 + .try_into() + .map_err(|_| de::Error::custom("Invalid item id!"))?; + + Slot::Item { + // i32 can always be u32 + item_count: item_count.0 as u32, + item_id, + } + }; + + Ok(slot) } } @@ -82,48 +83,33 @@ impl Serialize for Slot { where S: Serializer, { - if self.item_count == 0.into() { - let mut s = serializer.serialize_seq(Some(1))?; - s.serialize_element(&self.item_count)?; - s.end() - } else { - match (&self.num_components_to_add, &self.num_components_to_remove) { - (Some(to_add), Some(to_remove)) => { - let mut s = serializer.serialize_seq(Some(6))?; - s.serialize_element(&self.item_count)?; - s.serialize_element(self.item_id.as_ref().unwrap())?; - s.serialize_element(to_add)?; - s.serialize_element(to_remove)?; - s.serialize_element(self.components_to_add.as_ref().unwrap())?; - s.serialize_element(self.components_to_remove.as_ref().unwrap())?; - s.end() + match self { + Self::NoItem => VarInt(0).serialize(serializer), + Self::Item { + item_count, + item_id, + } => { + // TODO: Components + + #[derive(Serialize)] + struct NetworkRepr { + item_count: VarInt, + item_id: VarInt, + components_to_add: VarInt, + components_to_remove: VarInt, } - (None, Some(to_remove)) => { - let mut s = serializer.serialize_seq(Some(5))?; - s.serialize_element(&self.item_count)?; - s.serialize_element(self.item_id.as_ref().unwrap())?; - s.serialize_element(&VarInt(0))?; - s.serialize_element(to_remove)?; - s.serialize_element(self.components_to_remove.as_ref().unwrap())?; - s.end() - } - (Some(to_add), None) => { - let mut s = serializer.serialize_seq(Some(5))?; - s.serialize_element(&self.item_count)?; - s.serialize_element(self.item_id.as_ref().unwrap())?; - s.serialize_element(to_add)?; - s.serialize_element(&VarInt(0))?; - s.serialize_element(self.components_to_add.as_ref().unwrap())?; - s.end() - } - (None, None) => { - let mut s = serializer.serialize_seq(Some(4))?; - s.serialize_element(&self.item_count)?; - s.serialize_element(&self.item_id.as_ref().unwrap())?; - s.serialize_element(&VarInt(0))?; - s.serialize_element(&VarInt(0))?; - s.end() + + let item_count: i32 = (*item_count) + .try_into() + .map_err(|_| ser::Error::custom("Item count overflows an i32!"))?; + + NetworkRepr { + item_count: item_count.into(), + item_id: (*item_id).into(), + components_to_add: 0.into(), + components_to_remove: 0.into(), } + .serialize(serializer) } } } @@ -131,48 +117,36 @@ impl Serialize for Slot { impl Slot { pub fn new(item_id: u16, count: u32) -> Self { - Slot { - item_count: count.into(), - item_id: Some((item_id as i32).into()), - // TODO: add these - num_components_to_add: None, - num_components_to_remove: None, - components_to_add: None, - components_to_remove: None, + Self::Item { + item_count: count, + item_id, } } pub fn to_stack(self) -> Result, &'static str> { - let item_id = self.item_id; - let Some(item_id) = item_id else { - return Ok(None); - }; - let item_id = item_id.0.try_into().map_err(|_| "Item id too large")?; - let item = Item::from_id(item_id).ok_or("Item id invalid")?; - if self.item_count.0 > item.components.max_stack_size as i32 { - Err("Oversized stack") - } else { - let stack = ItemStack { - item, - item_count: self - .item_count - .0 - .try_into() - .map_err(|_| "Stack count too large")?, - }; - Ok(Some(stack)) + match self { + Self::NoItem => Ok(None), + Self::Item { + item_count, + item_id, + } => { + let item = Item::from_id(item_id).ok_or("Item id invalid")?; + if item_count > item.components.max_stack_size as u32 { + Err("Stack item count greater than allowed") + } else { + let stack = ItemStack { + item, + // This is checked above + item_count: item_count as u8, + }; + Ok(Some(stack)) + } + } } } pub const fn empty() -> Self { - Slot { - item_count: VarInt(0), - item_id: None, - num_components_to_add: None, - num_components_to_remove: None, - components_to_add: None, - components_to_remove: None, - } + Self::NoItem } } diff --git a/pumpkin-protocol/src/codec/var_int.rs b/pumpkin-protocol/src/codec/var_int.rs index 838c1a5e9..b3cc56954 100644 --- a/pumpkin-protocol/src/codec/var_int.rs +++ b/pumpkin-protocol/src/codec/var_int.rs @@ -47,6 +47,7 @@ impl VarInt { Ok(()) } + // TODO: Validate that the first byte will not overflow a i32 pub fn decode(read: &mut impl Read) -> Result { let mut val = 0; for i in 0..Self::MAX_SIZE.get() { @@ -99,41 +100,40 @@ impl VarInt { } } -impl From for VarInt { - fn from(value: i32) -> Self { - VarInt(value) - } +// Macros are needed because traits over generics succccccccccck +macro_rules! gen_from { + ($ty: ty) => { + impl From<$ty> for VarInt { + fn from(value: $ty) -> Self { + VarInt(value.into()) + } + } + }; } -impl From for VarInt { - fn from(value: u32) -> Self { - VarInt(value as i32) - } +gen_from!(i8); +gen_from!(u8); +gen_from!(i16); +gen_from!(u16); +gen_from!(i32); + +macro_rules! gen_try_from { + ($ty: ty) => { + impl TryFrom<$ty> for VarInt { + type Error = >::Error; + + fn try_from(value: $ty) -> Result { + Ok(VarInt(value.try_into()?)) + } + } + }; } -impl From for VarInt { - fn from(value: u8) -> Self { - VarInt(value as i32) - } -} - -impl From for VarInt { - fn from(value: u16) -> Self { - VarInt(value as i32) - } -} - -impl From for VarInt { - fn from(value: usize) -> Self { - VarInt(value as i32) - } -} - -impl From for i32 { - fn from(value: VarInt) -> Self { - value.0 - } -} +gen_try_from!(u32); +gen_try_from!(i64); +gen_try_from!(u64); +gen_try_from!(isize); +gen_try_from!(usize); impl AsRef for VarInt { fn as_ref(&self) -> &i32 { diff --git a/pumpkin-protocol/src/codec/var_long.rs b/pumpkin-protocol/src/codec/var_long.rs index de957e576..9ed4c418c 100644 --- a/pumpkin-protocol/src/codec/var_long.rs +++ b/pumpkin-protocol/src/codec/var_long.rs @@ -47,6 +47,7 @@ impl VarLong { Ok(()) } + // TODO: Validate that the first byte will not overflow a i64 pub fn decode(read: &mut impl Read) -> Result { let mut val = 0; for i in 0..Self::MAX_SIZE.get() { diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index a7a35c17d..cb0b1a89b 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -11,7 +11,6 @@ use ser::{NetworkWriteExt, ReadingError, WritingError, packet::Packet}; use serde::{ Deserialize, Serialize, Serializer, de::{DeserializeSeed, Visitor}, - ser::SerializeSeq, }; use tokio::io::{AsyncRead, AsyncWrite}; @@ -86,7 +85,7 @@ where { enum IdOrStateDeserializer { Init, - Id(u32), + Id(u16), Value(T), } @@ -104,11 +103,15 @@ where IdOrStateDeserializer::Init => { // Get the VarInt let id = VarInt::deserialize(deserializer)?; - assert!(id.0 >= 0); - *self = IdOrStateDeserializer::::Id(id.0 as u32); + *self = IdOrStateDeserializer::::Id(id.0.try_into().map_err(|_| { + serde::de::Error::custom(format!( + "{} cannot be mapped to a registry id", + id.0 + )) + })?); } IdOrStateDeserializer::Id(id) => { - assert!(*id == 0); + debug_assert!(*id == 0); // Get the data let value = T::deserialize(deserializer)?; *self = IdOrStateDeserializer::Value(value); @@ -144,7 +147,7 @@ where #[derive(PartialEq, Clone)] pub enum IdOr { - Id(u32), + Id(u16), Value(T), } @@ -168,10 +171,16 @@ impl Serialize for IdOr { match self { IdOr::Id(id) => VarInt::from(*id + 1).serialize(serializer), IdOr::Value(value) => { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&VarInt::from(0))?; - seq.serialize_element(value)?; - seq.end() + #[derive(Serialize)] + struct NetworkRepr { + zero_id: VarInt, + value: T, + } + NetworkRepr { + zero_id: 0.into(), + value, + } + .serialize(serializer) } } } @@ -383,6 +392,7 @@ pub struct Property { pub signature: Option, } +#[derive(Serialize)] pub struct KnownPack<'a> { pub namespace: &'a str, pub id: &'a str, diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 2d7d1a803..d42c7bca7 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -144,7 +144,12 @@ impl NetworkEncoder { if data_len > MAX_PACKET_DATA_SIZE { return Err(PacketEncodeError::TooLong(data_len)); } - let data_len_var_int: VarInt = data_len.into(); + let data_len_var_int: VarInt = data_len.try_into().map_err(|_| { + PacketEncodeError::Message(format!( + "Packet data length is too large to fit in VarInt! ({})", + data_len + )) + })?; if let Some((compression_threshold, compression_level)) = self.compression { if data_len >= compression_threshold { @@ -170,8 +175,15 @@ impl NetworkEncoder { .map_err(|err| PacketEncodeError::Message(err.to_string()))?; debug_assert!(!compressed_buf.is_empty()); - let full_packet_len_var_int: VarInt = - (data_len_var_int.written_size() + compressed_buf.len()).into(); + let full_packet_len_var_int: VarInt = (data_len_var_int.written_size() + + compressed_buf.len()) + .try_into() + .map_err(|_| { + PacketEncodeError::Message(format!( + "Full packet length is too large to fit in VarInt! ({})", + data_len + )) + })?; let complete_serialization_length = full_packet_len_var_int.written_size() + full_packet_len_var_int.0 as usize; @@ -197,8 +209,14 @@ impl NetworkEncoder { // 0 to indicate uncompressed let data_len_var_int: VarInt = 0.into(); - let full_packet_len_var_int: VarInt = - (data_len_var_int.written_size() + data_len).into(); + let full_packet_len_var_int: VarInt = (data_len_var_int.written_size() + data_len) + .try_into() + .map_err(|_| { + PacketEncodeError::Message(format!( + "Full packet length is too large to fit in VarInt! ({})", + data_len + )) + })?; let complete_serialization_length = full_packet_len_var_int.written_size() + full_packet_len_var_int.0 as usize; diff --git a/pumpkin-protocol/src/ser/mod.rs b/pumpkin-protocol/src/ser/mod.rs index fdd9aa980..d635d3398 100644 --- a/pumpkin-protocol/src/ser/mod.rs +++ b/pumpkin-protocol/src/ser/mod.rs @@ -11,6 +11,17 @@ use thiserror::Error; pub mod packet; pub mod serializer; +// TODO: This is a bit hacky +const NO_PREFIX_MARKER: &str = "__network_no_prefix"; + +pub fn network_serialize_no_prefix(input: T, serializer: S) -> Result +where + T: serde::Serialize, + S: serde::Serializer, +{ + serializer.serialize_newtype_struct(NO_PREFIX_MARKER, &input) +} + #[derive(Debug, Error)] pub enum ReadingError { #[error("EOF, Tried to read {0} but No bytes left to consume")] @@ -262,26 +273,55 @@ pub trait NetworkWriteExt { fn write_f64_be(&mut self, data: f64) -> Result<(), WritingError>; fn write_slice(&mut self, data: &[u8]) -> Result<(), WritingError>; - fn write_bool(&mut self, data: bool) -> Result<(), WritingError>; + fn write_bool(&mut self, data: bool) -> Result<(), WritingError> { + if data { + self.write_u8_be(1) + } else { + self.write_u8_be(0) + } + } fn write_var_int(&mut self, data: &VarInt) -> Result<(), WritingError>; fn write_var_long(&mut self, data: &VarLong) -> Result<(), WritingError>; fn write_string_bounded(&mut self, data: &str, bound: usize) -> Result<(), WritingError>; fn write_string(&mut self, data: &str) -> Result<(), WritingError>; fn write_identifier(&mut self, data: &Identifier) -> Result<(), WritingError>; - fn write_uuid(&mut self, data: &uuid::Uuid) -> Result<(), WritingError>; + + fn write_uuid(&mut self, data: &uuid::Uuid) -> Result<(), WritingError> { + let (first, second) = data.as_u64_pair(); + self.write_u64_be(first)?; + self.write_u64_be(second) + } + fn write_bitset(&mut self, bitset: &BitSet) -> Result<(), WritingError>; fn write_option( &mut self, data: &Option, - write: impl FnOnce(&mut Self, &G) -> Result<(), WritingError>, - ) -> Result<(), WritingError>; + writer: impl FnOnce(&mut Self, &G) -> Result<(), WritingError>, + ) -> Result<(), WritingError> { + if let Some(data) = data { + self.write_bool(true)?; + writer(self, data) + } else { + self.write_bool(false) + } + } fn write_list( &mut self, - data: &[G], - write: impl Fn(&mut Self, &G) -> Result<(), WritingError>, - ) -> Result<(), WritingError>; + list: &[G], + writer: impl Fn(&mut Self, &G) -> Result<(), WritingError>, + ) -> Result<(), WritingError> { + self.write_var_int(&list.len().try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", list.len())) + })?)?; + + for data in list { + writer(self, data)?; + } + + Ok(()) + } } impl NetworkWriteExt for W { @@ -339,14 +379,6 @@ impl NetworkWriteExt for W { self.write_all(data).map_err(WritingError::IoError) } - fn write_bool(&mut self, data: bool) -> Result<(), WritingError> { - if data { - self.write_u8_be(1) - } else { - self.write_u8_be(0) - } - } - fn write_var_int(&mut self, data: &VarInt) -> Result<(), WritingError> { data.encode(self) } @@ -357,7 +389,10 @@ impl NetworkWriteExt for W { fn write_string_bounded(&mut self, data: &str, bound: usize) -> Result<(), WritingError> { assert!(data.len() <= bound); - self.write_var_int(&data.len().into())?; + self.write_var_int(&data.len().try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", data.len())) + })?)?; + self.write_all(data.as_bytes()) .map_err(WritingError::IoError) } @@ -370,41 +405,9 @@ impl NetworkWriteExt for W { data.encode(self) } - fn write_uuid(&mut self, data: &uuid::Uuid) -> Result<(), WritingError> { - let (first, second) = data.as_u64_pair(); - self.write_u64_be(first)?; - self.write_u64_be(second) - } - fn write_bitset(&mut self, data: &BitSet) -> Result<(), WritingError> { data.encode(self) } - - fn write_option( - &mut self, - data: &Option, - writer: impl FnOnce(&mut Self, &G) -> Result<(), WritingError>, - ) -> Result<(), WritingError> { - if let Some(data) = data { - self.write_bool(true)?; - writer(self, data) - } else { - self.write_bool(false) - } - } - - fn write_list( - &mut self, - list: &[G], - writer: impl Fn(&mut Self, &G) -> Result<(), WritingError>, - ) -> Result<(), WritingError> { - self.write_var_int(&list.len().into())?; - for data in list { - writer(self, data)?; - } - - Ok(()) - } } #[cfg(test)] diff --git a/pumpkin-protocol/src/ser/serializer.rs b/pumpkin-protocol/src/ser/serializer.rs index b7b6e5643..9d9aa3373 100644 --- a/pumpkin-protocol/src/ser/serializer.rs +++ b/pumpkin-protocol/src/ser/serializer.rs @@ -2,10 +2,10 @@ use std::fmt::Display; use serde::{ Serialize, - ser::{self}, + ser::{self, Impossible}, }; -use super::{NetworkWriteExt, Write, WritingError}; +use super::{NO_PREFIX_MARKER, NetworkWriteExt, Write, WritingError}; pub struct Serializer { pub write: W, @@ -23,6 +23,198 @@ impl ser::Error for WritingError { } } +/// This serializer just writes a sequence without a varint prefix and defers the rest of the +/// serialization to the wrapped serializer +struct NonPrefixedSeqSerializer<'a, W: Write> { + wrapped: &'a mut Serializer, +} + +macro_rules! create_fail_method { + ($method: ident, $ty: ty) => { + fn $method(self, _v: $ty) -> Result { + Err(WritingError::Serde(format!( + "Expected a sequence, but found {}!", + stringify!($ty) + ))) + } + }; +} + +impl ser::SerializeSeq for NonPrefixedSeqSerializer<'_, W> { + type Ok = (); + type Error = WritingError; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + value.serialize(&mut *self.wrapped).map(|_| ()) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl ser::Serializer for NonPrefixedSeqSerializer<'_, W> { + type Ok = (); + type Error = WritingError; + + type SerializeStructVariant = Impossible; + type SerializeStruct = Impossible; + type SerializeMap = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeSeq = Self; + + create_fail_method!(serialize_bool, bool); + create_fail_method!(serialize_bytes, &[u8]); + create_fail_method!(serialize_char, char); + create_fail_method!(serialize_f32, f32); + create_fail_method!(serialize_f64, f64); + create_fail_method!(serialize_i8, i8); + create_fail_method!(serialize_i16, i16); + create_fail_method!(serialize_i32, i32); + create_fail_method!(serialize_i64, i64); + create_fail_method!(serialize_u8, u8); + create_fail_method!(serialize_u16, u16); + create_fail_method!(serialize_u32, u32); + create_fail_method!(serialize_u64, u64); + create_fail_method!(serialize_str, &str); + + fn serialize_map(self, _len: Option) -> Result { + Err(WritingError::Serde( + "Expected a sequence but found a map!".into(), + )) + } + + fn serialize_newtype_struct( + self, + name: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + Err(WritingError::Serde(format!( + "Expected a sequence but found a newtype struct {}!", + name + ))) + } + + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + Err(WritingError::Serde(format!( + "Expected a sequence but found a newtype variant {}!", + name + ))) + } + + fn serialize_none(self) -> Result { + self.wrapped.serialize_none() + } + + fn serialize_seq(self, _len: Option) -> Result { + Ok(self) + } + + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + self.wrapped.serialize_bool(true)?; + value.serialize(self) + } + + fn serialize_struct( + self, + name: &'static str, + _len: usize, + ) -> Result { + Err(WritingError::Serde(format!( + "Expected a sequence but found a struct {}!", + name + ))) + } + + fn serialize_struct_variant( + self, + name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(WritingError::Serde(format!( + "Expected a sequence but found a struct variant {}!", + name + ))) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(WritingError::Serde( + "Expected a sequence but found a tuple!".into(), + )) + } + + fn serialize_tuple_struct( + self, + name: &'static str, + _len: usize, + ) -> Result { + Err(WritingError::Serde(format!( + "Expected a sequence but found a tuple struct {}!", + name + ))) + } + + fn serialize_tuple_variant( + self, + name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(WritingError::Serde(format!( + "Expected a sequence but found a tuple variant {}!", + name + ))) + } + + fn serialize_unit(self) -> Result { + Err(WritingError::Serde( + "Expected a sequence but found a unit!".into(), + )) + } + + fn serialize_unit_struct(self, name: &'static str) -> Result { + Err(WritingError::Serde(format!( + "Expected a sequence but found a unit struct {}!", + name + ))) + } + + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + _variant: &'static str, + ) -> Result { + Err(WritingError::Serde(format!( + "Expected a sequence but found a unit variant {}!", + name + ))) + } +} + // General notes on the serializer: // // Primitives are written as-is @@ -87,13 +279,13 @@ impl ser::Serializer for &mut Serializer { // TODO: This is super sketchy... is there a way to do it better? Can we choose what // serializer to use on a struct somehow from within the struct? if name == "TextComponent" { - let mut buf = Vec::new(); - let mut nbt_serializer = pumpkin_nbt::serializer::Serializer::new(&mut buf, None); - value - .serialize(&mut nbt_serializer) - .expect("Failed to serialize NBT for TextComponent within the network serializer"); - - self.serialize_bytes(&buf) + let mut nbt_serializer = + pumpkin_nbt::serializer::Serializer::new(&mut self.write, None); + value.serialize(&mut nbt_serializer).map_err(|err| { + WritingError::Serde(format!("Failed to serialize TextComponent NBT: {}", err)) + }) + } else if name == NO_PREFIX_MARKER { + value.serialize(NonPrefixedSeqSerializer { wrapped: self }) } else { value.serialize(self) } @@ -108,15 +300,26 @@ impl ser::Serializer for &mut Serializer { where T: ?Sized + Serialize, { - self.write.write_var_int(&variant_index.into())?; + self.write + .write_var_int(&variant_index.try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", variant_index)) + })?)?; value.serialize(self) } fn serialize_none(self) -> Result { self.write.write_bool(false) } - fn serialize_seq(self, _len: Option) -> Result { - // here is where all arrays/list getting written, usually we prefix the length of every length with an var int. The problem is - // that byte arrays also getting thrown in here, and we don't want to prefix them + fn serialize_seq(self, len: Option) -> Result { + let Some(len) = len else { + return Err(WritingError::Serde( + "Sequences must have a known length".into(), + )); + }; + + self.write.write_var_int(&len.try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", len)) + })?)?; + Ok(self) } fn serialize_some(self, value: &T) -> Result @@ -163,7 +366,10 @@ impl ser::Serializer for &mut Serializer { _len: usize, ) -> Result { // Serialize ENUM index as varint - self.write.write_var_int(&variant_index.into())?; + self.write + .write_var_int(&variant_index.try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", variant_index)) + })?)?; Ok(self) } fn serialize_u128(self, _v: u128) -> Result { @@ -194,7 +400,10 @@ impl ser::Serializer for &mut Serializer { _variant: &'static str, ) -> Result { // For ENUMs, only write enum index as varint - self.write.write_var_int(&variant_index.into()) + self.write + .write_var_int(&variant_index.try_into().map_err(|_| { + WritingError::Message(format!("{} isn't representable as a VarInt", variant_index)) + })?) } fn is_human_readable(&self) -> bool { false diff --git a/pumpkin/src/command/client_suggestions.rs b/pumpkin/src/command/client_suggestions.rs index 1a193da53..7f65f3db9 100644 --- a/pumpkin/src/command/client_suggestions.rs +++ b/pumpkin/src/command/client_suggestions.rs @@ -48,7 +48,7 @@ pub async fn send_c_commands_packet(player: &Arc, dispatcher: &CommandDi let mut proto_nodes = Vec::new(); let root_node_index = root.build(&mut proto_nodes); - let packet = CCommands::new(proto_nodes, root_node_index.into()); + let packet = CCommands::new(proto_nodes.into(), root_node_index.try_into().unwrap()); player.client.enqueue_packet(&packet).await; } @@ -63,12 +63,12 @@ impl<'a> ProtoNodeBuilder<'a> { let mut children = Vec::new(); for node in self.child_nodes { let i = node.build(buffer); - children.push(i.into()); + children.push(i.try_into().unwrap()); } let i = buffer.len(); buffer.push(ProtoNode { - children, + children: children.into(), node_type: self.node_type, }); i diff --git a/pumpkin/src/entity/hunger.rs b/pumpkin/src/entity/hunger.rs index 3aea69ecd..5925aa642 100644 --- a/pumpkin/src/entity/hunger.rs +++ b/pumpkin/src/entity/hunger.rs @@ -4,9 +4,10 @@ use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; use pumpkin_nbt::compound::NbtCompound; +// TODO: This entire thing should be atomic, not individual fields pub struct HungerManager { /// The current hunger level. - pub level: AtomicCell, + pub level: AtomicCell, /// The food saturation level. pub saturation: AtomicCell, pub exhaustion: AtomicCell, @@ -67,8 +68,10 @@ impl HungerManager { #[async_trait] impl NBTStorage for HungerManager { + // TODO: Proper value checks + async fn write_nbt(&self, nbt: &mut NbtCompound) { - nbt.put_int("foodLevel", self.level.load() as i32); + nbt.put_int("foodLevel", self.level.load().into()); nbt.put_float("foodSaturationLevel", self.saturation.load()); nbt.put_float("foodExhaustionLevel", self.exhaustion.load()); nbt.put_int("foodTickTimer", self.tick_timer.load() as i32); @@ -76,7 +79,7 @@ impl NBTStorage for HungerManager { async fn read_nbt(&mut self, nbt: &mut NbtCompound) { self.level - .store(nbt.get_int("foodLevel").unwrap_or(20) as u32); + .store(nbt.get_int("foodLevel").unwrap_or(20) as u8); self.saturation .store(nbt.get_float("foodSaturationLevel").unwrap_or(5.0)); self.exhaustion diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index eee5f12e0..2c5ee49c8 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -144,7 +144,7 @@ impl EntityBase for ItemEntity { .enqueue_packet(&CTakeItemEntity::new( self.entity.entity_id.into(), player.entity_id().into(), - total_pick_up.into(), + total_pick_up.try_into().unwrap(), )) .await; } diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index 6bbe0fd28..e1471ecd2 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -80,7 +80,7 @@ impl LivingEntity { .broadcast_packet_all(&CTakeItemEntity::new( item.entity_id.into(), self.entity.entity_id.into(), - stack_amount.into(), + stack_amount.try_into().unwrap(), )) .await; } diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 83e011323..254e56273 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -411,7 +411,7 @@ impl Entity { self.world .read() .await - .broadcast_packet_all(&CSetEntityMetadata::new(self.entity_id.into(), buf)) + .broadcast_packet_all(&CSetEntityMetadata::new(self.entity_id.into(), buf.into())) .await; } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index a47591230..8a226654f 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -4,7 +4,7 @@ use std::{ ops::AddAssign, sync::{ Arc, - atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32, Ordering}, + atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU8, AtomicU32, Ordering}, }, time::{Duration, Instant}, }; @@ -220,7 +220,7 @@ pub struct Player { /// The player's last known experience level. pub last_sent_xp: AtomicI32, pub last_sent_health: AtomicI32, - pub last_sent_food: AtomicU32, + pub last_sent_food: AtomicU8, pub last_food_saturation: AtomicBool, /// The player's permission level. pub permission_lvl: AtomicCell, @@ -319,7 +319,7 @@ impl Player { chunk_manager: Mutex::new(ChunkManager::new(16)), last_sent_xp: AtomicI32::new(-1), last_sent_health: AtomicI32::new(-1), - last_sent_food: AtomicU32::new(0), + last_sent_food: AtomicU8::new(0), last_food_saturation: AtomicBool::new(true), has_played_before: AtomicBool::new(false), chat_session: Arc::new(Mutex::new(ChatSession::default())), // Placeholder value until the player actually sets their session id @@ -520,7 +520,7 @@ impl Player { ) { self.client .enqueue_packet(&CSoundEffect::new( - IdOr::Id(u32::from(sound_id)), + IdOr::Id(sound_id), category, position, volume, @@ -581,7 +581,7 @@ impl Player { self.client.send_packet_now(&CChunkData(&chunk)).await; } self.client - .send_packet_now(&CChunkBatchEnd::new(chunk_count)) + .send_packet_now(&CChunkBatchEnd::new(chunk_count as u16)) .await; } @@ -1232,7 +1232,7 @@ impl Player { pub async fn send_message( &self, message: &TextComponent, - chat_type: u32, + chat_type: u8, sender_name: &TextComponent, target_name: Option<&TextComponent>, ) { diff --git a/pumpkin/src/net/container.rs b/pumpkin/src/net/container.rs index 974094ad5..c4f859504 100644 --- a/pumpkin/src/net/container.rs +++ b/pumpkin/src/net/container.rs @@ -75,7 +75,7 @@ impl Player { inventory.increment_state_id(); let packet = CSetContainerContent::new( id.into(), - (inventory.state_id).into(), + (inventory.state_id).try_into().unwrap(), &slots, &carried_item, ); diff --git a/pumpkin/src/net/packet/login.rs b/pumpkin/src/net/packet/login.rs index 9af2c948e..650c0b4fc 100644 --- a/pumpkin/src/net/packet/login.rs +++ b/pumpkin/src/net/packet/login.rs @@ -7,7 +7,6 @@ use pumpkin_protocol::{ config::{CConfigAddResourcePack, CConfigServerLinks, CKnownPacks, CUpdateTags}, login::{CLoginSuccess, CSetCompression}, }, - codec::var_int::VarInt, server::login::{SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart}, }; use pumpkin_util::text::TextComponent; @@ -247,8 +246,10 @@ impl Client { async fn enable_compression(&self) { let compression = advanced_config().networking.packet_compression.info.clone(); // We want to wait until we have sent the compression packet to the client - self.send_packet_now(&CSetCompression::new(compression.threshold.into())) - .await; + self.send_packet_now(&CSetCompression::new( + compression.threshold.try_into().unwrap(), + )) + .await; self.set_compression(compression).await; } @@ -340,15 +341,11 @@ impl Client { self.send_packet_now(&server.get_branding()).await; if advanced_config().server_links.enabled { - self.send_packet_now(&CConfigServerLinks::new( - &VarInt(LINKS.len() as i32), - &LINKS, - )) - .await; + self.send_packet_now(&CConfigServerLinks::new(&LINKS)).await; } - // TODO: Is this the right place to send them? // Send tags. + // TODO: Is this the right place to send them? self.send_packet_now(&CUpdateTags::new(&[ pumpkin_data::tag::RegistryKey::Block, diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 7ea26e932..68ac5b343 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -1593,9 +1593,9 @@ impl Player { let response = CCommandSuggestions::new( packet.id, - (last_word_start + 2).into(), - (cmd.len() - last_word_start - 1).into(), - suggestions, + (last_word_start + 2).try_into().unwrap(), + (cmd.len() - last_word_start - 1).try_into().unwrap(), + suggestions.into(), ); self.client.enqueue_packet(&response).await; diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 8bd4eb315..dc2062f21 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -344,7 +344,7 @@ impl Server { &self, message: &TextComponent, sender_name: &TextComponent, - chat_type: u32, + chat_type: u8, target_name: Option<&TextComponent>, ) { send_cancellable! {{ diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 90a622004..32e10dbd2 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -207,7 +207,7 @@ impl World { &self, message: &TextComponent, sender_name: &TextComponent, - chat_type: u32, + chat_type: u8, target_name: Option<&TextComponent>, ) { self.broadcast_packet_all(&CDisguisedChatMessage::new( @@ -320,14 +320,7 @@ impl World { pitch: f32, ) { let seed = thread_rng().r#gen::(); - let packet = CSoundEffect::new( - IdOr::Id(u32::from(sound_id)), - category, - position, - volume, - pitch, - seed, - ); + let packet = CSoundEffect::new(IdOr::Id(sound_id), category, position, volume, pitch, seed); self.broadcast_packet_all(&packet).await; } @@ -518,7 +511,7 @@ impl World { entity_id, base_config.hardcore, &dimensions, - base_config.max_players.into(), + base_config.max_players.try_into().unwrap(), base_config.view_distance.get().into(), // TODO: view distance base_config.simulation_distance.get().into(), // TODO: sim view dinstance false, @@ -811,7 +804,7 @@ impl World { } else { Particle::ExplosionEmitter }; - let sound = IdOr::::Id(Sound::EntityGenericExplode as u32); + let sound = IdOr::::Id(Sound::EntityGenericExplode as u16); for (_, player) in self.players.read().await.iter() { if player.position().squared_distance_to_vec(position) > 4096.0 { continue; @@ -1200,11 +1193,8 @@ impl World { .remove(&player.gameprofile.id) .unwrap(); let uuid = player.gameprofile.id; - self.broadcast_packet_except( - &[player.gameprofile.id], - &CRemovePlayerInfo::new(1.into(), &[uuid]), - ) - .await; + self.broadcast_packet_except(&[player.gameprofile.id], &CRemovePlayerInfo::new(&[uuid])) + .await; self.broadcast_packet_all(&CRemoveEntities::new(&[player.entity_id().into()])) .await;