diff --git a/crates/pumpkin-protocol/src/java/server/config/accept_code_of_conduct.rs b/crates/pumpkin-protocol/src/java/server/config/accept_code_of_conduct.rs index 5563614bc..a767c805d 100644 --- a/crates/pumpkin-protocol/src/java/server/config/accept_code_of_conduct.rs +++ b/crates/pumpkin-protocol/src/java/server/config/accept_code_of_conduct.rs @@ -15,3 +15,13 @@ impl<'a> ServerPacket<'a> for SAcceptCodeOfConduct { Ok(Self) } } + +impl crate::ClientPacket for SAcceptCodeOfConduct { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/client_information.rs b/crates/pumpkin-protocol/src/java/server/config/client_information.rs index e4bbb1e81..90e2ae7f6 100644 --- a/crates/pumpkin-protocol/src/java/server/config/client_information.rs +++ b/crates/pumpkin-protocol/src/java/server/config/client_information.rs @@ -65,3 +65,28 @@ impl<'a> ServerPacket<'a> for SClientInformationConfig<'a> { }) } } + +impl crate::ClientPacket for SClientInformationConfig<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.locale)?; + write.write_i8(self.view_distance)?; + write.write_var_int(&self.chat_mode)?; + write.write_bool(self.chat_colors)?; + write.write_u8(self.skin_parts)?; + if version >= &JavaMinecraftVersion::V_1_9 { + write.write_var_int(&self.main_hand)?; + } + if version >= &JavaMinecraftVersion::V_1_17 { + write.write_bool(self.text_filtering)?; + } + if version >= &JavaMinecraftVersion::V_1_18 { + write.write_bool(self.server_listing)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/cookie_response.rs b/crates/pumpkin-protocol/src/java/server/config/cookie_response.rs index d597f8afc..f500edecf 100644 --- a/crates/pumpkin-protocol/src/java/server/config/cookie_response.rs +++ b/crates/pumpkin-protocol/src/java/server/config/cookie_response.rs @@ -50,3 +50,22 @@ impl<'a> ServerPacket<'a> for SConfigCookieResponse<'a> { }) } } + +impl crate::ClientPacket for SConfigCookieResponse<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::{VarInt, ser::NetworkWriteExt}; + write.write_string(self.key)?; + if let Some(payload) = self.payload { + write.write_bool(true)?; + write.write_var_int(&VarInt(payload.len() as i32))?; + write.write_slice(payload)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/custom_click_action.rs b/crates/pumpkin-protocol/src/java/server/config/custom_click_action.rs index 73b78d833..c27938111 100644 --- a/crates/pumpkin-protocol/src/java/server/config/custom_click_action.rs +++ b/crates/pumpkin-protocol/src/java/server/config/custom_click_action.rs @@ -20,3 +20,21 @@ impl<'a> ServerPacket<'a> for SCustomClickAction<'a> { }) } } + +impl crate::ClientPacket for SCustomClickAction<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.action_id)?; + if let Some(payload) = self.payload { + write.write_bool(true)?; + write.write_slice(payload)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/keep_alive.rs b/crates/pumpkin-protocol/src/java/server/config/keep_alive.rs index b9d7af794..31c66f90d 100644 --- a/crates/pumpkin-protocol/src/java/server/config/keep_alive.rs +++ b/crates/pumpkin-protocol/src/java/server/config/keep_alive.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SKeepAlive { }) } } + +impl crate::ClientPacket for SKeepAlive { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i64_be(self.keep_alive_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/known_packs.rs b/crates/pumpkin-protocol/src/java/server/config/known_packs.rs index a78f664a3..76bed47e3 100644 --- a/crates/pumpkin-protocol/src/java/server/config/known_packs.rs +++ b/crates/pumpkin-protocol/src/java/server/config/known_packs.rs @@ -31,3 +31,18 @@ impl<'a> ServerPacket<'a> for SKnownPacks<'a> { Ok(Self { known_packs }) } } + +impl crate::ClientPacket for SKnownPacks<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::{VarInt, ser::NetworkWriteExt}; + write.write_var_int(&VarInt(self.known_packs.len() as i32))?; + for pack in &self.known_packs { + pack.write(&mut write)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/plugin_message.rs b/crates/pumpkin-protocol/src/java/server/config/plugin_message.rs index fa2c0cc35..e8c3fa9d9 100644 --- a/crates/pumpkin-protocol/src/java/server/config/plugin_message.rs +++ b/crates/pumpkin-protocol/src/java/server/config/plugin_message.rs @@ -28,3 +28,16 @@ impl<'a> ServerPacket<'a> for SPluginMessage<'a> { }) } } + +impl crate::ClientPacket for SPluginMessage<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.channel)?; + write.write_slice(self.data)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/pong.rs b/crates/pumpkin-protocol/src/java/server/config/pong.rs index 0ba348414..ef5eeac49 100644 --- a/crates/pumpkin-protocol/src/java/server/config/pong.rs +++ b/crates/pumpkin-protocol/src/java/server/config/pong.rs @@ -16,3 +16,15 @@ impl<'a> ServerPacket<'a> for SConfigPong { }) } } + +impl crate::ClientPacket for SConfigPong { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i32_be(self.id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/config/resource_pack_response.rs b/crates/pumpkin-protocol/src/java/server/config/resource_pack_response.rs index 68f54025e..a1088f7a5 100644 --- a/crates/pumpkin-protocol/src/java/server/config/resource_pack_response.rs +++ b/crates/pumpkin-protocol/src/java/server/config/resource_pack_response.rs @@ -42,6 +42,19 @@ impl<'a> ServerPacket<'a> for SConfigResourcePack { } } +impl crate::ClientPacket for SConfigResourcePack { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_uuid(&self.uuid)?; + write.write_var_int(&self.result)?; + Ok(()) + } +} + impl SConfigResourcePack { #[must_use] pub const fn response_result(&self) -> ResourcePackResponseResult { diff --git a/crates/pumpkin-protocol/src/java/server/login/cookie_response.rs b/crates/pumpkin-protocol/src/java/server/login/cookie_response.rs index e37c3888f..08e832edf 100644 --- a/crates/pumpkin-protocol/src/java/server/login/cookie_response.rs +++ b/crates/pumpkin-protocol/src/java/server/login/cookie_response.rs @@ -40,3 +40,22 @@ impl<'a> ServerPacket<'a> for SLoginCookieResponse<'a> { }) } } + +impl crate::ClientPacket for SLoginCookieResponse<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::{VarInt, ser::NetworkWriteExt}; + write.write_string(self.key)?; + if let Some(payload) = self.payload { + write.write_bool(true)?; + write.write_var_int(&VarInt(payload.len() as i32))?; + write.write_slice(payload)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/login/login_response.rs b/crates/pumpkin-protocol/src/java/server/login/login_response.rs index 418c0d9a3..66fdd5a8d 100644 --- a/crates/pumpkin-protocol/src/java/server/login/login_response.rs +++ b/crates/pumpkin-protocol/src/java/server/login/login_response.rs @@ -16,3 +16,13 @@ impl<'a> ServerPacket<'a> for SLoginAcknowledged { Ok(Self) } } + +impl crate::ClientPacket for SLoginAcknowledged { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/login/plugin_response.rs b/crates/pumpkin-protocol/src/java/server/login/plugin_response.rs index 7307dc6be..33421c368 100644 --- a/crates/pumpkin-protocol/src/java/server/login/plugin_response.rs +++ b/crates/pumpkin-protocol/src/java/server/login/plugin_response.rs @@ -19,3 +19,21 @@ impl<'a> ServerPacket<'a> for SLoginPluginResponse { }) } } + +impl crate::ClientPacket for SLoginPluginResponse { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.message_id)?; + if let Some(data) = &self.data { + write.write_bool(true)?; + write.write_slice(data)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/attack.rs b/crates/pumpkin-protocol/src/java/server/play/attack.rs index e801212b0..657b6b241 100644 --- a/crates/pumpkin-protocol/src/java/server/play/attack.rs +++ b/crates/pumpkin-protocol/src/java/server/play/attack.rs @@ -23,3 +23,15 @@ impl<'a> ServerPacket<'a> for SAttack { }) } } + +impl crate::ClientPacket for SAttack { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.entity_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/block_entity_tag_query.rs b/crates/pumpkin-protocol/src/java/server/play/block_entity_tag_query.rs index 652783d0c..7e97a4190 100644 --- a/crates/pumpkin-protocol/src/java/server/play/block_entity_tag_query.rs +++ b/crates/pumpkin-protocol/src/java/server/play/block_entity_tag_query.rs @@ -22,3 +22,16 @@ impl<'a> ServerPacket<'a> for SBlockEntityTagQuery { }) } } + +impl crate::ClientPacket for SBlockEntityTagQuery { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.transaction_id)?; + write.write_block_pos(&self.location)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs b/crates/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs index b87f1b0b3..72605ae47 100644 --- a/crates/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs +++ b/crates/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs @@ -23,3 +23,16 @@ impl<'a> ServerPacket<'a> for SBundleItemSelected { }) } } + +impl crate::ClientPacket for SBundleItemSelected { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.slot_id)?; + write.write_var_int(&self.selected_item_index)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/change_game_mode.rs b/crates/pumpkin-protocol/src/java/server/play/change_game_mode.rs index 9852bcff9..bc194870d 100644 --- a/crates/pumpkin-protocol/src/java/server/play/change_game_mode.rs +++ b/crates/pumpkin-protocol/src/java/server/play/change_game_mode.rs @@ -20,3 +20,15 @@ impl<'a> ServerPacket<'a> for SChangeGameMode { }) } } + +impl crate::ClientPacket for SChangeGameMode { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_u8(self.game_mode as u8)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/chat_command.rs b/crates/pumpkin-protocol/src/java/server/play/chat_command.rs index 850b1a681..bf460bbcb 100644 --- a/crates/pumpkin-protocol/src/java/server/play/chat_command.rs +++ b/crates/pumpkin-protocol/src/java/server/play/chat_command.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SChatCommand<'a> { }) } } + +impl crate::ClientPacket for SChatCommand<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.command)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/chunk_batch.rs b/crates/pumpkin-protocol/src/java/server/play/chunk_batch.rs index ab67b1879..b159b86cd 100644 --- a/crates/pumpkin-protocol/src/java/server/play/chunk_batch.rs +++ b/crates/pumpkin-protocol/src/java/server/play/chunk_batch.rs @@ -18,3 +18,15 @@ impl<'a> ServerPacket<'a> for SChunkBatch { }) } } + +impl crate::ClientPacket for SChunkBatch { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_f32_be(self.chunks_per_tick)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/click_container.rs b/crates/pumpkin-protocol/src/java/server/play/click_container.rs index f38ce9f90..3de2038ba 100644 --- a/crates/pumpkin-protocol/src/java/server/play/click_container.rs +++ b/crates/pumpkin-protocol/src/java/server/play/click_container.rs @@ -66,6 +66,32 @@ impl<'a> ServerPacket<'a> for SClickSlot { } } +impl crate::ClientPacket for SClickSlot { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.sync_id)?; + if version >= &JavaMinecraftVersion::V_1_17_1 { + write.write_var_int(&self.revision)?; + } else { + write.write_i16_be(self.revision.0 as i16)?; + } + write.write_i16_be(self.slot)?; + write.write_i8(self.button)?; + self.mode.write(&mut write)?; + write.write_var_int(&VarInt(self.array_of_changed_slots.len() as i32))?; + for (slot, item) in &self.array_of_changed_slots { + write.write_i16_be(*slot)?; + item.write(&mut write)?; + } + self.carried_item.write(&mut write)?; + Ok(()) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub enum SlotActionType { /// Performs a normal slot click. This can pick up or place items in the slot, possibly merging the cursor stack into the slot, or swapping the slot stack with the cursor stack if they can't be merged. @@ -93,6 +119,22 @@ impl SlotActionType { Self::try_from(mode.0) .map_err(|_| ReadingError::Message("Invalid slot action type".to_string())) } + + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + let mode = match self { + Self::Pickup => 0, + Self::QuickMove => 1, + Self::Swap => 2, + Self::Clone => 3, + Self::Throw => 4, + Self::QuickCraft => 5, + Self::PickupAll => 6, + }; + write.write_var_int(&VarInt(mode)) + } } #[derive(Debug)] diff --git a/crates/pumpkin-protocol/src/java/server/play/client_command.rs b/crates/pumpkin-protocol/src/java/server/play/client_command.rs index 8c62e03bf..6e8671ebe 100644 --- a/crates/pumpkin-protocol/src/java/server/play/client_command.rs +++ b/crates/pumpkin-protocol/src/java/server/play/client_command.rs @@ -20,3 +20,15 @@ impl<'a> ServerPacket<'a> for SClientCommand { }) } } + +impl crate::ClientPacket for SClientCommand { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.action_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/client_information.rs b/crates/pumpkin-protocol/src/java/server/play/client_information.rs index 8565717f7..7428b1129 100644 --- a/crates/pumpkin-protocol/src/java/server/play/client_information.rs +++ b/crates/pumpkin-protocol/src/java/server/play/client_information.rs @@ -55,3 +55,28 @@ impl<'a> ServerPacket<'a> for SClientInformationPlay<'a> { }) } } + +impl crate::ClientPacket for SClientInformationPlay<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.locale)?; + write.write_i8(self.view_distance)?; + write.write_var_int(&self.chat_mode)?; + write.write_bool(self.chat_colors)?; + write.write_u8(self.skin_parts)?; + if version >= &JavaMinecraftVersion::V_1_9 { + write.write_var_int(&self.main_hand)?; + } + if version >= &JavaMinecraftVersion::V_1_17 { + write.write_bool(self.text_filtering)?; + } + if version >= &JavaMinecraftVersion::V_1_18 { + write.write_bool(self.server_listing)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/client_tick_end.rs b/crates/pumpkin-protocol/src/java/server/play/client_tick_end.rs index 3d757f176..642858c1e 100644 --- a/crates/pumpkin-protocol/src/java/server/play/client_tick_end.rs +++ b/crates/pumpkin-protocol/src/java/server/play/client_tick_end.rs @@ -14,3 +14,13 @@ impl<'a> ServerPacket<'a> for SClientTickEnd { Ok(Self) } } + +impl crate::ClientPacket for SClientTickEnd { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/close_container.rs b/crates/pumpkin-protocol/src/java/server/play/close_container.rs index e1d8e3a81..7d6844722 100644 --- a/crates/pumpkin-protocol/src/java/server/play/close_container.rs +++ b/crates/pumpkin-protocol/src/java/server/play/close_container.rs @@ -20,3 +20,15 @@ impl<'a> ServerPacket<'a> for SCloseContainer { }) } } + +impl crate::ClientPacket for SCloseContainer { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.window_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/command_suggestion.rs b/crates/pumpkin-protocol/src/java/server/play/command_suggestion.rs index 44a9a317c..daf1ce646 100644 --- a/crates/pumpkin-protocol/src/java/server/play/command_suggestion.rs +++ b/crates/pumpkin-protocol/src/java/server/play/command_suggestion.rs @@ -23,3 +23,16 @@ impl<'a> ServerPacket<'a> for SCommandSuggestion<'a> { }) } } + +impl crate::ClientPacket for SCommandSuggestion<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.id)?; + write.write_string(self.command)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/configuration_acknowledged.rs b/crates/pumpkin-protocol/src/java/server/play/configuration_acknowledged.rs index 7f56d1883..1546ab207 100644 --- a/crates/pumpkin-protocol/src/java/server/play/configuration_acknowledged.rs +++ b/crates/pumpkin-protocol/src/java/server/play/configuration_acknowledged.rs @@ -15,3 +15,13 @@ impl<'a> ServerPacket<'a> for SConfigurationAcknowledged { Ok(Self) } } + +impl crate::ClientPacket for SConfigurationAcknowledged { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/confirm_teleport.rs b/crates/pumpkin-protocol/src/java/server/play/confirm_teleport.rs index fd74d8e75..834d605ee 100644 --- a/crates/pumpkin-protocol/src/java/server/play/confirm_teleport.rs +++ b/crates/pumpkin-protocol/src/java/server/play/confirm_teleport.rs @@ -20,3 +20,15 @@ impl<'a> ServerPacket<'a> for SConfirmTeleport { }) } } + +impl crate::ClientPacket for SConfirmTeleport { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.teleport_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/container_button_click.rs b/crates/pumpkin-protocol/src/java/server/play/container_button_click.rs index c1ceac1b8..5211934b3 100644 --- a/crates/pumpkin-protocol/src/java/server/play/container_button_click.rs +++ b/crates/pumpkin-protocol/src/java/server/play/container_button_click.rs @@ -23,3 +23,16 @@ impl<'a> ServerPacket<'a> for SContainerButtonClick { }) } } + +impl crate::ClientPacket for SContainerButtonClick { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.window_id)?; + write.write_var_int(&self.button_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/container_slot_state_changed.rs b/crates/pumpkin-protocol/src/java/server/play/container_slot_state_changed.rs index ccc60adcf..cb801629a 100644 --- a/crates/pumpkin-protocol/src/java/server/play/container_slot_state_changed.rs +++ b/crates/pumpkin-protocol/src/java/server/play/container_slot_state_changed.rs @@ -24,3 +24,17 @@ impl<'a> ServerPacket<'a> for SContainerSlotStateChanged { }) } } + +impl crate::ClientPacket for SContainerSlotStateChanged { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.slot_id)?; + write.write_var_int(&self.container_id)?; + write.write_bool(self.new_state)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/cookie_response.rs b/crates/pumpkin-protocol/src/java/server/play/cookie_response.rs index 27aca3ccd..f438258cd 100644 --- a/crates/pumpkin-protocol/src/java/server/play/cookie_response.rs +++ b/crates/pumpkin-protocol/src/java/server/play/cookie_response.rs @@ -39,3 +39,22 @@ impl<'a> ServerPacket<'a> for SCookieResponse<'a> { }) } } + +impl crate::ClientPacket for SCookieResponse<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::{VarInt, ser::NetworkWriteExt}; + write.write_string(self.key)?; + if let Some(payload) = self.payload { + write.write_bool(true)?; + write.write_var_int(&VarInt(payload.len() as i32))?; + write.write_slice(payload)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/custom_click_action.rs b/crates/pumpkin-protocol/src/java/server/play/custom_click_action.rs index d61955e59..ab23defe3 100644 --- a/crates/pumpkin-protocol/src/java/server/play/custom_click_action.rs +++ b/crates/pumpkin-protocol/src/java/server/play/custom_click_action.rs @@ -20,3 +20,21 @@ impl<'a> ServerPacket<'a> for SCustomClickAction<'a> { }) } } + +impl crate::ClientPacket for SCustomClickAction<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.action_id)?; + if let Some(payload) = self.payload { + write.write_bool(true)?; + write.write_slice(payload)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/custom_payload.rs b/crates/pumpkin-protocol/src/java/server/play/custom_payload.rs index d63e6cd4e..58cbea1ac 100644 --- a/crates/pumpkin-protocol/src/java/server/play/custom_payload.rs +++ b/crates/pumpkin-protocol/src/java/server/play/custom_payload.rs @@ -28,3 +28,16 @@ impl<'a> ServerPacket<'a> for SCustomPayload<'a> { }) } } + +impl crate::ClientPacket for SCustomPayload<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.channel)?; + write.write_slice(self.data)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/debug_sample_subscription.rs b/crates/pumpkin-protocol/src/java/server/play/debug_sample_subscription.rs index 6f240c501..831b99325 100644 --- a/crates/pumpkin-protocol/src/java/server/play/debug_sample_subscription.rs +++ b/crates/pumpkin-protocol/src/java/server/play/debug_sample_subscription.rs @@ -18,3 +18,15 @@ impl<'a> ServerPacket<'a> for SDebugSampleSubscription { }) } } + +impl crate::ClientPacket for SDebugSampleSubscription { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.sample_type)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/debug_subscription_request.rs b/crates/pumpkin-protocol/src/java/server/play/debug_subscription_request.rs index f4a0bcf37..19206114b 100644 --- a/crates/pumpkin-protocol/src/java/server/play/debug_subscription_request.rs +++ b/crates/pumpkin-protocol/src/java/server/play/debug_subscription_request.rs @@ -18,3 +18,15 @@ impl<'a> ServerPacket<'a> for SDebugSubscriptionRequest { }) } } + +impl crate::ClientPacket for SDebugSubscriptionRequest { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.sample_type)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/edit_book.rs b/crates/pumpkin-protocol/src/java/server/play/edit_book.rs index 8ff8ec4d2..b89b5aad3 100644 --- a/crates/pumpkin-protocol/src/java/server/play/edit_book.rs +++ b/crates/pumpkin-protocol/src/java/server/play/edit_book.rs @@ -33,3 +33,25 @@ impl<'a> ServerPacket<'a> for SEditBook<'a> { Ok(Self { slot, pages, title }) } } + +impl crate::ClientPacket for SEditBook<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.slot)?; + write.write_var_int(&VarInt(self.pages.len() as i32))?; + for page in &self.pages { + write.write_string_bounded(page, 1024)?; + } + if let Some(title) = self.title { + write.write_bool(true)?; + write.write_string_bounded(title, 128)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/entity_tag_query.rs b/crates/pumpkin-protocol/src/java/server/play/entity_tag_query.rs index 46c0e5a7e..2136e0d5b 100644 --- a/crates/pumpkin-protocol/src/java/server/play/entity_tag_query.rs +++ b/crates/pumpkin-protocol/src/java/server/play/entity_tag_query.rs @@ -22,3 +22,16 @@ impl<'a> ServerPacket<'a> for SEntityTagQuery { }) } } + +impl crate::ClientPacket for SEntityTagQuery { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.transaction_id)?; + write.write_var_int(&self.entity_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/interact.rs b/crates/pumpkin-protocol/src/java/server/play/interact.rs index d8043d037..77066e473 100644 --- a/crates/pumpkin-protocol/src/java/server/play/interact.rs +++ b/crates/pumpkin-protocol/src/java/server/play/interact.rs @@ -63,6 +63,40 @@ impl<'a> ServerPacket<'a> for SInteract { } } +impl crate::ClientPacket for SInteract { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + if version >= &JavaMinecraftVersion::V_26_1 { + write.write_var_int(&self.entity_id)?; + write.write_var_int(&self.hand.unwrap_or(VarInt(0)))?; + if let Some(pos) = self.target_position { + LpVector3d(pos).write(&mut write)?; + } else { + LpVector3d(Vector3::new(0.0, 0.0, 0.0)).write(&mut write)?; + } + write.write_bool(self.sneaking)?; + return Ok(()); + } + + write.write_var_int(&self.entity_id)?; + write.write_var_int(&self.r#type)?; + if let Some(target) = self.target_position { + write.write_f32_be(target.x as f32)?; + write.write_f32_be(target.y as f32)?; + write.write_f32_be(target.z as f32)?; + } + if let Some(hand) = self.hand { + write.write_var_int(&hand)?; + } + write.write_bool(self.sneaking)?; + Ok(()) + } +} + #[derive(PartialEq, Eq, Debug, Clone)] pub enum ActionType { Interact, diff --git a/crates/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs b/crates/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs index 9476b32f3..a020130e8 100644 --- a/crates/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs +++ b/crates/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs @@ -28,3 +28,17 @@ impl<'a> ServerPacket<'a> for SJigsawGenerate { }) } } + +impl crate::ClientPacket for SJigsawGenerate { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.pos)?; + write.write_var_int(&self.levels)?; + write.write_bool(self.keep_jigsaws)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/keep_alive.rs b/crates/pumpkin-protocol/src/java/server/play/keep_alive.rs index 4a366afd9..c30e4f5f6 100644 --- a/crates/pumpkin-protocol/src/java/server/play/keep_alive.rs +++ b/crates/pumpkin-protocol/src/java/server/play/keep_alive.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SKeepAlive { }) } } + +impl crate::ClientPacket for SKeepAlive { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i64_be(self.keep_alive_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/lock_difficulty.rs b/crates/pumpkin-protocol/src/java/server/play/lock_difficulty.rs index 7a112f43e..85ecec9db 100644 --- a/crates/pumpkin-protocol/src/java/server/play/lock_difficulty.rs +++ b/crates/pumpkin-protocol/src/java/server/play/lock_difficulty.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SLockDifficulty { }) } } + +impl crate::ClientPacket for SLockDifficulty { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_bool(self.locked)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/move_vehicle.rs b/crates/pumpkin-protocol/src/java/server/play/move_vehicle.rs index 5e6fc6c7b..c1b3e4636 100644 --- a/crates/pumpkin-protocol/src/java/server/play/move_vehicle.rs +++ b/crates/pumpkin-protocol/src/java/server/play/move_vehicle.rs @@ -29,3 +29,19 @@ impl<'a> ServerPacket<'a> for SMoveVehicle { }) } } + +impl crate::ClientPacket for SMoveVehicle { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_f64_be(self.x)?; + write.write_f64_be(self.y)?; + write.write_f64_be(self.z)?; + write.write_f32_be(self.yaw)?; + write.write_f32_be(self.pitch)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/paddle_boat.rs b/crates/pumpkin-protocol/src/java/server/play/paddle_boat.rs index 53ea3f2b6..9a1ad8414 100644 --- a/crates/pumpkin-protocol/src/java/server/play/paddle_boat.rs +++ b/crates/pumpkin-protocol/src/java/server/play/paddle_boat.rs @@ -21,3 +21,16 @@ impl<'a> ServerPacket<'a> for SPaddleBoat { }) } } + +impl crate::ClientPacket for SPaddleBoat { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_bool(self.left_paddle)?; + write.write_bool(self.right_paddle)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/pick_item.rs b/crates/pumpkin-protocol/src/java/server/play/pick_item.rs index ed9d4417f..609fd46be 100644 --- a/crates/pumpkin-protocol/src/java/server/play/pick_item.rs +++ b/crates/pumpkin-protocol/src/java/server/play/pick_item.rs @@ -24,6 +24,19 @@ impl<'a> ServerPacket<'a> for SPickItemFromBlock { } } +impl crate::ClientPacket for SPickItemFromBlock { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.pos)?; + write.write_bool(self.include_data)?; + Ok(()) + } +} + #[java_packet(PLAY_PICK_ITEM_FROM_ENTITY)] pub struct SPickItemFromEntity { pub id: VarInt, @@ -38,3 +51,16 @@ impl<'a> ServerPacket<'a> for SPickItemFromEntity { }) } } + +impl crate::ClientPacket for SPickItemFromEntity { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.id)?; + write.write_bool(self.include_data)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/ping_request.rs b/crates/pumpkin-protocol/src/java/server/play/ping_request.rs index 2bb16afe1..0e7551721 100644 --- a/crates/pumpkin-protocol/src/java/server/play/ping_request.rs +++ b/crates/pumpkin-protocol/src/java/server/play/ping_request.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SPlayPingRequest { }) } } + +impl crate::ClientPacket for SPlayPingRequest { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i64_be(self.payload)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/place_recipe.rs b/crates/pumpkin-protocol/src/java/server/play/place_recipe.rs index a82c110aa..e0afd10ba 100644 --- a/crates/pumpkin-protocol/src/java/server/play/place_recipe.rs +++ b/crates/pumpkin-protocol/src/java/server/play/place_recipe.rs @@ -24,3 +24,17 @@ impl<'a> ServerPacket<'a> for SPlaceRecipe { }) } } + +impl crate::ClientPacket for SPlaceRecipe { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i8(self.container_id)?; + write.write_var_int(&self.recipe_display_id)?; + write.write_bool(self.use_max_items)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_abilities.rs b/crates/pumpkin-protocol/src/java/server/play/player_abilities.rs index 53f4221b0..3bc736be2 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_abilities.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_abilities.rs @@ -21,3 +21,15 @@ impl<'a> ServerPacket<'a> for SPlayerAbilities { }) } } + +impl crate::ClientPacket for SPlayerAbilities { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i8(self.flags)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_action.rs b/crates/pumpkin-protocol/src/java/server/play/player_action.rs index a2d52e1e0..faa6d9809 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_action.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_action.rs @@ -31,6 +31,21 @@ impl<'a> ServerPacket<'a> for SPlayerAction { } } +impl crate::ClientPacket for SPlayerAction { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.status)?; + write.write_block_pos(&self.position)?; + write.write_u8(self.face)?; + write.write_var_int(&self.sequence)?; + Ok(()) + } +} + #[expect(clippy::doc_markdown)] pub enum Status { /// Sent when the player starts digging a block. If the block was insta-mined or the player is in creative mode, the client will not send `Status` = `FinishedDigging``, and will assume the server completed the destruction. To detect this, it is necessary to calculate the block destruction speed server-side. diff --git a/crates/pumpkin-protocol/src/java/server/play/player_command.rs b/crates/pumpkin-protocol/src/java/server/play/player_command.rs index b2a9b9910..57dc1a323 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_command.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_command.rs @@ -70,3 +70,45 @@ impl<'a> ServerPacket<'a> for SPlayerCommand { }) } } + +impl crate::ClientPacket for SPlayerCommand { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + let action_id = if version < &JavaMinecraftVersion::V_1_21_6 { + match self.action { + Action::StartSneaking => 0, + Action::StopSneaking => 1, + Action::LeaveBed => 2, + Action::StartSprinting => 3, + Action::StopSprinting => 4, + Action::StartHorseJump => 5, + Action::StopHorseJump => 6, + Action::OpenVehicleInventory => 7, + Action::StartFlyingElytra => 8, + } + } else { + match self.action { + Action::StartSneaking | Action::StopSneaking => { + return Err(crate::ser::WritingError::Message( + "Sneaking action removed in 1.21.6+".into(), + )); + } + Action::LeaveBed => 0, + Action::StartSprinting => 1, + Action::StopSprinting => 2, + Action::StartHorseJump => 3, + Action::StopHorseJump => 4, + Action::OpenVehicleInventory => 5, + Action::StartFlyingElytra => 6, + } + }; + write.write_var_int(&self.entity_id)?; + write.write_var_int(&VarInt(action_id))?; + write.write_var_int(&self.jump_boost)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_ground.rs b/crates/pumpkin-protocol/src/java/server/play/player_ground.rs index d7d673d54..41c378d4d 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_ground.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_ground.rs @@ -18,3 +18,15 @@ impl<'a> ServerPacket<'a> for SSetPlayerGround { }) } } + +impl crate::ClientPacket for SSetPlayerGround { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_bool(self.on_ground)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_input.rs b/crates/pumpkin-protocol/src/java/server/play/player_input.rs index b8e922ef9..5d98cda31 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_input.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_input.rs @@ -56,3 +56,38 @@ impl<'a> ServerPacket<'a> for SPlayerInput { } } } + +impl crate::ClientPacket for SPlayerInput { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + if version >= &JavaMinecraftVersion::V_1_21_2 { + write.write_i8(self.input)?; + } else { + let mut sideways: f32 = 0.0; + let mut forward: f32 = 0.0; + if (self.input & Self::FORWARD) != 0 { + forward += 1.0; + } + if (self.input & Self::BACKWARD) != 0 { + forward -= 1.0; + } + if (self.input & Self::LEFT) != 0 { + sideways += 1.0; + } + if (self.input & Self::RIGHT) != 0 { + sideways -= 1.0; + } + let jumping = (self.input & Self::JUMP) != 0; + let sneaking = (self.input & Self::SNEAK) != 0; + write.write_f32_be(sideways)?; + write.write_f32_be(forward)?; + write.write_bool(jumping)?; + write.write_bool(sneaking)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_loaded.rs b/crates/pumpkin-protocol/src/java/server/play/player_loaded.rs index 20171b093..ab5bcc2cb 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_loaded.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_loaded.rs @@ -14,3 +14,13 @@ impl<'a> ServerPacket<'a> for SPlayerLoaded { Ok(Self) } } + +impl crate::ClientPacket for SPlayerLoaded { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_position.rs b/crates/pumpkin-protocol/src/java/server/play/player_position.rs index bbd2d1ed8..0d1053374 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_position.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_position.rs @@ -27,3 +27,18 @@ impl<'a> ServerPacket<'a> for SPlayerPosition { }) } } + +impl crate::ClientPacket for SPlayerPosition { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_f64_be(self.position.x)?; + write.write_f64_be(self.position.y)?; + write.write_f64_be(self.position.z)?; + write.write_u8(self.collision)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_position_rotation.rs b/crates/pumpkin-protocol/src/java/server/play/player_position_rotation.rs index 8b6ad6bdf..3cdbdd83c 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_position_rotation.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_position_rotation.rs @@ -34,3 +34,20 @@ impl<'a> ServerPacket<'a> for SPlayerPositionRotation { }) } } + +impl crate::ClientPacket for SPlayerPositionRotation { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_f64_be(self.position.x)?; + write.write_f64_be(self.position.y)?; + write.write_f64_be(self.position.z)?; + write.write_f32_be(self.yaw)?; + write.write_f32_be(self.pitch)?; + write.write_u8(self.collision)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_rotation.rs b/crates/pumpkin-protocol/src/java/server/play/player_rotation.rs index 0dcb64250..bd8f49a5e 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_rotation.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_rotation.rs @@ -22,3 +22,17 @@ impl<'a> ServerPacket<'a> for SPlayerRotation { }) } } + +impl crate::ClientPacket for SPlayerRotation { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_f32_be(self.yaw)?; + write.write_f32_be(self.pitch)?; + write.write_bool(self.ground)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/player_session.rs b/crates/pumpkin-protocol/src/java/server/play/player_session.rs index 469d3d007..ec52d7f1f 100644 --- a/crates/pumpkin-protocol/src/java/server/play/player_session.rs +++ b/crates/pumpkin-protocol/src/java/server/play/player_session.rs @@ -45,3 +45,20 @@ impl<'a> ServerPacket<'a> for SPlayerSession { }) } } + +impl crate::ClientPacket for SPlayerSession { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::{VarInt, ser::NetworkWriteExt}; + write.write_uuid(&self.session_id)?; + write.write_i64_be(self.expires_at)?; + write.write_var_int(&VarInt(self.public_key.len() as i32))?; + write.write_slice(&self.public_key)?; + write.write_var_int(&VarInt(self.key_signature.len() as i32))?; + write.write_slice(&self.key_signature)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/pong.rs b/crates/pumpkin-protocol/src/java/server/play/pong.rs index 363171931..8af952f44 100644 --- a/crates/pumpkin-protocol/src/java/server/play/pong.rs +++ b/crates/pumpkin-protocol/src/java/server/play/pong.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SPlayPong { }) } } + +impl crate::ClientPacket for SPlayPong { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i32_be(self.id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs b/crates/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs index 2e7222c54..97db3e7dd 100644 --- a/crates/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs +++ b/crates/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs @@ -24,3 +24,17 @@ impl<'a> ServerPacket<'a> for SRecipeBookChangeSettings { }) } } + +impl crate::ClientPacket for SRecipeBookChangeSettings { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.book_type)?; + write.write_bool(self.is_open)?; + write.write_bool(self.is_filtering)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs b/crates/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs index 32dc86716..a8a00b881 100644 --- a/crates/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs +++ b/crates/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs @@ -20,3 +20,15 @@ impl<'a> ServerPacket<'a> for SRecipeBookSeenRecipe { }) } } + +impl crate::ClientPacket for SRecipeBookSeenRecipe { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.recipe_display_id)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/rename_item.rs b/crates/pumpkin-protocol/src/java/server/play/rename_item.rs index 8ff7d31dd..fd9b8a26c 100644 --- a/crates/pumpkin-protocol/src/java/server/play/rename_item.rs +++ b/crates/pumpkin-protocol/src/java/server/play/rename_item.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SRenameItem<'a> { }) } } + +impl crate::ClientPacket for SRenameItem<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string_bounded(self.item_name, 32767)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/resource_pack_response.rs b/crates/pumpkin-protocol/src/java/server/play/resource_pack_response.rs index 0c2596426..f01f9918d 100644 --- a/crates/pumpkin-protocol/src/java/server/play/resource_pack_response.rs +++ b/crates/pumpkin-protocol/src/java/server/play/resource_pack_response.rs @@ -52,3 +52,16 @@ impl<'a> ServerPacket<'a> for SPlayResourcePack { }) } } + +impl crate::ClientPacket for SPlayResourcePack { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_uuid(&self.uuid)?; + write.write_var_int(&self.result)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/seen_advancement.rs b/crates/pumpkin-protocol/src/java/server/play/seen_advancement.rs index af8c5a162..de8ce1479 100644 --- a/crates/pumpkin-protocol/src/java/server/play/seen_advancement.rs +++ b/crates/pumpkin-protocol/src/java/server/play/seen_advancement.rs @@ -32,3 +32,23 @@ impl<'a> ServerPacket<'a> for SSeenAdvancement { } } } + +impl crate::ClientPacket for SSeenAdvancement { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::{VarInt, ser::NetworkWriteExt}; + match self { + Self::OpenTab(id) => { + write.write_var_int(&VarInt(0))?; + write.write_string(&id.to_string())?; + } + Self::CloseTab => { + write.write_var_int(&VarInt(1))?; + } + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/select_trade.rs b/crates/pumpkin-protocol/src/java/server/play/select_trade.rs index 267c3271c..7f95389b7 100644 --- a/crates/pumpkin-protocol/src/java/server/play/select_trade.rs +++ b/crates/pumpkin-protocol/src/java/server/play/select_trade.rs @@ -21,3 +21,15 @@ impl<'a> ServerPacket<'a> for SSelectTrade { }) } } + +impl crate::ClientPacket for SSelectTrade { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.selected_slot)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_beacon.rs b/crates/pumpkin-protocol/src/java/server/play/set_beacon.rs index 5e1b8cbde..8d900ca28 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_beacon.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_beacon.rs @@ -22,3 +22,22 @@ impl<'a> ServerPacket<'a> for SSetBeacon { }) } } + +impl crate::ClientPacket for SSetBeacon { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_option( + &self.primary_effect, + crate::ser::NetworkWriteExt::write_var_int, + )?; + write.write_option( + &self.secondary_effect, + crate::ser::NetworkWriteExt::write_var_int, + )?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_command_block.rs b/crates/pumpkin-protocol/src/java/server/play/set_command_block.rs index 65ebd9ab8..f7d3ad169 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_command_block.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_command_block.rs @@ -33,6 +33,21 @@ impl<'a> ServerPacket<'a> for SSetCommandBlock<'a> { } } +impl crate::ClientPacket for SSetCommandBlock<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.pos)?; + write.write_string_bounded(self.command, 32767)?; + write.write_var_int(&self.mode)?; + write.write_i8(self.flags)?; + Ok(()) + } +} + pub enum CommandBlockMode { Chain, Repeating, diff --git a/crates/pumpkin-protocol/src/java/server/play/set_command_minecart.rs b/crates/pumpkin-protocol/src/java/server/play/set_command_minecart.rs index 1e0f44d3a..5385389a8 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_command_minecart.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_command_minecart.rs @@ -24,3 +24,17 @@ impl<'a> ServerPacket<'a> for SSetCommandMinecart<'a> { }) } } + +impl crate::ClientPacket for SSetCommandMinecart<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.entity_id)?; + write.write_string(self.command)?; + write.write_bool(self.track_output)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_creative_slot.rs b/crates/pumpkin-protocol/src/java/server/play/set_creative_slot.rs index 3b6d41bcd..ada340902 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_creative_slot.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_creative_slot.rs @@ -24,3 +24,16 @@ impl<'a> ServerPacket<'a> for SSetCreativeSlot { Ok(Self { slot, clicked_item }) } } + +impl crate::ClientPacket for SSetCreativeSlot { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i16_be(self.slot)?; + self.clicked_item.write_with_version(&mut write, version)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_game_rule.rs b/crates/pumpkin-protocol/src/java/server/play/set_game_rule.rs index 47bb8f03a..a3f1f8c4a 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_game_rule.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_game_rule.rs @@ -21,3 +21,16 @@ impl<'a> ServerPacket<'a> for SSetGameRule<'a> { }) } } + +impl crate::ClientPacket for SSetGameRule<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_string(self.rule)?; + write.write_string(self.value)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_held_item.rs b/crates/pumpkin-protocol/src/java/server/play/set_held_item.rs index 2201618de..a4b6106d2 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_held_item.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_held_item.rs @@ -18,3 +18,15 @@ impl<'a> ServerPacket<'a> for SSetHeldItem { }) } } + +impl crate::ClientPacket for SSetHeldItem { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_i16_be(self.slot)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs b/crates/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs index 6b6917d60..c2d0e40c5 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs @@ -35,3 +35,22 @@ impl<'a> ServerPacket<'a> for SSetJigsawBlock<'a> { }) } } + +impl crate::ClientPacket for SSetJigsawBlock<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.pos)?; + write.write_string_bounded(self.name, 32767)?; + write.write_string_bounded(self.target, 32767)?; + write.write_string_bounded(self.pool, 32767)?; + write.write_string_bounded(self.final_state, 32767)?; + write.write_string_bounded(self.joint, 32767)?; + write.write_var_int(&self.selection_priority)?; + write.write_var_int(&self.placement_priority)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_structure_block.rs b/crates/pumpkin-protocol/src/java/server/play/set_structure_block.rs index 4e957d2b7..c8013571b 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_structure_block.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_structure_block.rs @@ -50,3 +50,30 @@ impl<'a> ServerPacket<'a> for SSetStructureBlock<'a> { }) } } + +impl crate::ClientPacket for SSetStructureBlock<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.location)?; + write.write_var_int(&self.action)?; + write.write_var_int(&self.mode)?; + write.write_string(self.name)?; + write.write_i8(self.offset_x)?; + write.write_i8(self.offset_y)?; + write.write_i8(self.offset_z)?; + write.write_u8(self.size_x)?; + write.write_u8(self.size_y)?; + write.write_u8(self.size_z)?; + write.write_var_int(&self.mirror)?; + write.write_var_int(&self.rotation)?; + write.write_string(self.metadata)?; + write.write_f32_be(self.integrity)?; + write.write_var_long(&self.seed)?; + write.write_u8(self.flags)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/set_test_block.rs b/crates/pumpkin-protocol/src/java/server/play/set_test_block.rs index 8f329fe47..337a33a28 100644 --- a/crates/pumpkin-protocol/src/java/server/play/set_test_block.rs +++ b/crates/pumpkin-protocol/src/java/server/play/set_test_block.rs @@ -29,6 +29,20 @@ impl<'a> ServerPacket<'a> for SSetTestBlock<'a> { } } +impl crate::ClientPacket for SSetTestBlock<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.position)?; + self.mode.write(&mut write)?; + write.write_string(self.message)?; + Ok(()) + } +} + #[derive(Clone, Copy, Debug)] pub enum TestBlockMode { Start, @@ -47,4 +61,17 @@ impl TestBlockMode { _ => Err(ReadingError::Message("Invalid TestBlockMode".to_string())), } } + + fn write( + self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + let val = match self { + Self::Start => 0, + Self::Log => 1, + Self::Fail => 2, + Self::Accept => 3, + }; + write.write_var_int(&crate::VarInt(val)) + } } diff --git a/crates/pumpkin-protocol/src/java/server/play/spectate_entity.rs b/crates/pumpkin-protocol/src/java/server/play/spectate_entity.rs index 82b0dc4e4..f53bad14c 100644 --- a/crates/pumpkin-protocol/src/java/server/play/spectate_entity.rs +++ b/crates/pumpkin-protocol/src/java/server/play/spectate_entity.rs @@ -19,3 +19,15 @@ impl<'a> ServerPacket<'a> for SSpectateEntity { }) } } + +impl crate::ClientPacket for SSpectateEntity { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_uuid(&self.target)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/swing_arm.rs b/crates/pumpkin-protocol/src/java/server/play/swing_arm.rs index 2a1a92030..1040b1b65 100644 --- a/crates/pumpkin-protocol/src/java/server/play/swing_arm.rs +++ b/crates/pumpkin-protocol/src/java/server/play/swing_arm.rs @@ -20,3 +20,15 @@ impl<'a> ServerPacket<'a> for SSwingArm { }) } } + +impl crate::ClientPacket for SSwingArm { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.hand)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs b/crates/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs index f7f0693f8..6c07ac7f4 100644 --- a/crates/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs +++ b/crates/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs @@ -21,3 +21,15 @@ impl<'a> ServerPacket<'a> for STeleportToEntity { }) } } + +impl crate::ClientPacket for STeleportToEntity { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_uuid(&self.target)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs b/crates/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs index 970c3bd0b..c5e28a8a2 100644 --- a/crates/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs +++ b/crates/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs @@ -28,6 +28,20 @@ impl<'a> ServerPacket<'a> for STestInstanceBlockAction<'a> { } } +impl crate::ClientPacket for STestInstanceBlockAction<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.pos)?; + self.action.write(&mut write)?; + self.data.write(&mut write)?; + Ok(()) + } +} + #[derive(Clone, Copy, Debug)] pub enum TestInstanceBlockAction { Init, @@ -54,6 +68,22 @@ impl TestInstanceBlockAction { )), } } + + fn write( + self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + let val = match self { + Self::Init => 0, + Self::Query => 1, + Self::Set => 2, + Self::Reset => 3, + Self::Save => 4, + Self::Export => 5, + Self::Run => 6, + }; + write.write_var_int(&VarInt(val)) + } } pub struct VarIntVector3 { @@ -70,6 +100,16 @@ impl VarIntVector3 { z: bytebuf.get_var_int()?, }) } + + fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.x)?; + write.write_var_int(&self.y)?; + write.write_var_int(&self.z)?; + Ok(()) + } } pub struct TestInstanceBlockData<'a> { @@ -116,6 +156,31 @@ impl<'a> TestInstanceBlockData<'a> { error_message, }) } + + fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_option(&self.test, |w, t| w.write_string(t))?; + self.size.write(write)?; + let rot_val = match self.rotation { + pumpkin_data::block_rotation::Rotation::None => 0, + pumpkin_data::block_rotation::Rotation::Clockwise90 => 1, + pumpkin_data::block_rotation::Rotation::Rotate180 => 2, + pumpkin_data::block_rotation::Rotation::CounterClockwise90 => 3, + }; + write.write_var_int(&VarInt(rot_val))?; + write.write_bool(self.ignore_entities)?; + let status_val = match self.status { + TestInstanceBlockStatus::Cleared => 0, + TestInstanceBlockStatus::Running => 1, + TestInstanceBlockStatus::Success => 2, + TestInstanceBlockStatus::Failed => 3, + }; + write.write_var_int(&VarInt(status_val))?; + write.write_option(&self.error_message, |w, msg| w.write_string(msg))?; + Ok(()) + } } #[derive(Clone, Copy, Debug)] diff --git a/crates/pumpkin-protocol/src/java/server/play/update_sign.rs b/crates/pumpkin-protocol/src/java/server/play/update_sign.rs index 98799582d..407e43044 100644 --- a/crates/pumpkin-protocol/src/java/server/play/update_sign.rs +++ b/crates/pumpkin-protocol/src/java/server/play/update_sign.rs @@ -31,3 +31,20 @@ impl<'a> ServerPacket<'a> for SUpdateSign<'a> { }) } } + +impl crate::ClientPacket for SUpdateSign<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_block_pos(&self.location)?; + write.write_bool(self.is_front_text)?; + write.write_string_bounded(self.line_1, MAX_LINE_LENGTH)?; + write.write_string_bounded(self.line_2, MAX_LINE_LENGTH)?; + write.write_string_bounded(self.line_3, MAX_LINE_LENGTH)?; + write.write_string_bounded(self.line_4, MAX_LINE_LENGTH)?; + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/use_item.rs b/crates/pumpkin-protocol/src/java/server/play/use_item.rs index cbaa955e8..575cc1f62 100644 --- a/crates/pumpkin-protocol/src/java/server/play/use_item.rs +++ b/crates/pumpkin-protocol/src/java/server/play/use_item.rs @@ -35,3 +35,20 @@ impl<'a> ServerPacket<'a> for SUseItem { }) } } + +impl crate::ClientPacket for SUseItem { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.hand)?; + write.write_var_int(&self.sequence)?; + if version >= &JavaMinecraftVersion::V_1_21_2 { + write.write_f32_be(self.yaw)?; + write.write_f32_be(self.pitch)?; + } + Ok(()) + } +} diff --git a/crates/pumpkin-protocol/src/java/server/play/use_item_on.rs b/crates/pumpkin-protocol/src/java/server/play/use_item_on.rs index d04fe3914..9694629c7 100644 --- a/crates/pumpkin-protocol/src/java/server/play/use_item_on.rs +++ b/crates/pumpkin-protocol/src/java/server/play/use_item_on.rs @@ -49,3 +49,25 @@ impl<'a> ServerPacket<'a> for SUseItemOn { }) } } + +impl crate::ClientPacket for SUseItemOn { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + use crate::ser::NetworkWriteExt; + write.write_var_int(&self.hand)?; + write.write_block_pos(&self.position)?; + write.write_var_int(&self.face)?; + write.write_f32_be(self.cursor_pos.x)?; + write.write_f32_be(self.cursor_pos.y)?; + write.write_f32_be(self.cursor_pos.z)?; + write.write_bool(self.inside_block)?; + if version >= &JavaMinecraftVersion::V_1_21_5 { + write.write_bool(self.is_against_world_border)?; + } + write.write_var_int(&self.sequence)?; + Ok(()) + } +} diff --git a/crates/pumpkin/src/command/client_suggestions.rs b/crates/pumpkin/src/command/client_suggestions.rs index 8c409fb6f..29050e4f2 100644 --- a/crates/pumpkin/src/command/client_suggestions.rs +++ b/crates/pumpkin/src/command/client_suggestions.rs @@ -36,6 +36,12 @@ pub async fn send_c_commands_packet( continue; } + // For double-slash commands, if the single-slash alias (/set) exists, only + // register the single-slash literal for the Java client. + if key.starts_with("//") && fallback_dispatcher.commands.contains_key(&key[1..]) { + continue; + } + let Ok(tree) = fallback_dispatcher.get_tree(key) else { continue; }; @@ -51,10 +57,16 @@ pub async fn send_c_commands_packet( let (is_executable, child_nodes) = nodes_to_proto_node_builders(&cmd_src, &tree.nodes, &tree.children); + let name = if key.starts_with("//") { + &key[1..] + } else { + key.as_str() + }; + let proto_node = ProtoNodeBuilder { child_nodes, node_type: ProtoNodeType::Literal { - name: key, + name, is_executable, redirect_target: None, restricted: false, @@ -104,26 +116,39 @@ pub async fn send_c_commands_packet( .values() .copied() .filter(|id| { - let disabled = match &dispatcher.tree[*id] { - AttachedNode::Literal(child) => { - dispatcher.is_disabled(&child.meta.literal_lowercase) - } - AttachedNode::Command(child) => { - dispatcher.is_disabled(&child.meta.literal_lowercase) - } - _ => false, + let (disabled, name) = match &dispatcher.tree[*id] { + AttachedNode::Literal(child) => ( + dispatcher.is_disabled(&child.meta.literal_lowercase), + child.meta.literal.as_ref(), + ), + AttachedNode::Command(child) => ( + dispatcher.is_disabled(&child.meta.literal_lowercase), + child.meta.literal.as_ref(), + ), + _ => (false, ""), }; - !disabled + if disabled { + return false; + } + if name.starts_with("//") && dispatcher.tree.get(&name[1..]).is_some() { + return false; + } + true }) .map(|id| resolve_node_id(id, node_id_offset, root_node_index)) .map(|i| VarInt(i as i32)) .collect(); } AttachedNode::Literal(literal_attached_node) => { + let name = if literal_attached_node.meta.literal.starts_with("//") { + &literal_attached_node.meta.literal[1..] + } else { + &literal_attached_node.meta.literal + }; let node = ProtoNode { children, node_type: ProtoNodeType::Literal { - name: &literal_attached_node.meta.literal, + name, is_executable: literal_attached_node.owned.command.is_some(), redirect_target, restricted: !satisfies_requirements, @@ -132,10 +157,15 @@ pub async fn send_c_commands_packet( proto_nodes.push(node); } AttachedNode::Command(command_attached_node) => { + let name = if command_attached_node.meta.literal.starts_with("//") { + &command_attached_node.meta.literal[1..] + } else { + &command_attached_node.meta.literal + }; let node = ProtoNode { children, node_type: ProtoNodeType::Literal { - name: &command_attached_node.meta.literal, + name, is_executable: command_attached_node.owned.command.is_some(), redirect_target, restricted: !satisfies_requirements, @@ -281,6 +311,7 @@ struct BuilderContext<'a> { enums: &'a mut Vec, } +#[expect(clippy::too_many_lines)] pub async fn send_bedrock_commands_packet( player: &Arc, server: &Server, @@ -298,6 +329,10 @@ pub async fn send_bedrock_commands_packet( continue; } + if key.starts_with("//") && fallback_dispatcher.commands.contains_key(&key[1..]) { + continue; + } + let Ok(tree) = fallback_dispatcher.get_tree(key) else { continue; }; @@ -365,6 +400,10 @@ pub async fn send_bedrock_commands_packet( continue; } + if name.starts_with("//") && dispatcher.tree.get(&name[1..]).is_some() { + continue; + } + let mut ctx = BuilderContext { enum_values: &mut enum_values, enums: &mut enums, diff --git a/crates/pumpkin/src/command/dispatcher.rs b/crates/pumpkin/src/command/dispatcher.rs index b7d292f03..00c6935be 100644 --- a/crates/pumpkin/src/command/dispatcher.rs +++ b/crates/pumpkin/src/command/dispatcher.rs @@ -663,23 +663,61 @@ impl CommandDispatcher { /// Register a command with the dispatcher. pub fn register>(&mut self, tree: CommandTree, permission: P) { - let mut names = tree.names.iter(); + let names = tree.names.clone(); + let mut names_iter = names.iter(); let permission = permission.into(); - let Some(primary_name) = names.next() else { + let Some(primary_name) = names_iter.next() else { tracing::warn!("Command registration skipped: command tree has no names"); return; }; - for name in names { + for name in names_iter { self.commands .insert(name.clone(), Command::Alias(primary_name.clone())); self.permissions.insert(name.clone(), permission.clone()); + + // For double-slash or slash-prefixed commands (like WorldEdit's //set or /set), + // automatically register the alternate slash variant as an alias. + if let Some(stripped) = name.strip_prefix("//") { + let single_slash = format!("/{stripped}"); + if !self.commands.contains_key(&single_slash) && &single_slash != primary_name { + self.commands + .insert(single_slash.clone(), Command::Alias(primary_name.clone())); + self.permissions.insert(single_slash, permission.clone()); + } + } else if let Some(stripped) = name.strip_prefix('/') { + let double_slash = format!("//{stripped}"); + if !self.commands.contains_key(&double_slash) && &double_slash != primary_name { + self.commands + .insert(double_slash.clone(), Command::Alias(primary_name.clone())); + self.permissions.insert(double_slash, permission.clone()); + } + } } - self.permissions.insert(primary_name.clone(), permission); + self.permissions + .insert(primary_name.clone(), permission.clone()); self.commands .insert(primary_name.clone(), Command::Tree(tree)); + + // For double-slash or slash-prefixed primary commands (like //set or /set), + // automatically register the alternate slash variant as an alias. + if let Some(stripped) = primary_name.strip_prefix("//") { + let single_slash = format!("/{stripped}"); + if !self.commands.contains_key(&single_slash) { + self.commands + .insert(single_slash.clone(), Command::Alias(primary_name.clone())); + self.permissions.insert(single_slash, permission); + } + } else if let Some(stripped) = primary_name.strip_prefix('/') { + let double_slash = format!("//{stripped}"); + if !self.commands.contains_key(&double_slash) { + self.commands + .insert(double_slash.clone(), Command::Alias(primary_name.clone())); + self.permissions.insert(double_slash, permission); + } + } } /// Remove a command from the dispatcher by its primary name. @@ -695,6 +733,30 @@ impl CommandDispatcher { } } + if let Some(stripped) = name.strip_prefix("//") { + let single_slash = format!("/{stripped}"); + for (key, value) in &self.commands { + if key == &single_slash { + to_remove.push(key.clone()); + } else if let Command::Alias(target) = value + && target == &single_slash + { + to_remove.push(key.clone()); + } + } + } else if let Some(stripped) = name.strip_prefix('/') { + let double_slash = format!("//{stripped}"); + for (key, value) in &self.commands { + if key == &double_slash { + to_remove.push(key.clone()); + } else if let Command::Alias(target) = value + && target == &double_slash + { + to_remove.push(key.clone()); + } + } + } + for key in to_remove { self.commands.remove(&key); self.permissions.remove(&key); @@ -868,4 +930,20 @@ mod test { assert!(selected.is(&error_types::DISPATCHER_UNKNOWN_COMMAND)); } + + #[test] + fn double_slash_command_registration_and_lookup() { + let mut dispatcher = super::CommandDispatcher::default(); + let tree = CommandTree::new(["//set", "/set"], "WorldEdit set block"); + dispatcher.register(tree, "worldedit.set"); + + assert!(dispatcher.commands.contains_key("//set")); + assert!(dispatcher.commands.contains_key("/set")); + assert!(dispatcher.get_tree("//set").is_ok()); + assert!(dispatcher.get_tree("/set").is_ok()); + + dispatcher.unregister("//set"); + assert!(!dispatcher.commands.contains_key("//set")); + assert!(!dispatcher.commands.contains_key("/set")); + } } diff --git a/crates/pumpkin/src/command/node/dispatcher.rs b/crates/pumpkin/src/command/node/dispatcher.rs index 487c809d4..b7ecc5140 100644 --- a/crates/pumpkin/src/command/node/dispatcher.rs +++ b/crates/pumpkin/src/command/node/dispatcher.rs @@ -144,7 +144,20 @@ impl CommandDispatcher { /// off through the server configuration. #[must_use] pub fn is_disabled(&self, name: &str) -> bool { - self.disabled.contains(name) + if self.disabled.contains(name) { + return true; + } + if name.starts_with('/') && self.disabled.contains(name.trim_start_matches('/')) { + return true; + } + if !name.starts_with('/') { + let single = format!("/{name}"); + let double = format!("//{name}"); + if self.disabled.contains(&single) || self.disabled.contains(&double) { + return true; + } + } + false } /// Returns `true` if a command (or alias) with the given name is registered @@ -184,13 +197,9 @@ impl CommandDispatcher { } /// Extracts the command name (the first whitespace-separated token) from a - /// raw input string, ignoring any leading slash. + /// raw input string. fn command_name(input: &str) -> &str { - input - .trim_start_matches('/') - .split_whitespace() - .next() - .unwrap_or("") + input.split_whitespace().next().unwrap_or("") } /// Registers a command which can then be dispatched. @@ -200,7 +209,51 @@ impl CommandDispatcher { /// unregister a command. This is due to redirection to /// potentially unregistered (freed) nodes. pub fn register(&mut self, command_node: impl Into) -> CommandNodeId { - self.tree.add_child_to_root(command_node) + let node = command_node.into(); + let name = node.meta.literal.to_string(); + let main_node_id = self.tree.add_child_to_root(node); + + // For double-slash or slash-prefixed commands (e.g. //set or /set), + // automatically register the alternate slash variant as an alias. + if let Some(stripped) = name.strip_prefix("//") { + let single_slash = format!("/{stripped}"); + if self.tree.get(&single_slash).is_none() { + let main_node = &self.tree[main_node_id]; + let description = main_node.meta.description.clone(); + let mut alias = crate::command::argument_builder::CommandArgumentBuilder::new( + single_slash, + description, + ); + if let Some(executor) = &main_node.owned.command { + alias = alias.executes_arc(executor.clone()); + alias = alias.overwrite_requirements(main_node.owned.requirements.clone()); + } + alias = alias.redirect(crate::command::node::Redirection::Local( + main_node_id.into(), + )); + self.tree.add_child_to_root(alias.build()); + } + } else if let Some(stripped) = name.strip_prefix('/') { + let double_slash = format!("//{stripped}"); + if self.tree.get(&double_slash).is_none() { + let main_node = &self.tree[main_node_id]; + let description = main_node.meta.description.clone(); + let mut alias = crate::command::argument_builder::CommandArgumentBuilder::new( + double_slash, + description, + ); + if let Some(executor) = &main_node.owned.command { + alias = alias.executes_arc(executor.clone()); + alias = alias.overwrite_requirements(main_node.owned.requirements.clone()); + } + alias = alias.redirect(crate::command::node::Redirection::Local( + main_node_id.into(), + )); + self.tree.add_child_to_root(alias.build()); + } + } + + main_node_id } /// Registers a command which can then be dispatched, along with its @@ -457,8 +510,19 @@ impl CommandDispatcher { "Source provided to this command was a dummy source" ); - if let Some(sliced) = input.strip_prefix("/") { - input = sliced; + // If input starts with '/', but the command with that leading slash is NOT + // registered, while the stripped command IS registered (or if it's an unknown command + // starting with a single slash, e.g. from console input), strip one leading slash. + // For double-slash commands like WorldEdit's `//set`, the client sends `/set`, which + // matches a registered `/set` or `//set` command and preserves the slash. + if let Some(sliced) = input.strip_prefix('/') { + let first_token = input.split_whitespace().next().unwrap_or(""); + let sliced_token = sliced.split_whitespace().next().unwrap_or(""); + if !self.has_command(first_token) + && (self.has_command(sliced_token) || !first_token.starts_with("//")) + { + input = sliced; + } } // A command that has been turned off in the configuration must behave as @@ -1259,4 +1323,21 @@ mod test { Ok(1) ); } + + #[tokio::test] + async fn double_slash_command_execution() { + let mut dispatcher = CommandDispatcher::new(); + let executor: for<'c> fn(&'c CommandContext) -> CommandExecutorResult<'c> = + |_| Box::pin(async move { Ok(42) }); + + dispatcher.register( + CommandArgumentBuilder::new("//set", "WorldEdit set command").executes(executor), + ); + + let source = CommandSource::dummy(); + // Direct execution with //set + assert_eq!(dispatcher.execute_input("//set", &source).await, Ok(42)); + // Execution via /set alias (as sent by Java client for //set) + assert_eq!(dispatcher.execute_input("/set", &source).await, Ok(42)); + } }