From 1842e8e6b19f05b5a360cde95593cb8b9f1cbcf0 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Sun, 3 Nov 2024 12:25:10 +0100 Subject: [PATCH] Parse Serverbound Packet IDs from JSON as well Before only Clientbound Packet IDs where parsed from JSON, Now Serverbound Packet IDs are parsed from JSON as well. This makes porting to new minecraft version much easier --- .../kotlin/de/snowii/extractor/Extractor.kt | 2 +- .../de/snowii/extractor/extractors/Recipes.kt | 1 - .../extractors/{Screen.kt => Screens.kt} | 2 +- pumpkin-macros/src/lib.rs | 23 ++- pumpkin-macros/src/packet.rs | 1 - pumpkin-protocol/src/bytebuf/packet_id.rs | 6 +- pumpkin-protocol/src/lib.rs | 6 +- pumpkin-protocol/src/server/config/mod.rs | 15 -- .../config/s_acknowledge_finish_config.rs | 3 + .../src/server/config/s_client_information.rs | 3 + .../src/server/config/s_known_packs.rs | 3 + .../src/server/config/s_plugin_message.rs | 3 + pumpkin-protocol/src/server/handshake/mod.rs | 3 + pumpkin-protocol/src/server/login/mod.rs | 12 -- .../src/server/login/s_encryption_response.rs | 3 + .../src/server/login/s_login_response.rs | 3 + .../src/server/login/s_login_start.rs | 3 + .../src/server/login/s_plugin_response.rs | 2 + pumpkin-protocol/src/server/play/mod.rs | 70 ------- .../src/server/play/s_chat_command.rs | 3 + .../src/server/play/s_chat_message.rs | 2 + .../src/server/play/s_click_container.rs | 2 + .../src/server/play/s_client_information.rs | 3 + .../src/server/play/s_close_container.rs | 2 + .../src/server/play/s_confirm_teleport.rs | 3 + .../src/server/play/s_interact.rs | 2 + .../src/server/play/s_keep_alive.rs | 2 + .../src/server/play/s_ping_request.rs | 2 + .../src/server/play/s_player_abilities.rs | 2 + .../src/server/play/s_player_action.rs | 2 + .../src/server/play/s_player_command.rs | 2 + .../src/server/play/s_player_ground.rs | 2 + .../src/server/play/s_player_position.rs | 3 + .../server/play/s_player_position_rotation.rs | 3 + .../src/server/play/s_player_rotation.rs | 3 + .../src/server/play/s_set_creative_slot.rs | 3 + .../src/server/play/s_set_held_item.rs | 2 + .../src/server/play/s_swing_arm.rs | 3 + .../src/server/play/s_use_item.rs | 2 + .../src/server/play/s_use_item_on.rs | 2 + pumpkin-protocol/src/server/status/mod.rs | 9 - .../src/server/status/s_ping_request.rs | 3 + .../src/server/status/s_status_request.rs | 3 + pumpkin/src/client/mod.rs | 151 +++++++------- pumpkin/src/entity/player.rs | 186 +++++++----------- 45 files changed, 251 insertions(+), 315 deletions(-) rename extractor/src/main/kotlin/de/snowii/extractor/extractors/{Screen.kt => Screens.kt} (95%) diff --git a/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt b/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt index dfedee0c3..a9ff4007d 100644 --- a/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt +++ b/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt @@ -28,7 +28,7 @@ class Extractor : ModInitializer { Particles(), SyncedRegistries(), Packet(), - Screen(), + Screens(), Items(), Blocks(), ) diff --git a/extractor/src/main/kotlin/de/snowii/extractor/extractors/Recipes.kt b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Recipes.kt index bc58511ae..de6243cce 100644 --- a/extractor/src/main/kotlin/de/snowii/extractor/extractors/Recipes.kt +++ b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Recipes.kt @@ -46,7 +46,6 @@ class Recipes : Extractor.Extractor { } } recipeJson.add("ingredients", ingredientArray) -// recipeJson.addProperty("placementSlots", gson.toJson(recipe.ingredientPlacement.placementSlots)) recipesJson.add(recipeJson) } diff --git a/extractor/src/main/kotlin/de/snowii/extractor/extractors/Screen.kt b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Screens.kt similarity index 95% rename from extractor/src/main/kotlin/de/snowii/extractor/extractors/Screen.kt rename to extractor/src/main/kotlin/de/snowii/extractor/extractors/Screens.kt index 54ad23418..42b8a165f 100644 --- a/extractor/src/main/kotlin/de/snowii/extractor/extractors/Screen.kt +++ b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Screens.kt @@ -7,7 +7,7 @@ import de.snowii.extractor.Extractor import net.minecraft.registry.Registries import net.minecraft.server.MinecraftServer -class Screen : Extractor.Extractor { +class Screens : Extractor.Extractor { override fun fileName(): String { return "screens.json" } diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index 989641c9c..cd7c79580 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -17,7 +17,28 @@ pub fn client_packet(input: TokenStream, item: TokenStream) -> TokenStream { let gen = quote! { #item - impl #impl_generics crate::bytebuf::packet_id::ClientPacketID for #name #ty_generics { + impl #impl_generics crate::bytebuf::packet_id::Packet for #name #ty_generics { + const PACKET_ID: i32 = #input as i32; + } + }; + + gen.into() +} + +#[proc_macro_attribute] +pub fn server_packet(input: TokenStream, item: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(item.clone()).unwrap(); + + let name = &ast.ident; + + let (impl_generics, ty_generics, _) = ast.generics.split_for_impl(); + + let input: proc_macro2::TokenStream = packet::packet_serverbound(input); + let item: proc_macro2::TokenStream = item.into(); + + let gen = quote! { + #item + impl #impl_generics crate::bytebuf::packet_id::Packet for #name #ty_generics { const PACKET_ID: i32 = #input as i32; } }; diff --git a/pumpkin-macros/src/packet.rs b/pumpkin-macros/src/packet.rs index 2b0b838e3..0d0a7c1a7 100644 --- a/pumpkin-macros/src/packet.rs +++ b/pumpkin-macros/src/packet.rs @@ -30,7 +30,6 @@ pub(crate) fn packet_clientbound(item: TokenStream) -> proc_macro2::TokenStream quote! { #id } } -#[expect(dead_code)] pub(crate) fn packet_serverbound(item: TokenStream) -> proc_macro2::TokenStream { let input_string = item.to_string(); let packet_name = input_string.trim_matches('"'); diff --git a/pumpkin-protocol/src/bytebuf/packet_id.rs b/pumpkin-protocol/src/bytebuf/packet_id.rs index c8d17c695..f2505b832 100644 --- a/pumpkin-protocol/src/bytebuf/packet_id.rs +++ b/pumpkin-protocol/src/bytebuf/packet_id.rs @@ -130,13 +130,13 @@ impl<'de> Deserialize<'de> for VarLong { } } -pub trait ClientPacketID { +pub trait Packet { const PACKET_ID: VarIntType; } impl

ClientPacket for P where - P: ClientPacketID + Serialize, + P: Packet + Serialize, { fn write(&self, bytebuf: &mut ByteBuffer) { let mut serializer = serializer::Serializer::new(ByteBuffer::empty()); @@ -151,7 +151,7 @@ where impl

ServerPacket for P where - P: DeserializeOwned, + P: Packet + DeserializeOwned, { fn read(bytebuf: &mut ByteBuffer) -> Result { let deserializer = deserializer::Deserializer::new(bytebuf); diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 5def3df30..b550ff43e 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -1,4 +1,4 @@ -use bytebuf::{packet_id::ClientPacketID, ByteBuffer, DeserializerError}; +use bytebuf::{packet_id::Packet, ByteBuffer, DeserializerError}; use pumpkin_core::text::{style::Style, TextComponent}; use serde::{Deserialize, Serialize}; @@ -72,11 +72,11 @@ pub struct RawPacket { pub bytebuf: ByteBuffer, } -pub trait ClientPacket: ClientPacketID { +pub trait ClientPacket: Packet { fn write(&self, bytebuf: &mut ByteBuffer); } -pub trait ServerPacket: Sized { +pub trait ServerPacket: Packet + Sized { fn read(bytebuf: &mut ByteBuffer) -> Result; } diff --git a/pumpkin-protocol/src/server/config/mod.rs b/pumpkin-protocol/src/server/config/mod.rs index b59b0e705..164c8e60a 100644 --- a/pumpkin-protocol/src/server/config/mod.rs +++ b/pumpkin-protocol/src/server/config/mod.rs @@ -3,22 +3,7 @@ mod s_client_information; mod s_known_packs; mod s_plugin_message; -use num_derive::FromPrimitive; pub use s_acknowledge_finish_config::*; pub use s_client_information::*; pub use s_known_packs::*; pub use s_plugin_message::*; - -/// DO NOT CHANGE ORDER -/// This Enum has the exact order like vanilla, Vanilla parses their Packet IDs from the enum order. Its also way easier to port. -#[derive(FromPrimitive)] -pub enum ServerboundConfigPackets { - ClientInformation, - CookieResponse, - PluginMessage, - AcknowledgedFinish, - KeepAlive, - Pong, - ResourcePackResponse, - KnownPacks, -} diff --git a/pumpkin-protocol/src/server/config/s_acknowledge_finish_config.rs b/pumpkin-protocol/src/server/config/s_acknowledge_finish_config.rs index 20b81d462..7796b8510 100644 --- a/pumpkin-protocol/src/server/config/s_acknowledge_finish_config.rs +++ b/pumpkin-protocol/src/server/config/s_acknowledge_finish_config.rs @@ -1,2 +1,5 @@ +use pumpkin_macros::server_packet; + #[derive(serde::Deserialize)] +#[server_packet("configuration:finish_configuration")] pub struct SAcknowledgeFinishConfig {} diff --git a/pumpkin-protocol/src/server/config/s_client_information.rs b/pumpkin-protocol/src/server/config/s_client_information.rs index 4ec1a6984..ba6823f7e 100644 --- a/pumpkin-protocol/src/server/config/s_client_information.rs +++ b/pumpkin-protocol/src/server/config/s_client_information.rs @@ -1,6 +1,9 @@ +use pumpkin_macros::server_packet; + use crate::VarInt; #[derive(serde::Deserialize)] +#[server_packet("configuration:client_information")] pub struct SClientInformationConfig { pub locale: String, // 16 pub view_distance: i8, diff --git a/pumpkin-protocol/src/server/config/s_known_packs.rs b/pumpkin-protocol/src/server/config/s_known_packs.rs index f468609cd..138cf2ae2 100644 --- a/pumpkin-protocol/src/server/config/s_known_packs.rs +++ b/pumpkin-protocol/src/server/config/s_known_packs.rs @@ -1,6 +1,9 @@ +use pumpkin_macros::server_packet; + use crate::VarInt; #[derive(serde::Deserialize)] +#[server_packet("configuration:select_known_packs")] pub struct SKnownPacks { pub known_pack_count: VarInt, // known_packs: &'a [KnownPack] diff --git a/pumpkin-protocol/src/server/config/s_plugin_message.rs b/pumpkin-protocol/src/server/config/s_plugin_message.rs index d2341a9de..7007f35d8 100644 --- a/pumpkin-protocol/src/server/config/s_plugin_message.rs +++ b/pumpkin-protocol/src/server/config/s_plugin_message.rs @@ -1,8 +1,11 @@ +use pumpkin_macros::server_packet; + use crate::{ bytebuf::{ByteBuffer, DeserializerError}, Identifier, ServerPacket, }; +#[server_packet("configuration:custom_payload")] pub struct SPluginMessage { pub channel: Identifier, pub data: Vec, diff --git a/pumpkin-protocol/src/server/handshake/mod.rs b/pumpkin-protocol/src/server/handshake/mod.rs index cbed59db7..a1d743528 100644 --- a/pumpkin-protocol/src/server/handshake/mod.rs +++ b/pumpkin-protocol/src/server/handshake/mod.rs @@ -1,8 +1,11 @@ +use pumpkin_macros::server_packet; + use crate::{ bytebuf::{ByteBuffer, DeserializerError}, ConnectionState, ServerPacket, VarInt, }; +#[server_packet("handshake:intention")] pub struct SHandShake { pub protocol_version: VarInt, pub server_address: String, // 255 diff --git a/pumpkin-protocol/src/server/login/mod.rs b/pumpkin-protocol/src/server/login/mod.rs index aa0aca2ec..872f74bbc 100644 --- a/pumpkin-protocol/src/server/login/mod.rs +++ b/pumpkin-protocol/src/server/login/mod.rs @@ -3,19 +3,7 @@ mod s_login_response; mod s_login_start; mod s_plugin_response; -use num_derive::FromPrimitive; pub use s_encryption_response::*; pub use s_login_response::*; pub use s_login_start::*; pub use s_plugin_response::*; - -/// DO NOT CHANGE ORDER -/// This Enum has the exact order like vanilla, Vanilla parses their Packet IDs from the enum order. Its also way easier to port. -#[derive(FromPrimitive)] -pub enum ServerboundLoginPackets { - LoginStart, - EncryptionResponse, - PluginResponse, - LoginAcknowledged, - CookieResponse, -} diff --git a/pumpkin-protocol/src/server/login/s_encryption_response.rs b/pumpkin-protocol/src/server/login/s_encryption_response.rs index 6c3a5d3ef..06c224624 100644 --- a/pumpkin-protocol/src/server/login/s_encryption_response.rs +++ b/pumpkin-protocol/src/server/login/s_encryption_response.rs @@ -1,8 +1,11 @@ +use pumpkin_macros::server_packet; + use crate::{ bytebuf::{ByteBuffer, DeserializerError}, ServerPacket, VarInt, }; +#[server_packet("login:key")] pub struct SEncryptionResponse { pub shared_secret_length: VarInt, pub shared_secret: Vec, diff --git a/pumpkin-protocol/src/server/login/s_login_response.rs b/pumpkin-protocol/src/server/login/s_login_response.rs index 1a1d432d8..aaf250bf6 100644 --- a/pumpkin-protocol/src/server/login/s_login_response.rs +++ b/pumpkin-protocol/src/server/login/s_login_response.rs @@ -1,3 +1,6 @@ +use pumpkin_macros::server_packet; + // Acknowledgement to the Login Success packet sent to the server. #[derive(serde::Deserialize)] +#[server_packet("login:login_acknowledged")] pub struct SLoginAcknowledged {} diff --git a/pumpkin-protocol/src/server/login/s_login_start.rs b/pumpkin-protocol/src/server/login/s_login_start.rs index 41aaa96b9..7a4a724da 100644 --- a/pumpkin-protocol/src/server/login/s_login_start.rs +++ b/pumpkin-protocol/src/server/login/s_login_start.rs @@ -1,8 +1,11 @@ +use pumpkin_macros::server_packet; + use crate::{ bytebuf::{ByteBuffer, DeserializerError}, ServerPacket, }; +#[server_packet("login:hello")] pub struct SLoginStart { pub name: String, // 16 pub uuid: uuid::Uuid, diff --git a/pumpkin-protocol/src/server/login/s_plugin_response.rs b/pumpkin-protocol/src/server/login/s_plugin_response.rs index 9f3ec74ec..24656471e 100644 --- a/pumpkin-protocol/src/server/login/s_plugin_response.rs +++ b/pumpkin-protocol/src/server/login/s_plugin_response.rs @@ -3,7 +3,9 @@ use crate::{ ServerPacket, VarInt, }; use bytes::BytesMut; +use pumpkin_macros::server_packet; +#[server_packet("login:custom_query_answer")] pub struct SLoginPluginResponse { pub message_id: VarInt, pub data: Option, diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index 0c1f9b9d1..6d97dd3d6 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -20,7 +20,6 @@ mod s_swing_arm; mod s_use_item; mod s_use_item_on; -use num_derive::FromPrimitive; pub use s_chat_command::*; pub use s_chat_message::*; pub use s_click_container::*; @@ -42,72 +41,3 @@ pub use s_set_held_item::*; pub use s_swing_arm::*; pub use s_use_item::*; pub use s_use_item_on::*; - -/// DO NOT CHANGE ORDER -/// This Enum has the exact order like vanilla, Vanilla parses their Packet IDs from the enum order. Its also way easier to port. -#[derive(FromPrimitive)] -pub enum ServerboundPlayPackets { - TeleportConfirm, - QueryBlockNbt, - SelectBundleItem, - SetDifficulty, - ChatAck, - ChatCommandUnsigned, - ChatCommand, - ChatMessage, - ChatSessionUpdate, - ChunkBatchAck, - ClientStatus, - ClientTickEnd, - ClientSettings, - TabComplete, - ConfigurationAck, - ClickWindowButton, - ClickWindow, - CloseWindow, - SlotStateChange, - CookieResponse, - PluginMessage, - DebugSampleSubscription, - EditBook, - QueryEntityNbt, - InteractEntity, - GenerateStructure, - KeepAlive, - LockDifficulty, - PlayerPosition, - PlayerPositionAndRotation, - PlayerRotation, - PlayerFlying, - - VehicleMove, - SteerBoat, - PickItem, - DebugPing, - CraftRecipeRequest, - PlayerAbilities, - PlayerDigging, - EntityAction, - PlayerInput, - Pong, - SetRecipeBookState, - SetDisplayedRecipe, - NameItem, - ResourcePackStatus, - AdvancementTab, - SelectTrade, - SetBeaconEffect, - HeldItemChange, - - UpdateCommandBlock, - UpdateCommandBlockMinecart, - CreativeInventoryAction, - UpdateJigsawBlock, - UpdateStructureBlock, - UpdateSign, - - Animation, - Spectate, - PlayerBlockPlacement, - UseItem, -} diff --git a/pumpkin-protocol/src/server/play/s_chat_command.rs b/pumpkin-protocol/src/server/play/s_chat_command.rs index e12bcdd89..c5c572cfb 100644 --- a/pumpkin-protocol/src/server/play/s_chat_command.rs +++ b/pumpkin-protocol/src/server/play/s_chat_command.rs @@ -1,4 +1,7 @@ +use pumpkin_macros::server_packet; + #[derive(serde::Deserialize)] +#[server_packet("play:chat_command")] pub struct SChatCommand { pub command: String, } diff --git a/pumpkin-protocol/src/server/play/s_chat_message.rs b/pumpkin-protocol/src/server/play/s_chat_message.rs index 58d3179b4..a8e61d746 100644 --- a/pumpkin-protocol/src/server/play/s_chat_message.rs +++ b/pumpkin-protocol/src/server/play/s_chat_message.rs @@ -1,4 +1,5 @@ use bytes::Bytes; +use pumpkin_macros::server_packet; use crate::{ bytebuf::{ByteBuffer, DeserializerError}, @@ -6,6 +7,7 @@ use crate::{ }; // derive(Deserialize)] +#[server_packet("play:chat")] pub struct SChatMessage { pub message: String, pub timestamp: i64, diff --git a/pumpkin-protocol/src/server/play/s_click_container.rs b/pumpkin-protocol/src/server/play/s_click_container.rs index f82aca1d3..8de3f8a79 100644 --- a/pumpkin-protocol/src/server/play/s_click_container.rs +++ b/pumpkin-protocol/src/server/play/s_click_container.rs @@ -1,9 +1,11 @@ use crate::slot::Slot; use crate::VarInt; +use pumpkin_macros::server_packet; use serde::de::SeqAccess; use serde::{de, Deserialize}; #[derive(Debug)] +#[server_packet("play:container_click")] pub struct SClickContainer { pub window_id: VarInt, pub state_id: VarInt, diff --git a/pumpkin-protocol/src/server/play/s_client_information.rs b/pumpkin-protocol/src/server/play/s_client_information.rs index b93760c2b..b16be1380 100644 --- a/pumpkin-protocol/src/server/play/s_client_information.rs +++ b/pumpkin-protocol/src/server/play/s_client_information.rs @@ -1,6 +1,9 @@ +use pumpkin_macros::server_packet; + use crate::VarInt; #[derive(serde::Deserialize)] +#[server_packet("play:client_information")] pub struct SClientInformationPlay { pub locale: String, // 16 pub view_distance: i8, diff --git a/pumpkin-protocol/src/server/play/s_close_container.rs b/pumpkin-protocol/src/server/play/s_close_container.rs index 1c6140195..d6f97dadc 100644 --- a/pumpkin-protocol/src/server/play/s_close_container.rs +++ b/pumpkin-protocol/src/server/play/s_close_container.rs @@ -1,8 +1,10 @@ +use pumpkin_macros::server_packet; use serde::Deserialize; use crate::VarInt; #[derive(Deserialize)] +#[server_packet("play:container_close")] pub struct SCloseContainer { pub window_id: VarInt, } diff --git a/pumpkin-protocol/src/server/play/s_confirm_teleport.rs b/pumpkin-protocol/src/server/play/s_confirm_teleport.rs index 2ee49d02a..b6f724e26 100644 --- a/pumpkin-protocol/src/server/play/s_confirm_teleport.rs +++ b/pumpkin-protocol/src/server/play/s_confirm_teleport.rs @@ -1,6 +1,9 @@ +use pumpkin_macros::server_packet; + use crate::VarInt; #[derive(serde::Deserialize)] +#[server_packet("play:accept_teleportation")] pub struct SConfirmTeleport { pub teleport_id: VarInt, } diff --git a/pumpkin-protocol/src/server/play/s_interact.rs b/pumpkin-protocol/src/server/play/s_interact.rs index 4350812a3..9a1a5d7b5 100644 --- a/pumpkin-protocol/src/server/play/s_interact.rs +++ b/pumpkin-protocol/src/server/play/s_interact.rs @@ -1,8 +1,10 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; +use pumpkin_macros::server_packet; use crate::{bytebuf::DeserializerError, ServerPacket, VarInt}; +#[server_packet("play:interact")] pub struct SInteract { pub entity_id: VarInt, pub typ: VarInt, diff --git a/pumpkin-protocol/src/server/play/s_keep_alive.rs b/pumpkin-protocol/src/server/play/s_keep_alive.rs index d49b7ecb5..714aaa134 100644 --- a/pumpkin-protocol/src/server/play/s_keep_alive.rs +++ b/pumpkin-protocol/src/server/play/s_keep_alive.rs @@ -1,6 +1,8 @@ +use pumpkin_macros::server_packet; use serde::Deserialize; #[derive(Deserialize)] +#[server_packet("play:keep_alive")] pub struct SKeepAlive { pub keep_alive_id: i64, } diff --git a/pumpkin-protocol/src/server/play/s_ping_request.rs b/pumpkin-protocol/src/server/play/s_ping_request.rs index 2b52299ea..77655ed3e 100644 --- a/pumpkin-protocol/src/server/play/s_ping_request.rs +++ b/pumpkin-protocol/src/server/play/s_ping_request.rs @@ -1,6 +1,8 @@ +use pumpkin_macros::server_packet; use serde::Deserialize; #[derive(Deserialize)] +#[server_packet("play:ping_request")] pub struct SPlayPingRequest { pub payload: i64, } diff --git a/pumpkin-protocol/src/server/play/s_player_abilities.rs b/pumpkin-protocol/src/server/play/s_player_abilities.rs index 0de8efadd..0bd5c06ed 100644 --- a/pumpkin-protocol/src/server/play/s_player_abilities.rs +++ b/pumpkin-protocol/src/server/play/s_player_abilities.rs @@ -1,8 +1,10 @@ +use pumpkin_macros::server_packet; use serde::Deserialize; //The vanilla client sends this packet when the player starts/stops flying. Bitmask 0x02 is set when the player is flying. #[derive(Deserialize)] +#[server_packet("play:player_abilities")] pub struct SPlayerAbilities { pub flags: i8, } diff --git a/pumpkin-protocol/src/server/play/s_player_action.rs b/pumpkin-protocol/src/server/play/s_player_action.rs index f53ca006d..b8d774dc2 100644 --- a/pumpkin-protocol/src/server/play/s_player_action.rs +++ b/pumpkin-protocol/src/server/play/s_player_action.rs @@ -1,9 +1,11 @@ use num_derive::FromPrimitive; use pumpkin_core::math::position::WorldPosition; +use pumpkin_macros::server_packet; use crate::VarInt; #[derive(serde::Deserialize)] +#[server_packet("play:player_action")] pub struct SPlayerAction { pub status: VarInt, pub location: WorldPosition, diff --git a/pumpkin-protocol/src/server/play/s_player_command.rs b/pumpkin-protocol/src/server/play/s_player_command.rs index e37011200..aeafcf153 100644 --- a/pumpkin-protocol/src/server/play/s_player_command.rs +++ b/pumpkin-protocol/src/server/play/s_player_command.rs @@ -1,7 +1,9 @@ use num_derive::FromPrimitive; +use pumpkin_macros::server_packet; use crate::{bytebuf::DeserializerError, ServerPacket, VarInt}; +#[server_packet("play:player_command")] pub struct SPlayerCommand { pub entity_id: VarInt, pub action: VarInt, diff --git a/pumpkin-protocol/src/server/play/s_player_ground.rs b/pumpkin-protocol/src/server/play/s_player_ground.rs index 0c167d829..dca0d17ee 100644 --- a/pumpkin-protocol/src/server/play/s_player_ground.rs +++ b/pumpkin-protocol/src/server/play/s_player_ground.rs @@ -1,6 +1,8 @@ +use pumpkin_macros::server_packet; use serde::Deserialize; #[derive(Deserialize)] +#[server_packet("play:move_player_status_only")] pub struct SSetPlayerGround { pub on_ground: bool, } diff --git a/pumpkin-protocol/src/server/play/s_player_position.rs b/pumpkin-protocol/src/server/play/s_player_position.rs index ae17d46e1..729b406be 100644 --- a/pumpkin-protocol/src/server/play/s_player_position.rs +++ b/pumpkin-protocol/src/server/play/s_player_position.rs @@ -1,4 +1,7 @@ +use pumpkin_macros::server_packet; + #[derive(serde::Deserialize)] +#[server_packet("play:move_player_pos")] pub struct SPlayerPosition { pub x: f64, pub feet_y: f64, diff --git a/pumpkin-protocol/src/server/play/s_player_position_rotation.rs b/pumpkin-protocol/src/server/play/s_player_position_rotation.rs index e83c57bc9..a97a68ed5 100644 --- a/pumpkin-protocol/src/server/play/s_player_position_rotation.rs +++ b/pumpkin-protocol/src/server/play/s_player_position_rotation.rs @@ -1,4 +1,7 @@ +use pumpkin_macros::server_packet; + #[derive(serde::Deserialize)] +#[server_packet("play:move_player_pos_rot")] pub struct SPlayerPositionRotation { pub x: f64, pub feet_y: f64, diff --git a/pumpkin-protocol/src/server/play/s_player_rotation.rs b/pumpkin-protocol/src/server/play/s_player_rotation.rs index 8a8ec4aab..ac2acafa8 100644 --- a/pumpkin-protocol/src/server/play/s_player_rotation.rs +++ b/pumpkin-protocol/src/server/play/s_player_rotation.rs @@ -1,4 +1,7 @@ +use pumpkin_macros::server_packet; + #[derive(serde::Deserialize)] +#[server_packet("play:move_player_rot")] pub struct SPlayerRotation { pub yaw: f32, pub pitch: f32, diff --git a/pumpkin-protocol/src/server/play/s_set_creative_slot.rs b/pumpkin-protocol/src/server/play/s_set_creative_slot.rs index f35318bb3..0dff80b44 100644 --- a/pumpkin-protocol/src/server/play/s_set_creative_slot.rs +++ b/pumpkin-protocol/src/server/play/s_set_creative_slot.rs @@ -1,6 +1,9 @@ +use pumpkin_macros::server_packet; + use crate::slot::Slot; #[derive(serde::Deserialize, Debug)] +#[server_packet("play:set_creative_mode_slot")] pub struct SSetCreativeSlot { pub slot: i16, pub clicked_item: Slot, diff --git a/pumpkin-protocol/src/server/play/s_set_held_item.rs b/pumpkin-protocol/src/server/play/s_set_held_item.rs index 2bf752878..05117dd29 100644 --- a/pumpkin-protocol/src/server/play/s_set_held_item.rs +++ b/pumpkin-protocol/src/server/play/s_set_held_item.rs @@ -1,6 +1,8 @@ +use pumpkin_macros::server_packet; use serde::Deserialize; #[derive(Deserialize)] +#[server_packet("play:pick_item")] pub struct SSetHeldItem { pub slot: i16, } diff --git a/pumpkin-protocol/src/server/play/s_swing_arm.rs b/pumpkin-protocol/src/server/play/s_swing_arm.rs index 65d69bd34..0f7d0f16e 100644 --- a/pumpkin-protocol/src/server/play/s_swing_arm.rs +++ b/pumpkin-protocol/src/server/play/s_swing_arm.rs @@ -1,6 +1,9 @@ +use pumpkin_macros::server_packet; + use crate::VarInt; #[derive(serde::Deserialize)] +#[server_packet("play:swing")] pub struct SSwingArm { pub hand: VarInt, } diff --git a/pumpkin-protocol/src/server/play/s_use_item.rs b/pumpkin-protocol/src/server/play/s_use_item.rs index 933e66b78..7035fd0a9 100644 --- a/pumpkin-protocol/src/server/play/s_use_item.rs +++ b/pumpkin-protocol/src/server/play/s_use_item.rs @@ -1,8 +1,10 @@ +use pumpkin_macros::server_packet; use serde::Deserialize; use crate::VarInt; #[derive(Deserialize)] +#[server_packet("play:use_item")] pub struct SUseItem { // 0 for main hand, 1 for off hand pub hand: VarInt, diff --git a/pumpkin-protocol/src/server/play/s_use_item_on.rs b/pumpkin-protocol/src/server/play/s_use_item_on.rs index a4338ad33..3e16d282c 100644 --- a/pumpkin-protocol/src/server/play/s_use_item_on.rs +++ b/pumpkin-protocol/src/server/play/s_use_item_on.rs @@ -1,9 +1,11 @@ use pumpkin_core::math::position::WorldPosition; +use pumpkin_macros::server_packet; use serde::Deserialize; use crate::VarInt; #[derive(Deserialize)] +#[server_packet("play:use_item_on")] pub struct SUseItemOn { pub hand: VarInt, pub location: WorldPosition, diff --git a/pumpkin-protocol/src/server/status/mod.rs b/pumpkin-protocol/src/server/status/mod.rs index 008bc5e5d..fb6b04ab5 100644 --- a/pumpkin-protocol/src/server/status/mod.rs +++ b/pumpkin-protocol/src/server/status/mod.rs @@ -1,14 +1,5 @@ mod s_ping_request; mod s_status_request; -use num_derive::FromPrimitive; pub use s_ping_request::*; pub use s_status_request::*; - -/// DO NOT CHANGE ORDER -/// This Enum has the exact order like vanilla, Vanilla parses their Packet IDs from the enum order. Its also way easier to port. -#[derive(FromPrimitive)] -pub enum ServerboundStatusPackets { - StatusRequest, - PingRequest, -} diff --git a/pumpkin-protocol/src/server/status/s_ping_request.rs b/pumpkin-protocol/src/server/status/s_ping_request.rs index 33054d22f..b8fd75c16 100644 --- a/pumpkin-protocol/src/server/status/s_ping_request.rs +++ b/pumpkin-protocol/src/server/status/s_ping_request.rs @@ -1,4 +1,7 @@ +use pumpkin_macros::server_packet; + #[derive(serde::Deserialize)] +#[server_packet("status:ping_request")] pub struct SStatusPingRequest { pub payload: i64, } diff --git a/pumpkin-protocol/src/server/status/s_status_request.rs b/pumpkin-protocol/src/server/status/s_status_request.rs index ecd3ba85d..9d96fcc66 100644 --- a/pumpkin-protocol/src/server/status/s_status_request.rs +++ b/pumpkin-protocol/src/server/status/s_status_request.rs @@ -1,4 +1,7 @@ +use pumpkin_macros::server_packet; + #[derive(serde::Deserialize)] +#[server_packet("status:status_request")] pub struct SStatusRequest { // empty } diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 8e4065c5b..12f018f77 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -14,25 +14,18 @@ use crate::{ use authentication::GameProfile; use crossbeam::atomic::AtomicCell; -use num_traits::FromPrimitive; use pumpkin_config::compression::CompressionInfo; use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ - bytebuf::DeserializerError, + bytebuf::{packet_id::Packet, DeserializerError}, client::{config::CConfigDisconnect, login::CLoginDisconnect, play::CPlayDisconnect}, packet_decoder::PacketDecoder, packet_encoder::{PacketEncodeError, PacketEncoder}, server::{ - config::{ - SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage, - ServerboundConfigPackets, - }, + config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage}, handshake::SHandShake, - login::{ - SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart, - ServerboundLoginPackets, - }, - status::{SStatusPingRequest, SStatusRequest, ServerboundStatusPackets}, + login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, + status::{SStatusPingRequest, SStatusRequest}, }, ClientPacket, ConnectionState, RawPacket, ServerPacket, }; @@ -397,24 +390,24 @@ impl Client { ) -> Result<(), DeserializerError> { log::debug!("Handling status group"); let bytebuf = &mut packet.bytebuf; - if let Some(packet) = ServerboundStatusPackets::from_i32(packet.id.0) { - match packet { - ServerboundStatusPackets::StatusRequest => { - self.handle_status_request(server, SStatusRequest::read(bytebuf)?) - .await; - } - ServerboundStatusPackets::PingRequest => { - self.handle_ping_request(SStatusPingRequest::read(bytebuf)?) - .await; - } - }; - } else { - log::error!( - "Failed to handle client packet id {:#04x} in Status State", - packet.id.0 - ); - return Err(DeserializerError::UnknownPacket); + match packet.id.0 { + SStatusRequest::PACKET_ID => { + self.handle_status_request(server, SStatusRequest::read(bytebuf)?) + .await; + } + SStatusPingRequest::PACKET_ID => { + self.handle_ping_request(SStatusPingRequest::read(bytebuf)?) + .await; + } + _ => { + log::error!( + "Failed to handle client packet id {:#04x} in Status State", + packet.id.0 + ); + return Err(DeserializerError::UnknownPacket); + } }; + Ok(()) } @@ -425,32 +418,30 @@ impl Client { ) -> Result<(), DeserializerError> { log::debug!("Handling login group for id"); let bytebuf = &mut packet.bytebuf; - if let Some(packet) = ServerboundLoginPackets::from_i32(packet.id.0) { - match packet { - ServerboundLoginPackets::LoginStart => { - self.handle_login_start(server, SLoginStart::read(bytebuf)?) - .await; - } - ServerboundLoginPackets::EncryptionResponse => { - self.handle_encryption_response(server, SEncryptionResponse::read(bytebuf)?) - .await; - } - ServerboundLoginPackets::PluginResponse => { - self.handle_plugin_response(SLoginPluginResponse::read(bytebuf)?) - .await; - } - ServerboundLoginPackets::LoginAcknowledged => { - self.handle_login_acknowledged(server, SLoginAcknowledged::read(bytebuf)?) - .await; - } - ServerboundLoginPackets::CookieResponse => {} - }; - } else { - log::error!( - "Failed to handle client packet id {:#04x} in Login State", - packet.id.0 - ); - return Ok(()); + match packet.id.0 { + SLoginStart::PACKET_ID => { + self.handle_login_start(server, SLoginStart::read(bytebuf)?) + .await; + } + SEncryptionResponse::PACKET_ID => { + self.handle_encryption_response(server, SEncryptionResponse::read(bytebuf)?) + .await; + } + SLoginPluginResponse::PACKET_ID => { + self.handle_plugin_response(SLoginPluginResponse::read(bytebuf)?) + .await; + } + SLoginAcknowledged::PACKET_ID => { + self.handle_login_acknowledged(server, SLoginAcknowledged::read(bytebuf)?) + .await; + } + _ => { + log::error!( + "Failed to handle client packet id {:#04x} in Login State", + packet.id.0 + ); + return Ok(()); + } }; Ok(()) } @@ -462,35 +453,29 @@ impl Client { ) -> Result<(), DeserializerError> { log::debug!("Handling config group"); let bytebuf = &mut packet.bytebuf; - if let Some(packet) = ServerboundConfigPackets::from_i32(packet.id.0) { - #[expect(clippy::match_same_arms)] - match packet { - ServerboundConfigPackets::ClientInformation => { - self.handle_client_information_config(SClientInformationConfig::read(bytebuf)?) - .await; - } - ServerboundConfigPackets::CookieResponse => {} - ServerboundConfigPackets::PluginMessage => { - self.handle_plugin_message(SPluginMessage::read(bytebuf)?) - .await; - } - ServerboundConfigPackets::AcknowledgedFinish => { - self.handle_config_acknowledged(&SAcknowledgeFinishConfig::read(bytebuf)?); - } - ServerboundConfigPackets::KeepAlive => {} - ServerboundConfigPackets::Pong => {} - ServerboundConfigPackets::ResourcePackResponse => {} - ServerboundConfigPackets::KnownPacks => { - self.handle_known_packs(server, SKnownPacks::read(bytebuf)?) - .await; - } - }; - } else { - log::error!( - "Failed to handle client packet id {:#04x} in Config State", - packet.id.0 - ); - return Err(DeserializerError::UnknownPacket); + match packet.id.0 { + SClientInformationConfig::PACKET_ID => { + self.handle_client_information_config(SClientInformationConfig::read(bytebuf)?) + .await; + } + SPluginMessage::PACKET_ID => { + self.handle_plugin_message(SPluginMessage::read(bytebuf)?) + .await; + } + SAcknowledgeFinishConfig::PACKET_ID => { + self.handle_config_acknowledged(&SAcknowledgeFinishConfig::read(bytebuf)?); + } + SKnownPacks::PACKET_ID => { + self.handle_known_packs(server, SKnownPacks::read(bytebuf)?) + .await; + } + _ => { + log::error!( + "Failed to handle client packet id {:#04x} in Config State", + packet.id.0 + ); + return Err(DeserializerError::UnknownPacket); + } }; Ok(()) } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 097b64bf9..a3d91428f 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -10,7 +10,7 @@ use std::{ use crossbeam::atomic::AtomicCell; use itertools::Itertools; use num_derive::FromPrimitive; -use num_traits::{FromPrimitive, ToPrimitive}; +use num_traits::ToPrimitive; use pumpkin_config::ADVANCED_CONFIG; use pumpkin_core::{ math::{boundingbox::BoundingBox, position::WorldPosition, vector2::Vector2, vector3::Vector3}, @@ -21,7 +21,7 @@ use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_inventory::player::PlayerInventory; use pumpkin_macros::sound; use pumpkin_protocol::{ - bytebuf::DeserializerError, + bytebuf::packet_id::Packet, client::play::{ CGameEvent, CHurtAnimation, CKeepAlive, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetHealth, CSyncPlayerPosition, CSystemChatMessage, GameEvent, @@ -31,7 +31,7 @@ use pumpkin_protocol::{ SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, - SUseItemOn, ServerboundPlayPackets, + SUseItemOn, }, RawPacket, ServerPacket, SoundCategory, VarInt, }; @@ -653,124 +653,80 @@ impl Player { } } - #[expect(clippy::too_many_lines)] pub async fn handle_play_packet( self: &Arc, server: &Arc, packet: &mut RawPacket, ) -> Result<(), Box> { let bytebuf = &mut packet.bytebuf; - if let Some(packet) = ServerboundPlayPackets::from_i32(packet.id.0) { - #[expect(clippy::match_same_arms)] - match packet { - ServerboundPlayPackets::TeleportConfirm => { - self.handle_confirm_teleport(SConfirmTeleport::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::QueryBlockNbt => {} - ServerboundPlayPackets::SelectBundleItem => {} - ServerboundPlayPackets::SetDifficulty => {} - ServerboundPlayPackets::ChatAck => {} - ServerboundPlayPackets::ChatCommandUnsigned => { - self.handle_chat_command(server, SChatCommand::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::ChatCommand => {} - ServerboundPlayPackets::ChatMessage => { - self.handle_chat_message(SChatMessage::read(bytebuf)?).await; - } - ServerboundPlayPackets::ChatSessionUpdate => {} - ServerboundPlayPackets::ChunkBatchAck => {} - ServerboundPlayPackets::ClientStatus => {} - ServerboundPlayPackets::ClientTickEnd => {} - ServerboundPlayPackets::ClientSettings => { - self.handle_client_information(SClientInformationPlay::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::TabComplete => {} - ServerboundPlayPackets::ConfigurationAck => {} - ServerboundPlayPackets::ClickWindowButton => {} - ServerboundPlayPackets::ClickWindow => {} - ServerboundPlayPackets::CloseWindow => {} - ServerboundPlayPackets::SlotStateChange => {} - ServerboundPlayPackets::CookieResponse => {} - ServerboundPlayPackets::PluginMessage => {} - ServerboundPlayPackets::DebugSampleSubscription => {} - ServerboundPlayPackets::EditBook => {} - ServerboundPlayPackets::QueryEntityNbt => {} - ServerboundPlayPackets::InteractEntity => { - self.handle_interact(server, SInteract::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::GenerateStructure => {} - ServerboundPlayPackets::KeepAlive => { - self.handle_keep_alive(SKeepAlive::read(bytebuf)?).await; - } - ServerboundPlayPackets::LockDifficulty => {} - ServerboundPlayPackets::PlayerPosition => { - self.handle_position(SPlayerPosition::read(bytebuf)?).await; - } - ServerboundPlayPackets::PlayerPositionAndRotation => { - self.handle_position_rotation(SPlayerPositionRotation::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::PlayerRotation => { - self.handle_rotation(SPlayerRotation::read(bytebuf)?).await; - } - ServerboundPlayPackets::PlayerFlying => { - self.handle_player_ground(&SSetPlayerGround::read(bytebuf)?); - } - ServerboundPlayPackets::VehicleMove => {} - ServerboundPlayPackets::SteerBoat => {} - ServerboundPlayPackets::PickItem => {} - ServerboundPlayPackets::DebugPing => {} - ServerboundPlayPackets::CraftRecipeRequest => {} - ServerboundPlayPackets::PlayerAbilities => { - self.handle_player_abilities(SPlayerAbilities::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::PlayerDigging => { - self.handle_player_action(SPlayerAction::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::EntityAction => { - self.handle_player_command(SPlayerCommand::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::PlayerInput => {} - ServerboundPlayPackets::Pong => {} - ServerboundPlayPackets::SetRecipeBookState => {} - ServerboundPlayPackets::SetDisplayedRecipe => {} - ServerboundPlayPackets::NameItem => {} - ServerboundPlayPackets::ResourcePackStatus => {} - ServerboundPlayPackets::AdvancementTab => {} - ServerboundPlayPackets::SelectTrade => {} - ServerboundPlayPackets::SetBeaconEffect => {} - ServerboundPlayPackets::HeldItemChange => { - self.handle_set_held_item(SSetHeldItem::read(bytebuf)?) - .await; - } - ServerboundPlayPackets::UpdateCommandBlock => {} - ServerboundPlayPackets::UpdateCommandBlockMinecart => {} - ServerboundPlayPackets::CreativeInventoryAction => { - self.handle_set_creative_slot(SSetCreativeSlot::read(bytebuf)?) - .await?; - } - ServerboundPlayPackets::UpdateJigsawBlock => {} - ServerboundPlayPackets::UpdateStructureBlock => {} - ServerboundPlayPackets::UpdateSign => {} - ServerboundPlayPackets::Animation => { - self.handle_swing_arm(SSwingArm::read(bytebuf)?).await; - } - ServerboundPlayPackets::Spectate => {} - ServerboundPlayPackets::PlayerBlockPlacement => { - self.handle_use_item_on(SUseItemOn::read(bytebuf)?).await; - } - ServerboundPlayPackets::UseItem => self.handle_use_item(&SUseItem::read(bytebuf)?), - }; - } else { - log::error!("Failed to handle player packet id {:#04x}", packet.id.0); - return Err(Box::new(DeserializerError::UnknownPacket)); + match packet.id.0 { + SConfirmTeleport::PACKET_ID => { + self.handle_confirm_teleport(SConfirmTeleport::read(bytebuf)?) + .await; + } + SChatCommand::PACKET_ID => { + self.handle_chat_command(server, SChatCommand::read(bytebuf)?) + .await; + } + SChatMessage::PACKET_ID => { + self.handle_chat_message(SChatMessage::read(bytebuf)?).await; + } + SClientInformationPlay::PACKET_ID => { + self.handle_client_information(SClientInformationPlay::read(bytebuf)?) + .await; + } + SInteract::PACKET_ID => { + self.handle_interact(server, SInteract::read(bytebuf)?) + .await; + } + SKeepAlive::PACKET_ID => { + self.handle_keep_alive(SKeepAlive::read(bytebuf)?).await; + } + SPlayerPosition::PACKET_ID => { + self.handle_position(SPlayerPosition::read(bytebuf)?).await; + } + SPlayerPositionRotation::PACKET_ID => { + self.handle_position_rotation(SPlayerPositionRotation::read(bytebuf)?) + .await; + } + SPlayerRotation::PACKET_ID => { + self.handle_rotation(SPlayerRotation::read(bytebuf)?).await; + } + SSetPlayerGround::PACKET_ID => { + self.handle_player_ground(&SSetPlayerGround::read(bytebuf)?); + } + SPlayerAbilities::PACKET_ID => { + self.handle_player_abilities(SPlayerAbilities::read(bytebuf)?) + .await; + } + SPlayerAction::PACKET_ID => { + self.handle_player_action(SPlayerAction::read(bytebuf)?) + .await; + } + SPlayerCommand::PACKET_ID => { + self.handle_player_command(SPlayerCommand::read(bytebuf)?) + .await; + } + SSetHeldItem::PACKET_ID => { + self.handle_set_held_item(SSetHeldItem::read(bytebuf)?) + .await; + } + SSetCreativeSlot::PACKET_ID => { + self.handle_set_creative_slot(SSetCreativeSlot::read(bytebuf)?) + .await?; + } + SSwingArm::PACKET_ID => { + self.handle_swing_arm(SSwingArm::read(bytebuf)?).await; + } + SUseItemOn::PACKET_ID => { + self.handle_use_item_on(SUseItemOn::read(bytebuf)?).await; + } + SUseItem::PACKET_ID => self.handle_use_item(&SUseItem::read(bytebuf)?), + _ => { + log::warn!("Failed to handle player packet id {:#04x}", packet.id.0); + // TODO: We give an error if all play packets are implemented + // return Err(Box::new(DeserializerError::UnknownPacket)); + } }; Ok(()) }