From e0117ab5596ac92a19638d820f79af07fe7f811e Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sun, 19 Jul 2026 18:07:37 +0200 Subject: [PATCH] perf: ditch serde because its slower i rather trade performance and compile time over convenience --- Cargo.lock | 101 ++- Cargo.toml | 8 +- pumpkin-protocol/Cargo.toml | 2 +- pumpkin-protocol/src/codec/bit_set.rs | 10 - pumpkin-protocol/src/codec/data_component.rs | 851 ++++++++---------- .../src/codec/item_stack_seralizer.rs | 436 ++++----- pumpkin-protocol/src/codec/little_endian.rs | 58 +- pumpkin-protocol/src/codec/lp_vector_3d.rs | 43 - pumpkin-protocol/src/codec/optional_int.rs | 15 +- pumpkin-protocol/src/codec/recipe.rs | 13 +- pumpkin-protocol/src/codec/var_int.rs | 52 -- pumpkin-protocol/src/codec/var_long.rs | 52 -- pumpkin-protocol/src/codec/var_uint.rs | 52 -- pumpkin-protocol/src/codec/var_ulong.rs | 52 -- .../java/client/config/add_resource_pack.rs | 27 +- .../src/java/client/config/clear_dialog.rs | 15 +- .../java/client/config/config_disconnect.rs | 17 +- .../src/java/client/config/cookie_request.rs | 16 +- .../src/java/client/config/finish_config.rs | 15 +- .../src/java/client/config/known_packs.rs | 19 +- .../src/java/client/config/server_links.rs | 20 +- .../src/java/client/config/show_dialog.rs | 17 +- .../src/java/client/config/store_cookie.rs | 18 +- .../src/java/client/config/transfer.rs | 18 +- pumpkin-protocol/src/java/client/dialog.rs | 57 +- .../src/java/client/login/cookie_request.rs | 17 +- .../java/client/login/encryption_request.rs | 22 +- .../src/java/client/login/login_disconnect.rs | 17 +- .../src/java/client/login/login_success.rs | 6 +- .../src/java/client/login/set_compression.rs | 16 +- .../src/java/client/play/acknowledge_block.rs | 20 +- .../src/java/client/play/actionbar.rs | 17 +- .../src/java/client/play/award_stats.rs | 28 +- .../java/client/play/block_destroy_stage.rs | 21 +- .../src/java/client/play/block_event.rs | 22 +- .../src/java/client/play/center_chunk.rs | 20 +- .../src/java/client/play/change_difficulty.rs | 17 +- .../src/java/client/play/chunk_batch_end.rs | 19 +- .../src/java/client/play/chunk_batch_start.rs | 15 +- .../src/java/client/play/clear_dialog.rs | 15 +- .../src/java/client/play/clear_title.rs | 17 +- .../src/java/client/play/close_container.rs | 19 +- .../src/java/client/play/combat_death.rs | 20 +- .../java/client/play/command_suggestions.rs | 38 +- .../src/java/client/play/cookie_request.rs | 16 +- .../src/java/client/play/damage_event.rs | 30 +- .../src/java/client/play/disconnect.rs | 16 +- .../client/play/disguised_chat_message.rs | 27 +- .../src/java/client/play/display_objective.rs | 20 +- .../src/java/client/play/entity_animation.rs | 20 +- .../src/java/client/play/entity_metadata.rs | 169 +++- .../java/client/play/entity_position_sync.rs | 28 +- .../java/client/play/entity_sound_effect.rs | 15 +- .../src/java/client/play/entity_status.rs | 18 +- .../src/java/client/play/explode.rs | 36 +- .../src/java/client/play/game_event.rs | 18 +- .../src/java/client/play/head_rot.rs | 17 +- .../src/java/client/play/hurt_animation.rs | 17 +- .../client/play/initialize_world_border.rs | 23 +- .../src/java/client/play/item_cooldown.rs | 18 +- .../src/java/client/play/keep_alive.rs | 17 +- .../src/java/client/play/merchant_offers.rs | 48 +- .../src/java/client/play/open_screen.rs | 20 +- .../src/java/client/play/open_sign_editor.rs | 18 +- .../src/java/client/play/ping_response.rs | 17 +- .../src/java/client/play/player_abilities.rs | 19 +- .../java/client/play/player_info_update.rs | 4 +- .../src/java/client/play/player_remove.rs | 30 +- .../src/java/client/play/remove_entities.rs | 19 +- .../src/java/client/play/remove_mob_effect.rs | 18 +- .../src/java/client/play/reset_score.rs | 18 +- .../client/play/select_advancements_tab.rs | 17 +- .../src/java/client/play/server_links.rs | 2 - .../src/java/client/play/set_border_center.rs | 18 +- .../java/client/play/set_border_lerp_size.rs | 18 +- .../src/java/client/play/set_border_size.rs | 17 +- .../client/play/set_border_warning_delay.rs | 16 +- .../play/set_border_warning_distance.rs | 16 +- .../src/java/client/play/set_camera.rs | 15 +- .../client/play/set_container_property.rs | 18 +- .../src/java/client/play/set_cursor_slot.rs | 4 +- .../src/java/client/play/set_experience.rs | 18 +- .../src/java/client/play/set_health.rs | 18 +- .../src/java/client/play/set_held_item.rs | 15 +- .../src/java/client/play/set_passengers.rs | 20 +- .../java/client/play/set_player_inventory.rs | 2 +- .../src/java/client/play/set_title.rs | 16 +- .../java/client/play/set_title_animation.rs | 18 +- .../src/java/client/play/show_dialog.rs | 16 +- .../src/java/client/play/sound_effect.rs | 17 +- .../src/java/client/play/store_cookie.rs | 21 +- .../src/java/client/play/subtitle.rs | 17 +- .../java/client/play/system_chat_message.rs | 18 +- .../src/java/client/play/tab_list.rs | 18 +- .../src/java/client/play/take_item.rs | 19 +- .../src/java/client/play/ticking_state.rs | 18 +- .../src/java/client/play/ticking_step.rs | 16 +- .../src/java/client/play/transfer.rs | 17 +- .../src/java/client/play/unload_chunk.rs | 18 +- .../java/client/play/update_advancement.rs | 151 ++-- .../src/java/client/play/update_attributes.rs | 32 +- .../src/java/client/play/update_entity_pos.rs | 20 +- .../java/client/play/update_entity_pos_rot.rs | 22 +- .../src/java/client/play/update_entity_rot.rs | 19 +- .../src/java/client/play/update_mob_effect.rs | 20 +- .../src/java/client/play/update_score.rs | 21 +- .../src/java/client/status/ping_response.rs | 17 +- .../src/java/client/status/status_response.rs | 17 +- pumpkin-protocol/src/java/packet_encoder.rs | 15 +- .../config/acknowledge_finish_config.rs | 12 +- .../java/server/config/client_information.rs | 24 +- .../java/server/config/custom_click_action.rs | 18 +- .../src/java/server/config/keep_alive.rs | 17 +- .../src/java/server/config/known_packs.rs | 17 +- .../server/config/resource_pack_response.rs | 19 +- .../src/java/server/login/login_response.rs | 12 +- .../src/java/server/login/login_start.rs | 2 - .../src/java/server/play/attack.rs | 19 +- .../java/server/play/bundle_item_selected.rs | 18 +- .../src/java/server/play/change_game_mode.rs | 17 +- .../src/java/server/play/chat_command.rs | 17 +- .../src/java/server/play/chunk_batch.rs | 16 +- .../src/java/server/play/click_container.rs | 103 +-- .../src/java/server/play/client_command.rs | 16 +- .../java/server/play/client_information.rs | 23 +- .../src/java/server/play/close_container.rs | 16 +- .../java/server/play/command_suggestion.rs | 18 +- .../src/java/server/play/confirm_teleport.rs | 16 +- .../server/play/container_button_click.rs | 19 +- .../java/server/play/custom_click_action.rs | 17 +- .../src/java/server/play/jigsaw_generate.rs | 21 +- .../src/java/server/play/keep_alive.rs | 17 +- .../src/java/server/play/move_vehicle.rs | 23 +- .../src/java/server/play/paddle_boat.rs | 18 +- .../src/java/server/play/pick_item.rs | 27 +- .../src/java/server/play/ping_request.rs | 17 +- .../src/java/server/play/place_recipe.rs | 18 +- .../src/java/server/play/player_abilities.rs | 17 +- .../src/java/server/play/player_action.rs | 21 +- .../src/java/server/play/player_ground.rs | 16 +- .../src/java/server/play/player_input.rs | 16 +- .../src/java/server/play/player_loaded.rs | 14 +- .../src/java/server/play/player_position.rs | 22 +- .../server/play/player_position_rotation.rs | 23 +- .../src/java/server/play/player_rotation.rs | 18 +- .../play/recipe_book_change_settings.rs | 18 +- .../server/play/recipe_book_seen_recipe.rs | 16 +- .../src/java/server/play/rename_item.rs | 17 +- .../src/java/server/play/seen_advancement.rs | 28 +- .../src/java/server/play/select_trade.rs | 17 +- .../src/java/server/play/set_beacon.rs | 17 +- .../src/java/server/play/set_command_block.rs | 19 +- .../src/java/server/play/set_creative_slot.rs | 2 +- .../src/java/server/play/set_held_item.rs | 16 +- .../src/java/server/play/set_jigsaw_block.rs | 23 +- .../src/java/server/play/set_test_block.rs | 32 +- .../src/java/server/play/swing_arm.rs | 16 +- .../java/server/play/teleport_to_entity.rs | 20 +- .../server/play/test_instance_block_action.rs | 87 +- .../src/java/server/play/use_item.rs | 22 +- .../src/java/server/play/use_item_on.rs | 26 +- .../src/java/server/status/ping_request.rs | 19 +- .../src/java/server/status/status_request.rs | 14 +- pumpkin-protocol/src/lib.rs | 247 ++--- pumpkin-protocol/src/packet.rs | 22 +- pumpkin-protocol/src/ser/deserializer.rs | 327 ------- pumpkin-protocol/src/ser/mod.rs | 403 ++------- pumpkin-protocol/src/ser/serializer.rs | 362 -------- pumpkin-world/src/chunk/format/mod.rs | 518 +++++++---- .../structure/template/processor.rs | 142 ++- pumpkin/src/entity/area_effect_cloud.rs | 32 +- pumpkin/src/entity/mod.rs | 5 +- pumpkin/src/entity/passive/villager/data.rs | 14 +- .../wasm/wasm_host/wit/v0_1/item_stack.rs | 20 +- .../loader/wasm/wasm_host/wit/v0_1/player.rs | 20 - 175 files changed, 3995 insertions(+), 3093 deletions(-) delete mode 100644 pumpkin-protocol/src/ser/deserializer.rs delete mode 100644 pumpkin-protocol/src/ser/serializer.rs diff --git a/Cargo.lock b/Cargo.lock index c6c7ef867..06bc7ee9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,7 +120,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -213,9 +213,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -485,7 +485,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1047,7 +1047,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1131,7 +1131,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1147,7 +1147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1300,7 +1300,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2159,7 +2159,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2186,7 +2186,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2360,7 +2360,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2403,7 +2403,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2432,7 +2432,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2520,7 +2520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.118", ] [[package]] @@ -2600,7 +2600,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2632,7 +2632,7 @@ checksum = "5a7ac85c0bb3fb351f10d531230aaa5e366b46d7c4e5328e5f02801d6dac1165" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2701,7 +2701,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn", + "syn 3.0.0", ] [[package]] @@ -2759,7 +2759,7 @@ dependencies = [ "proc-macro-error2", "pumpkin-data", "quote", - "syn", + "syn 3.0.0", ] [[package]] @@ -2835,7 +2835,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.11.0", - "syn", + "syn 3.0.0", "thiserror 2.0.18", "tokio", "ureq", @@ -3085,7 +3085,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3230,7 +3230,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3265,7 +3265,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3398,7 +3398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3455,6 +3455,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3469,7 +3480,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3507,7 +3518,7 @@ dependencies = [ "fastrand", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3545,7 +3556,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3556,7 +3567,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3622,9 +3633,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee" dependencies = [ "bytes", "libc", @@ -3645,7 +3656,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3835,7 +3846,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3998,9 +4009,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.4" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -4078,7 +4089,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -4262,7 +4273,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", "wit-parser 0.251.0", @@ -4370,7 +4381,7 @@ checksum = "e747f4a074699ba1b4e4d841fb263f9b7df5bd1555181c4752bf5990d21ba676" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4490,7 +4501,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4552,7 +4563,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4563,7 +4574,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4826,7 +4837,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.118", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4842,7 +4853,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4946,7 +4957,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", "synstructure", ] @@ -4967,7 +4978,7 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4987,7 +4998,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", "synstructure", ] @@ -5027,7 +5038,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f00a7c4e1..9dc2096ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,8 +98,8 @@ strip = false [workspace.dependencies] serde_json5 = "0.2.1" -tokio = { version = "1.52", default-features = false } -syn = { version = "2.0", default-features = false, features = ["printing"] } +tokio = { version = "1.53", default-features = false } +syn = { version = "3.0", default-features = false, features = ["full", "parsing", "printing", "proc-macro", "clone-impls", "derive"] } thiserror = { version = "2.0", default-features = false } @@ -110,7 +110,7 @@ futures = { version = "0.3", default-features = false, features = ["executor"] } rayon = { version = "1.12", default-features = false } crossbeam = { version = "0.8", default-features = false, features = ["std"] } -uuid = { version = "1.23", default-features = false, features = ["serde", "v3", "v4", "std"] } +uuid = { version = "1.24", default-features = false, features = ["serde", "v3", "v4", "std"] } serde = { version = "1.0", default-features = false, features = ["derive", "std"] } serde_repr = { version = "0.1", default-features = false } serde_json = { version = "1.0", default-features = false, features = ["std"] } @@ -120,7 +120,7 @@ proc-macro-error2 = { version = "2", default-features = false } aes = { version = "0.9", default-features = false } async-compression = { version = "0.4.42", default-features = false } base64 = { version = "0.22.1", default-features = false, features = ["std"] } -bitflags = { version = "2.13.0", default-features = false, features = ["std"] } +bitflags = { version = "2.13.1", default-features = false, features = ["std"] } cesu8 = { version = "1.1", default-features = false } cfb8 = { version = "0.9", default-features = false } ctr = { version = "0.10.1", default-features = false } diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index a16f7e6aa..60b05f628 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -17,9 +17,9 @@ pumpkin-data = { workspace = true, features = ["packet", "item_id_remap", "entit pumpkin-macros.workspace = true pumpkin-world.workspace = true pumpkin-util.workspace = true +serde = { workspace = true, features = ["derive"] } uuid.workspace = true -serde.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["net"] } bytes.workspace = true diff --git a/pumpkin-protocol/src/codec/bit_set.rs b/pumpkin-protocol/src/codec/bit_set.rs index 481cdfe9d..f2036c841 100644 --- a/pumpkin-protocol/src/codec/bit_set.rs +++ b/pumpkin-protocol/src/codec/bit_set.rs @@ -1,8 +1,6 @@ use std::io::Read; use std::io::Write; -use serde::{Serialize, Serializer}; - use crate::ReadingError; use crate::WritingError; use crate::ser::NetworkReadExt; @@ -34,11 +32,3 @@ impl BitSet { Ok(Self(array.into_boxed_slice())) } } - -impl Serialize for BitSet { - fn serialize(&self, _serializer: S) -> Result { - Err(serde::ser::Error::custom( - "BitSet serialization not implemented", - )) - } -} diff --git a/pumpkin-protocol/src/codec/data_component.rs b/pumpkin-protocol/src/codec/data_component.rs index 5248fde96..520399e14 100644 --- a/pumpkin-protocol/src/codec/data_component.rs +++ b/pumpkin-protocol/src/codec/data_component.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use crate::codec::var_int::VarInt; +use crate::ser::{NetworkReadExt, NetworkWriteExt, ReadingError, WritingError}; use pumpkin_data::Enchantment; use pumpkin_data::data_component::DataComponent; use pumpkin_data::data_component_impl::{ @@ -21,9 +22,6 @@ use pumpkin_data::effect::StatusEffect; use pumpkin_data::entity::EntityType; use pumpkin_data::sound::Sound; use pumpkin_nbt::{serializer::NbtWriteHelperJava, tag::NbtTag}; -use serde::de; -use serde::de::SeqAccess; -use serde::ser::{SerializeStruct, Serializer}; const MAX_STATUS_EFFECTS: usize = 128; @@ -52,106 +50,79 @@ pub fn proto_to_data_sound(id_or: &crate::IdOr) -> Option, T: IDSetContent>( - seq: &mut A, -) -> Result, A::Error> { - let id_type = seq - .next_element::()? - .ok_or(de::Error::custom("No type/len VarInt in IDSet"))? - .0; +fn deserialize_idset( + seq: &mut impl NetworkReadExt, +) -> Result, ReadingError> { + let id_type = seq.get_var_int()?.0; match id_type.cmp(&0) { std::cmp::Ordering::Equal => { - let tag = seq - .next_element::()? - .ok_or(de::Error::custom("No tag name in IDSet"))?; - Ok(IDSet::Tag(Cow::Owned(tag))) + let tag = seq.get_str()?; + Ok(IDSet::Tag(Cow::Owned(tag.into()))) } std::cmp::Ordering::Greater => { let len = id_type - 1; let mut content_vec = Vec::with_capacity(len as usize); for _ in 0..len { - let varint_id = seq - .next_element::()? - .ok_or(de::Error::custom("Missing registry id VarInt in IDSet"))? - .0; + let varint_id = seq.get_var_int()?.0; - let elmt = T::from_id(varint_id as u16) - .ok_or(de::Error::custom("Invalid registry id VarInt in IDSet"))?; + let elmt = T::from_id(varint_id as u16).ok_or(ReadingError::Message( + "Invalid registry id VarInt in IDSet".into(), + ))?; content_vec.push(elmt); } Ok(IDSet::IDs(Cow::Owned(content_vec))) } - std::cmp::Ordering::Less => { - Result::Err(de::Error::custom("Negative type/len VarInt in IDSet")) - } + std::cmp::Ordering::Less => Result::Err(ReadingError::Message( + "Negative type/len VarInt in IDSet".into(), + )), } } -fn serialize_idset( +fn serialize_idset( idset: &IDSet, - seq: &mut T, -) -> Result<(), T::Error> { + seq: &mut impl NetworkWriteExt, +) -> Result<(), WritingError> { match idset { IDSet::Tag(tag) => { - seq.serialize_field::("", &VarInt(0))?; - seq.serialize_field::("", tag) + seq.write_var_int(&VarInt(0))?; + seq.write_string(tag) } IDSet::IDs(elements) => { - seq.serialize_field::("", &VarInt(elements.len() as i32 + 1))?; + seq.write_var_int(&VarInt(elements.len() as i32 + 1))?; for elmt in elements.iter() { - seq.serialize_field::("", &VarInt(elmt.registry_id() as i32))?; + seq.write_var_int(&VarInt(elmt.registry_id() as i32))?; } Ok(()) } } } -fn deserialize_status_effects<'a, A: SeqAccess<'a>>( - seq: &mut A, -) -> Result, A::Error> { - let effects_len = seq - .next_element::()? - .ok_or(de::Error::custom("No effects_len VarInt!"))? - .0 as usize; +fn deserialize_status_effects( + seq: &mut impl NetworkReadExt, +) -> Result, ReadingError> { + let effects_len = seq.get_var_int()?.0 as usize; if effects_len > MAX_STATUS_EFFECTS { - return Err(de::Error::custom("Too many status effects")); + return Err(ReadingError::Message("Too many status effects".into())); } let mut custom_effects = Vec::with_capacity(effects_len); for _ in 0..effects_len { - let effect_registry_id = seq - .next_element::()? - .ok_or(de::Error::custom("No effect_id VarInt!"))? - .0; + let effect_registry_id = seq.get_var_int()?.0; let effect_name = StatusEffect::from_id(effect_registry_id as u16) - .ok_or(de::Error::custom("Invalid effect_id!"))? + .ok_or(ReadingError::Message("Invalid effect_id!".into()))? .minecraft_name; let effect_id = Cow::Borrowed(effect_name); // Effect parameters - let amplifier = seq - .next_element::()? - .ok_or(de::Error::custom("No amplifier VarInt!"))? - .0; - let duration = seq - .next_element::()? - .ok_or(de::Error::custom("No duration VarInt!"))? - .0; - let ambient = seq - .next_element::()? - .ok_or(de::Error::custom("No ambient bool!"))?; - let show_particles = seq - .next_element::()? - .ok_or(de::Error::custom("No show_particles bool!"))?; - let show_icon = seq - .next_element::()? - .ok_or(de::Error::custom("No show_icon bool!"))?; + let amplifier = seq.get_var_int()?.0; + let duration = seq.get_var_int()?.0; + let ambient = seq.get_bool()?; + let show_particles = seq.get_bool()?; + let show_icon = seq.get_bool()?; // Hidden effect (optional, recursive) - we skip it for now - let has_hidden = seq - .next_element::()? - .ok_or(de::Error::custom("No has_hidden bool!"))?; + let has_hidden = seq.get_bool()?; if has_hidden { // Skip hidden effect parameters recursively skip_effect_parameters(seq)?; @@ -170,43 +141,38 @@ fn deserialize_status_effects<'a, A: SeqAccess<'a>>( Ok(custom_effects) } -fn serialize_status_effects( +fn serialize_status_effects( effects: &Vec, - seq: &mut T, -) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt(effects.len() as i32))?; + seq: &mut impl NetworkWriteExt, +) -> Result<(), WritingError> { + seq.write_var_int(&VarInt(effects.len() as i32))?; for effect in effects { let effect_id = StatusEffect::from_minecraft_name(&effect.effect_id) .ok_or_else(|| { - serde::ser::Error::custom(format!("Invalid status effect: {}", effect.effect_id)) + WritingError::Message(format!("Invalid status effect: {}", effect.effect_id)) })? .registry_id(); - seq.serialize_field::("", &VarInt(effect_id as i32))?; + seq.write_var_int(&VarInt(effect_id as i32))?; // Effect parameters - seq.serialize_field::("", &VarInt::from(effect.amplifier))?; - seq.serialize_field::("", &VarInt::from(effect.duration))?; - seq.serialize_field::("", &effect.ambient)?; - seq.serialize_field::("", &effect.show_particles)?; - seq.serialize_field::("", &effect.show_icon)?; + seq.write_var_int(&VarInt::from(effect.amplifier))?; + seq.write_var_int(&VarInt::from(effect.duration))?; + seq.write_bool(effect.ambient)?; + seq.write_bool(effect.show_particles)?; + seq.write_bool(effect.show_icon)?; // No hidden effect for now - seq.serialize_field::("", &false)?; + seq.write_bool(false)?; } Ok(()) } -fn deserialize_consume_effect<'a, A: SeqAccess<'a>>( - seq: &mut A, -) -> Result { - let effect_type = seq - .next_element::()? - .ok_or(de::Error::custom("No type VarInt in ConsumeEffect"))? - .0; +fn deserialize_consume_effect( + seq: &mut impl NetworkReadExt, +) -> Result { + let effect_type = seq.get_var_int()?.0; match effect_type { 0 => { - let probability = seq - .next_element::()? - .ok_or(de::Error::custom("No probability float in ConsumeEffect"))?; + let probability = seq.get_f32()?; Ok(ConsumeEffect::ApplyEffects(( Cow::Owned(deserialize_status_effects(seq)?), probability, @@ -218,111 +184,106 @@ fn deserialize_consume_effect<'a, A: SeqAccess<'a>>( } 2 => Ok(ConsumeEffect::ClearAllEffects), 3 => { - let diameter = seq - .next_element::()? - .ok_or(de::Error::custom("No diameter float in ConsumeEffect"))?; + let diameter = seq.get_f32()?; Ok(ConsumeEffect::TeleportRandomly(diameter)) } 4 => { - let proto_sound_event = seq - .next_element::>()? - .ok_or(de::Error::custom( - "No sound IdOr in ConsumeEffect", - ))?; + // Need to read IdOr manually. This depends on how it is serialized. + // In vanilla, it's either an id (0) or a sound event (1) ... but wait, `crate::IdOr` doesn't have a `NetworkReadExt` method. + // Let's defer this and assume it implements `read` for now or wait, `IdOr` does implement `PacketRead` or something? + // Actually, we can just use `IdOr::read` if we impl it, but let's change it to: + let proto_sound_event = crate::IdOr::::read(seq, |r| { + let sound_name = r.get_str()?.into(); + let range = r.get_option(NetworkReadExt::get_f32)?; + Ok(crate::SoundEvent { sound_name, range }) + }) + .map_err(|e| { + ReadingError::Message(format!("No sound IdOr in ConsumeEffect: {e}")) + })?; Ok(ConsumeEffect::PlaySound( - proto_to_data_sound(&proto_sound_event) - .ok_or(de::Error::custom("Invalid sound in ConsumeEffect"))?, + proto_to_data_sound(&proto_sound_event).ok_or(ReadingError::Message( + "Invalid sound in ConsumeEffect".into(), + ))?, )) } - _ => Err(de::Error::custom("Invalid effect_type in ConsumeEffect")), + _ => Err(ReadingError::Message( + "Invalid effect_type in ConsumeEffect".into(), + )), } } -fn serialize_consume_effect( +fn serialize_consume_effect( consume_effect: &ConsumeEffect, - seq: &mut T, -) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt(consume_effect.registry_id() as i32))?; + seq: &mut impl NetworkWriteExt, +) -> Result<(), WritingError> { + seq.write_var_int(&VarInt(consume_effect.registry_id() as i32))?; match consume_effect { ConsumeEffect::ApplyEffects((effects, probability)) => { serialize_status_effects(&effects.to_vec(), seq)?; - seq.serialize_field::("", probability)?; + seq.write_f32(*probability)?; } ConsumeEffect::RemoveEffects(idset) => serialize_idset(idset, seq)?, ConsumeEffect::ClearAllEffects => (), - ConsumeEffect::TeleportRandomly(diameter) => seq.serialize_field::("", diameter)?, + ConsumeEffect::TeleportRandomly(diameter) => seq.write_f32(*diameter)?, ConsumeEffect::PlaySound(id_or) => { - seq.serialize_field::>("", &data_to_proto_sound(id_or))?; + crate::IdOr::::write(&data_to_proto_sound(id_or), seq, |w, e| { + w.write_string(&e.sound_name)?; + w.write_option(&e.range, |w2, r| w2.write_f32(*r)) + })?; } } Ok(()) } trait DataComponentCodec { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error>; - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result; + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError>; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result; } impl DataComponentCodec for MaxStackSizeImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt::from(self.size)) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_var_int(&VarInt::from(self.size)) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let size = u8::try_from( - seq.next_element::()? - .ok_or(de::Error::custom("No MaxStackSize VarInt!"))? - .0, - ) - .map_err(|_| de::Error::custom("No MaxStackSize VarInt!"))?; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let size = u8::try_from(seq.get_var_int()?.0) + .map_err(|_| ReadingError::Message("No MaxStackSize VarInt!".into()))?; Ok(Self { size }) } } impl DataComponentCodec for DamageImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt::from(self.damage)) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_var_int(&VarInt::from(self.damage)) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let damage = seq - .next_element::()? - .ok_or(de::Error::custom("No damage VarInt!"))? - .0; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let damage = seq.get_var_int()?.0; Ok(Self { damage }) } } impl DataComponentCodec for EnchantmentsImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt::from(self.enchantment.len() as i32))?; + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_var_int(&VarInt::from(self.enchantment.len() as i32))?; for (enc, level) in self.enchantment.iter() { - seq.serialize_field::("", &VarInt::from(enc.id))?; - seq.serialize_field::("", &VarInt::from(*level))?; + seq.write_var_int(&VarInt::from(enc.id))?; + seq.write_var_int(&VarInt::from(*level))?; } Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { const MAX_ENCHANTMENTS: usize = 256; - let len = seq - .next_element::()? - .ok_or(de::Error::custom("No EnchantmentsImpl len VarInt!"))? - .0 as usize; + let len = seq.get_var_int()?.0 as usize; if len > MAX_ENCHANTMENTS { - return Err(de::Error::custom("Too many enchantments")); + return Err(ReadingError::Message("Too many enchantments".into())); } let mut enc = Vec::with_capacity(len); for _ in 0..len { - let id = seq - .next_element::()? - .ok_or(de::Error::custom("No EnchantmentsImpl id VarInt!"))? - .0 as u8; - let level = seq - .next_element::()? - .ok_or(de::Error::custom("No EnchantmentsImpl level VarInt!"))? - .0; + let id = seq.get_var_int()?.0 as u8; + let level = seq.get_var_int()?.0; enc.push(( - Enchantment::from_id(id).ok_or(de::Error::custom( - "EnchantmentsImpl Enchantment VarInt Incorrect!", + Enchantment::from_id(id).ok_or(ReadingError::Message( + "EnchantmentsImpl Enchantment VarInt Incorrect!".into(), ))?, level, )); @@ -334,89 +295,76 @@ impl DataComponentCodec for EnchantmentsImpl { } impl DataComponentCodec for UnbreakableImpl { - fn serialize(&self, _seq: &mut T) -> Result<(), T::Error> { + fn serialize(&self, _seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(_seq: &mut A) -> Result { + fn deserialize(_seq: &mut impl NetworkReadExt) -> Result { Ok(Self) } } impl DataComponentCodec for ItemModelImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field("", &self.id) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_string(&self.id) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let id = seq - .next_element::()? - .ok_or(de::Error::custom("No ItemModelImpl id string!"))?; - Ok(Self { id: Cow::Owned(id) }) + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let id = seq.get_str()?; + Ok(Self { + id: Cow::Owned(id.into()), + }) } } impl DataComponentCodec for CustomNameImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field("", &NetworkTextNbtString(self.name.clone().get_text())) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + let mut bytes = Vec::new(); + NbtTag::String(self.name.clone().get_text().into_boxed_str()) + .serialize(&mut NbtWriteHelperJava::new(&mut bytes)) + .map_err(|e| WritingError::Message(e.to_string()))?; + seq.write_slice(&bytes)?; + Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let name = seq - .next_element::()? - .ok_or(de::Error::custom("No CustomNameImpl name string!"))?; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let name = seq.get_str()?; Ok(Self { - name: pumpkin_util::text::TextComponent::text(name), + name: pumpkin_util::text::TextComponent::text(String::from(name)), }) } } impl DataComponentCodec for CustomDataImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field("", &NetworkNbtTag(NbtTag::Compound(self.data.clone()))) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + let mut bytes = Vec::new(); + NbtTag::Compound(self.data.clone()) + .serialize(&mut NbtWriteHelperJava::new(&mut bytes)) + .map_err(|e| WritingError::Message(e.to_string()))?; + seq.write_slice(&bytes)?; + Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(_seq: &mut A) -> Result { - Err(de::Error::custom( - "CustomData raw component decoding is not supported; use the custom-data item-stack API", + fn deserialize(_seq: &mut impl NetworkReadExt) -> Result { + Err(ReadingError::Message( + "CustomData raw component decoding is not supported; use the custom-data item-stack API".into(), )) } } -struct NetworkTextNbtString(String); - -impl serde::Serialize for NetworkTextNbtString { - fn serialize(&self, serializer: S) -> Result { - let mut bytes = Vec::new(); - NbtTag::String(self.0.clone().into_boxed_str()) - .serialize(&mut NbtWriteHelperJava::new(&mut bytes)) - .map_err(serde::ser::Error::custom)?; - serializer.serialize_bytes(&bytes) - } -} - -struct NetworkNbtTag(NbtTag); - -impl serde::Serialize for NetworkNbtTag { - fn serialize(&self, serializer: S) -> Result { - let mut bytes = Vec::new(); - self.0 - .clone() - .serialize(&mut NbtWriteHelperJava::new(&mut bytes)) - .map_err(serde::ser::Error::custom)?; - serializer.serialize_bytes(&bytes) - } -} - impl DataComponentCodec for ConsumableImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &self.consume_seconds)?; - seq.serialize_field::("", &VarInt(self.animation as i32))?; - seq.serialize_field::>( - "", + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_f32(self.consume_seconds)?; + seq.write_var_int(&VarInt(self.animation as i32))?; + crate::IdOr::::write( &data_to_proto_sound(&self.sound_event), + seq, + |w, e| { + w.write_string(&e.sound_name)?; + w.write_option(&e.range, |w2, r| w2.write_f32(*r)) + }, )?; - seq.serialize_field::("", &self.consume_particles)?; - seq.serialize_field::("", &VarInt(self.effects.len() as i32))?; + seq.write_bool(self.consume_particles)?; + seq.write_var_int(&VarInt(self.effects.len() as i32))?; for effect in self.effects.iter() { serialize_consume_effect(effect, seq)?; @@ -425,33 +373,25 @@ impl DataComponentCodec for ConsumableImpl { Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let consume_seconds = seq.next_element::()?.ok_or(de::Error::custom( - "No ConsumableImpl consume_seconds float!", - ))?; - let animation_id = seq - .next_element::()? - .ok_or(de::Error::custom("No ConsumableImpl animation VarInt!"))?; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let consume_seconds = seq.get_f32()?; + let animation_id = seq.get_var_int()?; let animation: ConsumeAnimation = animation_id .0 .try_into() - .map_err(|()| de::Error::custom("Invalid ConsumableImpl animation id!"))?; - let proto_sound_event = seq - .next_element::>()? - .ok_or(de::Error::custom( - "No ConsumableImpl sound_event IdOr!", - ))?; - let consume_particles = seq.next_element::()?.ok_or(de::Error::custom( - "No ConsumableImpl consume_particles bool!", - ))?; + .map_err(|()| ReadingError::Message("Invalid ConsumableImpl animation id!".into()))?; + let proto_sound_event = crate::IdOr::::read(seq, |r| { + let sound_name = r.get_str()?.into(); + let range = r.get_option(NetworkReadExt::get_f32)?; + Ok(crate::SoundEvent { sound_name, range }) + })?; + let consume_particles = seq.get_bool()?; - let sound_event = proto_to_data_sound(&proto_sound_event) - .ok_or(de::Error::custom("Invalid sound in ConsumableImpl"))?; - let effects_len = seq - .next_element::()? - .ok_or(de::Error::custom("No array_len VarInt in ConsumableImpl"))? - .0; + let sound_event = proto_to_data_sound(&proto_sound_event).ok_or(ReadingError::Message( + "Invalid sound in ConsumableImpl".into(), + ))?; + let effects_len = seq.get_var_int()?.0; let mut effects_vec = Vec::with_capacity(effects_len as usize); for _ in 0..effects_len { @@ -471,59 +411,74 @@ impl DataComponentCodec for ConsumableImpl { } impl DataComponentCodec for EquippableImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt(self.slot.get_slot_index()))?; - seq.serialize_field::>( - "", + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_var_int(&VarInt(self.slot.get_slot_index()))?; + crate::IdOr::::write( &data_to_proto_sound(&self.equip_sound), + seq, + |w, e| { + w.write_string(&e.sound_name)?; + w.write_option(&e.range, |w2, r| w2.write_f32(*r)) + }, )?; - seq.serialize_field::>>("", &self.asset_id)?; - seq.serialize_field::>>("", &self.camera_overlay)?; - seq.serialize_field::("", &self.allowed_entities.is_some())?; + seq.write_bool(self.asset_id.is_some())?; + if let Some(asset) = &self.asset_id { + seq.write_string(asset)?; + } + + seq.write_bool(self.camera_overlay.is_some())?; + if let Some(overlay) = &self.camera_overlay { + seq.write_string(overlay)?; + } + + seq.write_bool(self.allowed_entities.is_some())?; if let Some(allowed) = &self.allowed_entities { serialize_idset(allowed, seq)?; } - seq.serialize_field::("", &self.dispensable)?; - seq.serialize_field::("", &self.swappable)?; - seq.serialize_field::("", &self.damage_on_hurt)?; - seq.serialize_field::("", &self.equip_on_interact)?; - seq.serialize_field::("", &self.can_be_sheared)?; - seq.serialize_field::>( - "", + seq.write_bool(self.dispensable)?; + seq.write_bool(self.swappable)?; + seq.write_bool(self.damage_on_hurt)?; + seq.write_bool(self.equip_on_interact)?; + seq.write_bool(self.can_be_sheared)?; + crate::IdOr::::write( &data_to_proto_sound(&self.shearing_sound), + seq, + |w, e| { + w.write_string(&e.sound_name)?; + w.write_option(&e.range, |w2, r| w2.write_f32(*r)) + }, ) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let slot_index = seq - .next_element::()? - .ok_or(de::Error::custom("No EquippableImpl slot VarInt!"))? - .0; - let slot = EquipmentSlot::from_slot_index(slot_index).ok_or(de::Error::custom(format!( - "Invalid equipment slot index {slot_index}" - )))?; - let equip_sound = proto_to_data_sound( - &seq.next_element::>()? - .ok_or(de::Error::custom( - "No EquippableImpl equip_sound IdOr!", - ))?, - ) - .ok_or(de::Error::custom("Invalid sound in EquippableImpl"))?; - let asset_id = - seq.next_element::>>()? - .ok_or(de::Error::custom( - "No EquippableImpl asset_id optional string!", - ))?; - let camera_overlay = - seq.next_element::>>()? - .ok_or(de::Error::custom( - "No EquippableImpl camera_overlay optional string!", - ))?; - let has_allowed_entities = seq.next_element::()?.ok_or(de::Error::custom( - "No EquippableImpl allowed_entities presence bool!", + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let slot_index = seq.get_var_int()?.0; + let slot = EquipmentSlot::from_slot_index(slot_index).ok_or(ReadingError::Message( + format!("Invalid equipment slot index {slot_index}"), ))?; + let equip_sound = proto_to_data_sound(&crate::IdOr::::read(seq, |r| { + let sound_name = r.get_str()?.into(); + let range = r.get_option(NetworkReadExt::get_f32)?; + Ok(crate::SoundEvent { sound_name, range }) + })?) + .ok_or(ReadingError::Message( + "Invalid sound in EquippableImpl".into(), + ))?; + + let asset_id = if seq.get_bool()? { + Some(Cow::Owned(seq.get_str()?.into())) + } else { + None + }; + + let camera_overlay = if seq.get_bool()? { + Some(Cow::Owned(seq.get_str()?.into())) + } else { + None + }; + + let has_allowed_entities = seq.get_bool()?; let allowed_entities: Option> = if has_allowed_entities { Some(deserialize_idset(seq)?) @@ -531,30 +486,20 @@ impl DataComponentCodec for EquippableImpl { None }; - let dispensable = seq - .next_element::()? - .ok_or(de::Error::custom("No EquippableImpl dispensable bool!"))?; - let swappable = seq - .next_element::()? - .ok_or(de::Error::custom("No EquippableImpl swappable bool!"))?; - let damage_on_hurt = seq - .next_element::()? - .ok_or(de::Error::custom("No EquippableImpl damage_on_hurt bool!"))?; - let equip_on_interact = seq.next_element::()?.ok_or(de::Error::custom( - "No EquippableImpl equip_on_interact bool!", - ))?; - let can_be_sheared = seq - .next_element::()? - .ok_or(de::Error::custom("No EquippableImpl can_be_shared bool!"))?; - let shearing_sound = proto_to_data_sound( - &seq.next_element::>()? - .ok_or(de::Error::custom( - "No EquippableImpl shearing_sound IdOr!", - ))?, - ) - .ok_or(de::Error::custom( - "Invalid shearing sound in EquippableImpl", - ))?; + let dispensable = seq.get_bool()?; + let swappable = seq.get_bool()?; + let damage_on_hurt = seq.get_bool()?; + let equip_on_interact = seq.get_bool()?; + let can_be_sheared = seq.get_bool()?; + let shearing_sound = + proto_to_data_sound(&crate::IdOr::::read(seq, |r| { + let sound_name = r.get_str()?.into(); + let range = r.get_option(NetworkReadExt::get_f32)?; + Ok(crate::SoundEvent { sound_name, range }) + })?) + .ok_or(ReadingError::Message( + "Invalid shearing sound in EquippableImpl".into(), + ))?; Ok(Self { slot, @@ -573,21 +518,21 @@ impl DataComponentCodec for EquippableImpl { } impl DataComponentCodec for PotionContentsImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { // Potion ID (optional) if let Some(potion_id) = self.potion_id { - seq.serialize_field::("", &true)?; - seq.serialize_field::("", &VarInt::from(potion_id))?; + seq.write_bool(true)?; + seq.write_var_int(&VarInt::from(potion_id))?; } else { - seq.serialize_field::("", &false)?; + seq.write_bool(false)?; } // Custom color (optional) if let Some(color) = self.custom_color { - seq.serialize_field::("", &true)?; - seq.serialize_field::("", &color)?; + seq.write_bool(true)?; + seq.write_i32(color)?; } else { - seq.serialize_field::("", &false)?; + seq.write_bool(false)?; } // Custom effects list @@ -595,51 +540,33 @@ impl DataComponentCodec for PotionContentsImpl { // Custom name (optional) if let Some(name) = &self.custom_name { - seq.serialize_field::("", &true)?; - seq.serialize_field::<&str>("", &name.as_str())?; + seq.write_bool(true)?; + seq.write_string(name.as_str())?; } else { - seq.serialize_field::("", &false)?; + seq.write_bool(false)?; } Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { // Potion ID (optional) - let has_potion = seq - .next_element::()? - .ok_or(de::Error::custom("No PotionContents has_potion bool!"))?; + let has_potion = seq.get_bool()?; let potion_id = has_potion - .then(|| { - seq.next_element::()? - .ok_or(de::Error::custom("No PotionContents potion_id VarInt!")) - .map(|value| value.0) - }) + .then(|| seq.get_var_int().map(|value| value.0)) .transpose()?; // Custom color (optional) - let has_color = seq - .next_element::()? - .ok_or(de::Error::custom("No PotionContents has_color bool!"))?; - let custom_color = has_color - .then(|| { - seq.next_element::()? - .ok_or(de::Error::custom("No PotionContents custom_color i32!")) - }) - .transpose()?; + let has_color = seq.get_bool()?; + let custom_color = has_color.then(|| seq.get_i32()).transpose()?; // Custom effects list let custom_effects = deserialize_status_effects(seq)?; // Custom name (optional) - let has_name = seq - .next_element::()? - .ok_or(de::Error::custom("No PotionContents has_name bool!"))?; + let has_name = seq.get_bool()?; let custom_name = has_name - .then(|| { - seq.next_element::()? - .ok_or(de::Error::custom("No PotionContents custom_name String!")) - }) + .then(|| seq.get_str().map(String::from)) .transpose()?; Ok(Self { @@ -652,26 +579,19 @@ impl DataComponentCodec for PotionContentsImpl { } /// Helper to skip hidden effect parameters recursively -fn skip_effect_parameters<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<(), A::Error> { +fn skip_effect_parameters(seq: &mut impl NetworkReadExt) -> Result<(), ReadingError> { // amplifier - seq.next_element::()? - .ok_or(de::Error::custom("No hidden amplifier VarInt!"))?; + seq.get_var_int()?; // duration - seq.next_element::()? - .ok_or(de::Error::custom("No hidden duration VarInt!"))?; + seq.get_var_int()?; // ambient - seq.next_element::()? - .ok_or(de::Error::custom("No hidden ambient bool!"))?; + seq.get_bool()?; // show_particles - seq.next_element::()? - .ok_or(de::Error::custom("No hidden show_particles bool!"))?; + seq.get_bool()?; // show_icon - seq.next_element::()? - .ok_or(de::Error::custom("No hidden show_icon bool!"))?; + seq.get_bool()?; // has_hidden (recursive) - let has_hidden = seq - .next_element::()? - .ok_or(de::Error::custom("No hidden has_hidden bool!"))?; + let has_hidden = seq.get_bool()?; if has_hidden { skip_effect_parameters(seq)?; } @@ -679,91 +599,69 @@ fn skip_effect_parameters<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<(), A::Er } impl DataComponentCodec for FireworkExplosionImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { // Shape (VarInt enum) - seq.serialize_field::("", &VarInt::from(self.shape.to_id()))?; + seq.write_var_int(&VarInt::from(self.shape.to_id()))?; // Colors list - seq.serialize_field::("", &VarInt::from(self.colors.len() as i32))?; + seq.write_var_int(&VarInt::from(self.colors.len() as i32))?; for color in &self.colors { - seq.serialize_field::("", color)?; + seq.write_i32(*color)?; } // Fade colors list - seq.serialize_field::("", &VarInt::from(self.fade_colors.len() as i32))?; + seq.write_var_int(&VarInt::from(self.fade_colors.len() as i32))?; for color in &self.fade_colors { - seq.serialize_field::("", color)?; + seq.write_i32(*color)?; } // hasTrail - seq.serialize_field::("", &self.has_trail)?; + seq.write_bool(self.has_trail)?; // hasTwinkle - seq.serialize_field::("", &self.has_twinkle)?; + seq.write_bool(self.has_twinkle)?; Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { // Needs a length cap during deserialization to prevent OOM from malicious packets // Vanilla doesn't have any limits (Integer.MAX_VALUE is technically a limit but not enforced in practice) const MAX_COLORS: usize = 256; const MAX_FADE_COLORS: usize = 256; // Shape (VarInt enum) - let shape_id = seq - .next_element::()? - .ok_or(de::Error::custom( - "No FireworkExplosionImpl shape_id VarInt!", - ))? - .0; - let shape = FireworkExplosionShape::from_id(shape_id) - .ok_or(de::Error::custom("Invalid FireworkExplosionShape id!"))?; + let shape_id = seq.get_var_int()?.0; + let shape = FireworkExplosionShape::from_id(shape_id).ok_or(ReadingError::Message( + "Invalid FireworkExplosionShape id!".into(), + ))?; // Colors list - let colors_len = seq - .next_element::()? - .ok_or(de::Error::custom( - "No FireworkExplosionImpl colors_len VarInt!", - ))? - .0 as usize; + let colors_len = seq.get_var_int()?.0 as usize; if colors_len > MAX_COLORS { - return Err(de::Error::custom(format!( + return Err(ReadingError::Message(format!( "FireworkExplosionImpl colors_len {colors_len} exceeds maximum of {MAX_COLORS}" ))); } let mut colors = Vec::with_capacity(colors_len); for _ in 0..colors_len { - let color = seq - .next_element::()? - .ok_or(de::Error::custom("No FireworkExplosionImpl color i32!"))?; + let color = seq.get_i32()?; colors.push(color); } // Fade colors list - let fade_colors_len = seq - .next_element::()? - .ok_or(de::Error::custom( - "No FireworkExplosionImpl fade_colors_len VarInt!", - ))? - .0 as usize; + let fade_colors_len = seq.get_var_int()?.0 as usize; if fade_colors_len > MAX_FADE_COLORS { - return Err(de::Error::custom(format!( + return Err(ReadingError::Message(format!( "FireworkExplosionImpl fade_colors_len {fade_colors_len} exceeds maximum of {MAX_FADE_COLORS}" ))); } let mut fade_colors = Vec::with_capacity(fade_colors_len); for _ in 0..fade_colors_len { - let color = seq.next_element::()?.ok_or(de::Error::custom( - "No FireworkExplosionImpl fade_color i32!", - ))?; + let color = seq.get_i32()?; fade_colors.push(color); } // hasTrail - let has_trail = seq.next_element::()?.ok_or(de::Error::custom( - "No FireworkExplosionImpl has_trail bool!", - ))?; + let has_trail = seq.get_bool()?; // hasTwinkle - let has_twinkle = seq.next_element::()?.ok_or(de::Error::custom( - "No FireworkExplosionImpl has_twinkle bool!", - ))?; + let has_twinkle = seq.get_bool()?; Ok(Self::new( shape, @@ -776,18 +674,18 @@ impl DataComponentCodec for FireworkExplosionImpl { } impl DataComponentCodec for FireworksImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { // Flight duration (VarInt) - seq.serialize_field::("", &VarInt::from(self.flight_duration))?; + seq.write_var_int(&VarInt::from(self.flight_duration))?; // Explosions list - seq.serialize_field::("", &VarInt::from(self.explosions.len() as i32))?; + seq.write_var_int(&VarInt::from(self.explosions.len() as i32))?; for explosion in &self.explosions { explosion.serialize(seq)?; } Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { // Needs a length cap during deserialization to prevent OOM from malicious packets // Vanilla doesn't have any limits const MAX_EXPLOSIONS: usize = 256; @@ -795,25 +693,17 @@ impl DataComponentCodec for FireworksImpl { const MAX_FLIGHT_DURATION: i32 = 255; // Flight duration - let flight_duration = seq - .next_element::()? - .ok_or(de::Error::custom( - "No FireworksImpl flight_duration VarInt!", - ))? - .0; + let flight_duration = seq.get_var_int()?.0; if !(0..=MAX_FLIGHT_DURATION).contains(&flight_duration) { - return Err(de::Error::custom(format!( + return Err(ReadingError::Message(format!( "FireworksImpl flight_duration {flight_duration} is out of bounds (0-{MAX_FLIGHT_DURATION})" ))); } // Explosions list - let explosions_len = seq - .next_element::()? - .ok_or(de::Error::custom("No FireworksImpl explosions_len VarInt!"))? - .0 as usize; + let explosions_len = seq.get_var_int()?.0 as usize; if explosions_len > MAX_EXPLOSIONS { - return Err(de::Error::custom(format!( + return Err(ReadingError::Message(format!( "FireworksImpl explosions_len {explosions_len} exceeds maximum of {MAX_EXPLOSIONS}" ))); } @@ -829,40 +719,31 @@ impl DataComponentCodec for FireworksImpl { } impl DataComponentCodec for StoredEnchantmentsImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt::from(self.enchantment.len() as i32))?; + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_var_int(&VarInt::from(self.enchantment.len() as i32))?; for (enc, level) in self.enchantment.iter() { - seq.serialize_field::("", &VarInt::from(enc.id))?; - seq.serialize_field::("", &VarInt::from(*level))?; + seq.write_var_int(&VarInt::from(enc.id))?; + seq.write_var_int(&VarInt::from(*level))?; } Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { const MAX_ENCHANTMENTS: usize = 256; - let len = seq - .next_element::()? - .ok_or(de::Error::custom("No StoredEnchantmentsImpl len VarInt!"))? - .0 as usize; + let len = seq.get_var_int()?.0 as usize; if len > MAX_ENCHANTMENTS { - return Err(de::Error::custom("Too many enchantments")); + return Err(ReadingError::Message("Too many enchantments".into())); } let mut stored_enchantments = Vec::with_capacity(len); for _ in 0..len { - let id = seq - .next_element::()? - .ok_or(de::Error::custom("No StoredEnchantmentsImpl id VarInt!"))? - .0 as u8; - let level = seq - .next_element::()? - .ok_or(de::Error::custom("No StoredEnchantmentsImpl level VarInt!"))? - .0; + let id = seq.get_var_int()?.0 as u8; + let level = seq.get_var_int()?.0; stored_enchantments.push(( - Enchantment::from_id(id).ok_or(de::Error::custom( - "StoredEnchantmentsImpl Enchantment VarInt Incorrect!", + Enchantment::from_id(id).ok_or(ReadingError::Message( + "StoredEnchantmentsImpl Enchantment VarInt Incorrect!".into(), ))?, level, )); @@ -873,14 +754,14 @@ impl DataComponentCodec for StoredEnchantmentsImpl { } } -pub fn deserialize<'a, A: SeqAccess<'a>>( +pub fn deserialize( id: DataComponent, - seq: &mut A, -) -> Result, A::Error> { + seq: &mut impl NetworkReadExt, +) -> Result, ReadingError> { match id { DataComponent::MaxStackSize => Ok(MaxStackSizeImpl::deserialize(seq)?.to_dyn()), - DataComponent::CustomData => Err(serde::de::Error::custom( - "CustomData raw component decoding is not supported; use the custom-data item-stack API", + DataComponent::CustomData => Err(ReadingError::Message( + "CustomData raw component decoding is not supported; use the custom-data item-stack API".into(), )), DataComponent::Enchantments => Ok(EnchantmentsImpl::deserialize(seq)?.to_dyn()), DataComponent::Damage => Ok(DamageImpl::deserialize(seq)?.to_dyn()), @@ -896,14 +777,14 @@ pub fn deserialize<'a, A: SeqAccess<'a>>( DataComponent::UseCooldown => Ok(UseCooldownImpl::deserialize(seq)?.to_dyn()), DataComponent::MapId => Ok(MapIdImpl::deserialize(seq)?.to_dyn()), DataComponent::BundleContents => Ok(BundleContentsImpl::deserialize(seq)?.to_dyn()), - _ => Err(serde::de::Error::custom(format!("{id:?} (TODO)"))), + _ => Err(ReadingError::Message(format!("{id:?} (TODO)"))), } } -pub fn serialize( +pub fn serialize( id: DataComponent, value: &dyn DataComponentImpl, - seq: &mut T, -) -> Result<(), T::Error> { + seq: &mut impl NetworkWriteExt, +) -> Result<(), WritingError> { match id { DataComponent::MaxStackSize => get::(value).serialize(seq), DataComponent::CustomData => get::(value).serialize(seq), @@ -921,7 +802,7 @@ pub fn serialize( DataComponent::UseCooldown => get::(value).serialize(seq), DataComponent::MapId => get::(value).serialize(seq), DataComponent::BundleContents => get::(value).serialize(seq), - _ => Err(serde::ser::Error::custom(format!( + _ => Err(WritingError::Message(format!( "{} not yet implemented", id.to_name() ))), @@ -929,34 +810,33 @@ pub fn serialize( } impl DataComponentCodec for MapIdImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt::from(self.id)) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_var_int(&VarInt::from(self.id)) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let id = seq - .next_element::()? - .ok_or(de::Error::custom("No MapId VarInt!"))? - .0; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let id = seq.get_var_int()?.0; Ok(Self { id }) } } impl DataComponentCodec for UseCooldownImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &self.seconds)?; - seq.serialize_field::>("", &self.cooldown_group) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_f32(self.seconds)?; + seq.write_bool(self.cooldown_group.is_some())?; + if let Some(group) = &self.cooldown_group { + seq.write_string(group)?; + } + Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let seconds = seq - .next_element::()? - .ok_or(de::Error::custom("No UseCooldownImpl seconds float!"))?; - let cooldown_group = seq - .next_element::>()? - .ok_or(de::Error::custom( - "No UseCooldownImpl cooldown_group optional string!", - ))?; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let seconds = seq.get_f32()?; + let cooldown_group = if seq.get_bool()? { + Some(seq.get_str()?.into()) + } else { + None + }; Ok(Self { seconds, cooldown_group, @@ -964,63 +844,49 @@ impl DataComponentCodec for UseCooldownImpl { } } -fn deserialize_item_stack_template<'a, A: SeqAccess<'a>>( - seq: &mut A, -) -> Result { +fn deserialize_item_stack_template( + seq: &mut impl NetworkReadExt, +) -> Result { const MAX_COMPONENTS: i32 = 256; - let item_id = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("Missing item_id in ItemStackTemplate"))? - .0 as u16; + let item_id = seq.get_var_int()?.0 as u16; - let count = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("Missing count in ItemStackTemplate"))? - .0 as u8; + let count = seq.get_var_int()?.0 as u8; - let num_to_add = seq.next_element::()?.map_or(0, |v| v.0); - let num_to_remove = seq.next_element::()?.map_or(0, |v| v.0); + let num_to_add = seq.get_var_int()?.0; + let num_to_remove = seq.get_var_int()?.0; if num_to_add < 0 || num_to_remove < 0 { - return Err(de::Error::custom("Negative component count")); + return Err(ReadingError::Message("Negative component count".into())); } let total_components = num_to_add .checked_add(num_to_remove) - .ok_or_else(|| de::Error::custom("Component count overflow"))?; + .ok_or_else(|| ReadingError::Message("Component count overflow".into()))?; if total_components > MAX_COMPONENTS { - return Err(de::Error::custom( - "Too many components in ItemStackTemplate patch", + return Err(ReadingError::Message( + "Too many components in ItemStackTemplate patch".into(), )); } let mut patch = Vec::with_capacity((num_to_add + num_to_remove) as usize); for _ in 0..num_to_add { - let id_val = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("Missing component ID"))? - .0; + let id_val = seq.get_var_int()?.0; let id = DataComponent::try_from_id(id_val as u8) - .ok_or_else(|| de::Error::custom(format!("Unknown component ID: {id_val}")))?; + .ok_or_else(|| ReadingError::Message(format!("Unknown component ID: {id_val}")))?; - let _byte_len = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("No data len VarInt!"))?; + let _byte_len = seq.get_var_int()?; let component_impl = deserialize(id, seq)?; patch.push((id, Some(component_impl))); } for _ in 0..num_to_remove { - let id_val = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("Missing remove component ID"))? - .0; + let id_val = seq.get_var_int()?.0; let id = DataComponent::try_from_id(id_val as u8) - .ok_or_else(|| de::Error::custom("Unknown component ID"))?; + .ok_or_else(|| ReadingError::Message("Unknown component ID".into()))?; patch.push((id, None)); } @@ -1031,12 +897,12 @@ fn deserialize_item_stack_template<'a, A: SeqAccess<'a>>( )) } -fn serialize_item_stack_template( +fn serialize_item_stack_template( stack: &pumpkin_data::item_stack::ItemStack, - seq: &mut T, -) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt::from(stack.item.id))?; - seq.serialize_field::("", &VarInt::from(stack.item_count))?; + seq: &mut impl NetworkWriteExt, +) -> Result<(), WritingError> { + seq.write_var_int(&VarInt::from(stack.item.id))?; + seq.write_var_int(&VarInt::from(stack.item_count))?; let mut to_add = 0u8; let mut to_remove = 0u8; @@ -1048,19 +914,19 @@ fn serialize_item_stack_template( } } - seq.serialize_field::("", &VarInt::from(to_add))?; - seq.serialize_field::("", &VarInt::from(to_remove))?; + seq.write_var_int(&VarInt::from(to_add))?; + seq.write_var_int(&VarInt::from(to_remove))?; for (id, data) in &stack.patch { if let Some(data) = data { - seq.serialize_field::("", &VarInt::from(id.to_id()))?; + seq.write_var_int(&VarInt::from(id.to_id()))?; serialize(*id, data.as_ref(), seq)?; } } for (id, data) in &stack.patch { if data.is_none() { - seq.serialize_field::("", &VarInt::from(id.to_id()))?; + seq.write_var_int(&VarInt::from(id.to_id()))?; } } @@ -1068,24 +934,23 @@ fn serialize_item_stack_template( } impl DataComponentCodec for BundleContentsImpl { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field::("", &VarInt::from(self.items.len() as i32))?; + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_var_int(&VarInt::from(self.items.len() as i32))?; for item in &self.items { serialize_item_stack_template(item, seq)?; } Ok(()) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { const MAX_BUNDLE_ITEMS: usize = 64; - let len = seq - .next_element::()? - .ok_or(de::Error::custom("No BundleContentsImpl len VarInt!"))? - .0 as usize; + let len = seq.get_var_int()?.0 as usize; if len > MAX_BUNDLE_ITEMS { - return Err(de::Error::custom("Too many items in BundleContents")); + return Err(ReadingError::Message( + "Too many items in BundleContents".into(), + )); } let mut items = Vec::with_capacity(len); @@ -1099,19 +964,13 @@ impl DataComponentCodec for BundleContentsImpl { macro_rules! codec_string_variant { ($struct_name:ident) => { impl DataComponentCodec for $struct_name { - fn serialize(&self, seq: &mut T) -> Result<(), T::Error> { - seq.serialize_field("", &self.value) + fn serialize(&self, seq: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + seq.write_string(&self.value) } - fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result { - let value = seq - .next_element::()? - .ok_or(de::Error::custom(concat!( - "No ", - stringify!($struct_name), - " value string!" - )))?; + fn deserialize(seq: &mut impl NetworkReadExt) -> Result { + let value = seq.get_str()?; Ok(Self { - value: Cow::Owned(value), + value: Cow::Owned(value.into()), }) } } diff --git a/pumpkin-protocol/src/codec/item_stack_seralizer.rs b/pumpkin-protocol/src/codec/item_stack_seralizer.rs index 998ba478c..9957ab846 100644 --- a/pumpkin-protocol/src/codec/item_stack_seralizer.rs +++ b/pumpkin-protocol/src/codec/item_stack_seralizer.rs @@ -1,6 +1,6 @@ use crate::VarInt; use crate::codec::data_component::{deserialize, serialize}; -use crate::ser::{NetworkReadExt, ReadingError, WritingError, deserializer, serializer}; +use crate::ser::{NetworkReadExt, NetworkWriteExt, ReadingError, WritingError}; use pumpkin_data::data_component::DataComponent; use pumpkin_data::data_component_impl::{CustomNameImpl, DataComponentImpl}; use pumpkin_data::item::Item; @@ -9,13 +9,8 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::tag::NbtTag; use pumpkin_util::text::TextComponent; use pumpkin_util::version::JavaMinecraftVersion; -use serde::ser::SerializeStruct; -use serde::{ - Deserialize, Serialize, Serializer, - de::{self, DeserializeSeed, SeqAccess}, -}; use std::borrow::Cow; -use std::io::{Cursor, Read}; +use std::io::Cursor; #[derive(Clone)] pub struct ItemStackSerializer<'a>(pub Cow<'a, ItemStack>); @@ -35,67 +30,52 @@ fn item_component_counts(stack: &ItemStack) -> (u8, u8) { (to_add, to_remove) } -fn serialize_any_item_stack_with_id( +fn serialize_any_item_stack_with_id( stack: &ItemStack, item_id: u16, is_template: bool, - serializer: S, -) -> Result { + write: &mut impl NetworkWriteExt, +) -> Result<(), WritingError> { if stack.is_empty() { - VarInt(0).serialize(serializer) + write.put_var_int(&VarInt(0)) } else { let (to_add, to_remove) = item_component_counts(stack); - let mut seq = serializer.serialize_struct("", 0)?; if is_template { - seq.serialize_field("", &VarInt::from(item_id))?; - seq.serialize_field("", &VarInt::from(stack.item_count))?; + write.put_var_int(&VarInt::from(item_id))?; + write.put_var_int(&VarInt::from(stack.item_count))?; } else { - seq.serialize_field("", &VarInt::from(stack.item_count))?; - seq.serialize_field("", &VarInt::from(item_id))?; + write.put_var_int(&VarInt::from(stack.item_count))?; + write.put_var_int(&VarInt::from(item_id))?; } - seq.serialize_field::("", &VarInt::from(to_add))?; - seq.serialize_field::("", &VarInt::from(to_remove))?; + write.put_var_int(&VarInt::from(to_add))?; + write.put_var_int(&VarInt::from(to_remove))?; for (id, data) in &stack.patch { if let Some(data) = data { - seq.serialize_field::("", &VarInt::from(id.to_id()))?; - serialize(*id, data.as_ref(), &mut seq)?; + write.put_var_int(&VarInt::from(id.to_id()))?; + serialize(*id, data.as_ref(), write)?; } } for (id, data) in &stack.patch { if data.is_none() { - seq.serialize_field::("", &VarInt::from(id.to_id()))?; + write.put_var_int(&VarInt::from(id.to_id()))?; } } - seq.end() + Ok(()) } } -fn serialize_item_stack_with_id( + +fn serialize_item_stack_with_id( stack: &ItemStack, item_id: u16, - serializer: S, -) -> Result { - serialize_any_item_stack_with_id(stack, item_id, false, serializer) + write: &mut impl NetworkWriteExt, +) -> Result<(), WritingError> { + serialize_any_item_stack_with_id(stack, item_id, false, write) } -struct ComponentAccess { - deserializer: deserializer::Deserializer, -} - -impl<'de, R: Read> SeqAccess<'de> for ComponentAccess { - type Error = ReadingError; - - fn next_element_seed>( - &mut self, - seed: T, - ) -> Result, Self::Error> { - seed.deserialize(&mut self.deserializer).map(Some) - } -} - -fn read_component_id(read: &mut impl Read) -> Result { +fn read_component_id(read: &mut impl NetworkReadExt) -> Result { let id_val = read.get_var_int()?.0; let id_u8 = id_val .try_into() @@ -122,7 +102,7 @@ fn decode_custom_name(component_data: &[u8]) -> Result Result<(DataComponent, Box), ReadingError> { let id = read_component_id(read)?; let byte_len = read.get_var_int()?.0; @@ -134,114 +114,82 @@ fn read_length_prefixed_component( let component_impl = if id == DataComponent::CustomName { decode_custom_name(component_data.as_ref())? } else { - let cursor = Cursor::new(component_data); - let mut access = ComponentAccess { - deserializer: deserializer::Deserializer::new(cursor), - }; - deserialize(id, &mut access)? + let mut cursor = Cursor::new(component_data); + deserialize(id, &mut cursor)? }; Ok((id, component_impl)) } -impl<'de> Deserialize<'de> for ItemStackSerializer<'static> { - fn deserialize>(deserializer: D) -> Result { - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = ItemStackSerializer<'static>; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid Slot encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - const MAX_COMPONENTS: i32 = 256; - - let item_count = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("Failed to decode VarInt"))?; - - if item_count.0 == 0 { - return Ok(ItemStackSerializer(Cow::Borrowed(ItemStack::EMPTY))); - } - - let item_id = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("No item id VarInt!"))?; - - let num_to_add = seq.next_element::()?.map_or(0, |v| v.0); - let num_to_remove = seq.next_element::()?.map_or(0, |v| v.0); - - if num_to_add < 0 || num_to_remove < 0 { - return Err(de::Error::custom("Negative component count")); - } - - let total_components = num_to_add - .checked_add(num_to_remove) - .ok_or_else(|| de::Error::custom("Component count overflow"))?; - - if total_components > MAX_COMPONENTS { - return Err(de::Error::custom("Too many components in ItemStack patch")); - } - - let mut patch = Vec::with_capacity((num_to_add + num_to_remove) as usize); - - for _ in 0..num_to_add { - let id_val = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("Missing component ID"))? - .0; - let id = DataComponent::try_from_id(id_val as u8).ok_or_else(|| { - de::Error::custom(format!("Unknown component ID: {id_val}")) - })?; - - // Minecraft protocol sends a byte length for the component data here - let _byte_len = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("No data len VarInt!"))?; - - let component_impl = deserialize(id, &mut seq)?; - - patch.push((id, Some(component_impl))); - } - - for _ in 0..num_to_remove { - let id_val = seq - .next_element::()? - .ok_or_else(|| de::Error::custom("Missing remove component ID"))? - .0; - let id = DataComponent::try_from_id(id_val as u8) - .ok_or_else(|| de::Error::custom("Unknown component ID"))?; - patch.push((id, None)); - } - - let item_id_u16: u16 = item_id - .0 - .try_into() - .map_err(|_| de::Error::custom("Invalid item id!"))?; - - Ok(ItemStackSerializer(Cow::Owned( - ItemStack::new_with_component( - item_count.0 as u8, - Item::from_id(item_id_u16).unwrap_or(&Item::AIR), - patch, - ), - ))) - } - } - deserializer.deserialize_seq(Visitor) - } -} - -impl Serialize for ItemStackSerializer<'_> { - fn serialize(&self, serializer: S) -> Result { - serialize_item_stack_with_id(self.0.as_ref(), self.0.item.id, serializer) - } -} - impl ItemStackSerializer<'_> { + pub fn read( + read: &mut impl NetworkReadExt, + ) -> Result, ReadingError> { + const MAX_COMPONENTS: i32 = 256; + + let item_count = read.get_var_int()?; + if item_count.0 == 0 { + return Ok(ItemStackSerializer(Cow::Borrowed(ItemStack::EMPTY))); + } + + let item_id = read.get_var_int()?; + let num_to_add = read.get_var_int()?.0; + let num_to_remove = read.get_var_int()?.0; + + if num_to_add < 0 || num_to_remove < 0 { + return Err(ReadingError::Message("Negative component count".into())); + } + + let total_components = num_to_add + .checked_add(num_to_remove) + .ok_or_else(|| ReadingError::Message("Component count overflow".into()))?; + + if total_components > MAX_COMPONENTS { + return Err(ReadingError::Message( + "Too many components in ItemStack patch".into(), + )); + } + + let mut patch = Vec::with_capacity((num_to_add + num_to_remove) as usize); + + for _ in 0..num_to_add { + let id_val = read.get_var_int()?.0; + let id = DataComponent::try_from_id(id_val as u8) + .ok_or_else(|| ReadingError::Message(format!("Unknown component ID: {id_val}")))?; + + let _byte_len = read.get_var_int()?; + + let component_impl = deserialize(id, read)?; + patch.push((id, Some(component_impl))); + } + + for _ in 0..num_to_remove { + let id_val = read.get_var_int()?.0; + let id = DataComponent::try_from_id(id_val as u8) + .ok_or_else(|| ReadingError::Message("Unknown component ID".into()))?; + patch.push((id, None)); + } + + let item_id_u16: u16 = item_id + .0 + .try_into() + .map_err(|_| ReadingError::Message("Invalid item id!".into()))?; + + Ok(ItemStackSerializer(Cow::Owned( + ItemStack::new_with_component( + item_count.0 as u8, + Item::from_id(item_id_u16).unwrap_or(&Item::AIR), + patch, + ), + ))) + } + + pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + serialize_item_stack_with_id(self.0.as_ref(), self.0.item.id, write) + } + pub fn read_length_prefixed_optional( - mut read: impl Read, + read: &mut impl NetworkReadExt, ) -> Result, ReadingError> { const MAX_COMPONENTS: i32 = 256; @@ -275,12 +223,12 @@ impl ItemStackSerializer<'_> { let mut patch = Vec::with_capacity(total_components as usize); for _ in 0..num_to_add { - let (id, component_impl) = read_length_prefixed_component(&mut read)?; + let (id, component_impl) = read_length_prefixed_component(read)?; patch.push((id, Some(component_impl))); } for _ in 0..num_to_remove { - patch.push((read_component_id(&mut read)?, None)); + patch.push((read_component_id(read)?, None)); } let item_id_u16 = item_id @@ -299,12 +247,11 @@ impl ItemStackSerializer<'_> { pub fn write_with_version( &self, - write: impl std::io::Write, + write: &mut impl NetworkWriteExt, version: &JavaMinecraftVersion, ) -> Result<(), WritingError> { let remapped_item_id = remap_item_id_for_version(self.0.item.id, *version); - let mut network_serializer = serializer::Serializer::new(write); - serialize_item_stack_with_id(self.0.as_ref(), remapped_item_id, &mut network_serializer) + serialize_item_stack_with_id(self.0.as_ref(), remapped_item_id, write) } #[must_use] @@ -346,6 +293,49 @@ pub struct ItemComponentHash { pub removed: Vec, } +impl ItemComponentHash { + pub fn read(read: &mut impl NetworkReadExt) -> Result { + const MAX_COMPONENTS: i32 = 256; + + let mut added = Vec::new(); + let mut removed = Vec::new(); + + let added_length = read.get_var_int()?; + if added_length.0 < 0 || added_length.0 > MAX_COMPONENTS { + return Err(ReadingError::Message("added_length out of bounds".into())); + } + for _ in 0..added_length.0 { + let component_id = read.get_var_int()?; + let component_value = read.get_i32()?; + added.push((component_id, component_value)); + } + + let removed_length = read.get_var_int()?; + if removed_length.0 < 0 || removed_length.0 > MAX_COMPONENTS { + return Err(ReadingError::Message("removed_length out of bounds".into())); + } + for _ in 0..removed_length.0 { + let component_id = read.get_var_int()?; + removed.push(component_id); + } + + Ok(Self { added, removed }) + } + + pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + write.put_var_int(&VarInt::from(self.added.len() as i32))?; + for (id, val) in &self.added { + write.put_var_int(id)?; + write.put_i32(*val)?; + } + write.put_var_int(&VarInt::from(self.removed.len() as i32))?; + for id in &self.removed { + write.put_var_int(id)?; + } + Ok(()) + } +} + #[derive(Debug, Clone)] pub struct ItemStackHash { item_id: VarInt, @@ -353,7 +343,39 @@ pub struct ItemStackHash { components: ItemComponentHash, } +#[derive(Debug, Clone)] +pub struct OptionalItemStackHash(pub Option); + impl OptionalItemStackHash { + pub fn read(read: &mut impl NetworkReadExt) -> Result { + let is_some = read.get_bool()?; + if is_some { + let item_id = read.get_var_int()?; + let count = read.get_var_int()?; + let components = ItemComponentHash::read(read)?; + + Ok(Self(Some(ItemStackHash { + item_id, + count, + components, + }))) + } else { + Ok(Self(None)) + } + } + + pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + if let Some(hash) = &self.0 { + write.put_bool(true)?; + write.put_var_int(&hash.item_id)?; + write.put_var_int(&hash.count)?; + hash.components.write(write)?; + } else { + write.put_bool(false)?; + } + Ok(()) + } + #[must_use] pub fn hash_equals(&self, other: &ItemStack) -> bool { if let Some(hash) = &self.0 { @@ -404,121 +426,26 @@ impl OptionalItemStackHash { } } -#[derive(Debug, Clone)] -pub struct OptionalItemStackHash(pub Option); - -impl<'de> Deserialize<'de> for OptionalItemStackHash { - fn deserialize>(deserializer: D) -> Result { - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = OptionalItemStackHash; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid Slot encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - let is_some = seq - .next_element::()? - .ok_or(de::Error::custom("No is some bool!"))?; - if is_some { - let item_id = seq - .next_element::()? - .ok_or(de::Error::custom("No item id VarInt!"))?; - let count = seq - .next_element::()? - .ok_or(de::Error::custom("No item count VarInt!"))?; - - let hashed_components = seq - .next_element::()? - .ok_or(de::Error::custom("No item component hash!"))?; - - let item_stack_hash = ItemStackHash { - item_id, - count, - components: hashed_components, - }; - Ok(OptionalItemStackHash(Some(item_stack_hash))) - } else { - Ok(OptionalItemStackHash(None)) - } - } - } - - deserializer.deserialize_seq(Visitor) - } -} - -impl<'de> Deserialize<'de> for ItemComponentHash { - fn deserialize>(deserializer: D) -> Result { - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = ItemComponentHash; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid Slot encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - const MAX_COMPONENTS: i32 = 256; - - let mut added = Vec::new(); - let mut removed = Vec::new(); - - let added_length = seq - .next_element::()? - .ok_or(de::Error::custom("No added length VarInt!"))?; - if added_length.0 < 0 || added_length.0 > MAX_COMPONENTS { - return Err(de::Error::custom("added_length out of bounds")); - } - for _ in 0..added_length.0 { - let component_id = seq - .next_element::()? - .ok_or(de::Error::custom("No component id VarInt!"))?; - let component_value = seq - .next_element::()? - .ok_or(de::Error::custom("No component value i32!"))?; - added.push((component_id, component_value)); - } - - let removed_length = seq - .next_element::()? - .ok_or(de::Error::custom("No removed length VarInt!"))?; - if removed_length.0 < 0 || removed_length.0 > MAX_COMPONENTS { - return Err(de::Error::custom("removed_length out of bounds")); - } - for _ in 0..removed_length.0 { - let component_id = seq - .next_element::()? - .ok_or(de::Error::custom("No component id VarInt!"))?; - removed.push(component_id); - } - - Ok(ItemComponentHash { added, removed }) - } - } - - deserializer.deserialize_seq(Visitor) - } -} - pub struct ItemStackTemplateSerializer<'a>(pub Cow<'a, ItemStack>); impl ItemStackTemplateSerializer<'_> { pub fn write_with_version( &self, - write: impl std::io::Write, + write: &mut impl NetworkWriteExt, version: &JavaMinecraftVersion, ) -> Result<(), WritingError> { let remapped_item_id = remap_item_id_for_version(self.0.item.id, *version); - let mut network_serializer = serializer::Serializer::new(write); serialize_any_item_stack_with_id( self.0.as_ref(), remapped_item_id, *version >= JavaMinecraftVersion::V_26_1, - &mut network_serializer, + write, ) } + + pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> { + serialize_any_item_stack_with_id(self.0.as_ref(), self.0.item.id, true, write) + } } impl From for ItemStackTemplateSerializer<'_> { @@ -526,12 +453,3 @@ impl From for ItemStackTemplateSerializer<'_> { ItemStackTemplateSerializer(Cow::Owned(item)) } } - -impl Serialize for ItemStackTemplateSerializer<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serialize_any_item_stack_with_id(self.0.as_ref(), self.0.item.id, true, serializer) - } -} diff --git a/pumpkin-protocol/src/codec/little_endian.rs b/pumpkin-protocol/src/codec/little_endian.rs index d54358159..278bfa929 100644 --- a/pumpkin-protocol/src/codec/little_endian.rs +++ b/pumpkin-protocol/src/codec/little_endian.rs @@ -1,51 +1,65 @@ -use serde::{Serialize, Serializer}; - pub struct Le64(pub i64); -impl Serialize for Le64 { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0.to_le_bytes()) +impl Le64 { + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.0.to_le_bytes()) } } pub struct Le32(pub i32); -impl Serialize for Le32 { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0.to_le_bytes()) +impl Le32 { + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.0.to_le_bytes()) } } pub struct Le16(pub i16); -impl Serialize for Le16 { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0.to_le_bytes()) +impl Le16 { + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.0.to_le_bytes()) } } -// Unsigned - pub struct LeU64(pub u64); -impl Serialize for LeU64 { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0.to_le_bytes()) +impl LeU64 { + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.0.to_le_bytes()) } } pub struct LeU32(pub u32); -impl Serialize for LeU32 { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0.to_le_bytes()) +impl LeU32 { + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.0.to_le_bytes()) } } pub struct LeU16(pub u16); -impl Serialize for LeU16 { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0.to_le_bytes()) +impl LeU16 { + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.0.to_le_bytes()) } } diff --git a/pumpkin-protocol/src/codec/lp_vector_3d.rs b/pumpkin-protocol/src/codec/lp_vector_3d.rs index d8768c300..272bdc595 100644 --- a/pumpkin-protocol/src/codec/lp_vector_3d.rs +++ b/pumpkin-protocol/src/codec/lp_vector_3d.rs @@ -1,16 +1,9 @@ -use std::fmt::Formatter; - use pumpkin_util::math::vector3::Vector3; use crate::{ VarInt, ser::{NetworkWriteExt, ReadingError, WritingError}, }; -use serde::{ - Deserializer, Serialize, - de::{self, Visitor}, - ser::Serializer, -}; #[derive(Clone, Copy)] pub struct LpVector3d(pub Vector3); @@ -153,42 +146,6 @@ fn from_long(quantized: i64, scale: f64) -> f64 { (normalized / 0.5) * scale } -impl Serialize for LpVector3d { - fn serialize(&self, serializer: S) -> Result { - let mut buf = Vec::new(); - self.write(&mut buf) - .map_err(|e| serde::ser::Error::custom(e.to_string()))?; - serializer.serialize_bytes(&buf) - } -} - -impl<'de> de::Deserialize<'de> for LpVector3d { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct VelocityVisitor; - - impl Visitor<'_> for VelocityVisitor { - type Value = LpVector3d; - - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - formatter.write_str("a byte array representing bit-packed velocity") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: de::Error, - { - let mut cursor = std::io::Cursor::new(v); - LpVector3d::read(&mut cursor).map_err(de::Error::custom) - } - } - - deserializer.deserialize_bytes(VelocityVisitor) - } -} - #[cfg(test)] mod tests { use super::{LpVector3d, encode_legacy_velocity_component}; diff --git a/pumpkin-protocol/src/codec/optional_int.rs b/pumpkin-protocol/src/codec/optional_int.rs index 8bef68636..d0c63382b 100644 --- a/pumpkin-protocol/src/codec/optional_int.rs +++ b/pumpkin-protocol/src/codec/optional_int.rs @@ -1,14 +1,11 @@ -use serde::{Serialize, Serializer}; - -use crate::codec::var_int::VarInt; - -#[derive(Debug, Clone, Copy)] pub struct OptionalInt(pub Option); -impl Serialize for OptionalInt { - fn serialize(&self, serializer: S) -> Result { - // Vanilla logic: empty is 0, otherwise value + 1 +impl OptionalInt { + pub fn write( + &self, + write: &mut impl crate::ser::NetworkWriteExt, + ) -> Result<(), crate::ser::WritingError> { let val = self.0.map_or(0, |id| id + 1); - VarInt(val).serialize(serializer) + write.write_var_int(&crate::codec::var_int::VarInt(val)) } } diff --git a/pumpkin-protocol/src/codec/recipe.rs b/pumpkin-protocol/src/codec/recipe.rs index d38f3799a..3aeea46c5 100644 --- a/pumpkin-protocol/src/codec/recipe.rs +++ b/pumpkin-protocol/src/codec/recipe.rs @@ -1,10 +1,9 @@ use pumpkin_data::recipes::RecipeCategoryTypes; -use serde::Serialize; use pumpkin_data::item::Item; use pumpkin_data::tag::Taggable; -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug)] pub enum OwnedRecipeIngredient { Simple(String), Tagged(String), @@ -30,14 +29,14 @@ impl OwnedRecipeIngredient { } } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug)] pub struct OwnedRecipeResult { pub item_id: String, pub count: u8, // TODO: Add components/enchantments if needed for the display result } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug)] pub enum OwnedCraftingRecipe { Shaped { category: RecipeCategoryTypes, @@ -55,7 +54,7 @@ pub enum OwnedCraftingRecipe { }, } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug)] pub struct OwnedCookingRecipe { pub recipe_id: String, pub category: RecipeCategoryTypes, @@ -66,7 +65,7 @@ pub struct OwnedCookingRecipe { pub result: OwnedRecipeResult, } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug)] pub enum OwnedCookingRecipeType { Blasting(OwnedCookingRecipe), Smelting(OwnedCookingRecipe), @@ -74,7 +73,7 @@ pub enum OwnedCookingRecipeType { CampfireCooking(OwnedCookingRecipe), } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug)] pub enum DynamicRecipe { Crafting(OwnedCraftingRecipe), Cooking(OwnedCookingRecipeType), diff --git a/pumpkin-protocol/src/codec/var_int.rs b/pumpkin-protocol/src/codec/var_int.rs index 081db4f2c..60de15508 100644 --- a/pumpkin-protocol/src/codec/var_int.rs +++ b/pumpkin-protocol/src/codec/var_int.rs @@ -4,11 +4,6 @@ use std::{ ops::Deref, }; -use bytes::BufMut; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{SeqAccess, Visitor}, -}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use crate::{ @@ -153,53 +148,6 @@ impl Deref for VarInt { } } -impl Serialize for VarInt { - fn serialize(&self, serializer: S) -> Result { - let mut value = self.0 as u32; - let mut buf = Vec::new(); - - while value > 0x7F { - buf.put_u8(value as u8 | 0x80); - value >>= 7; - } - - buf.put_u8(value as u8); - - serializer.serialize_bytes(&buf) - } -} - -impl<'de> Deserialize<'de> for VarInt { - fn deserialize>(deserializer: D) -> Result { - struct VarIntVisitor; - - impl<'de> Visitor<'de> for VarIntVisitor { - type Value = VarInt; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - let mut val = 0; - for i in 0..VarInt::MAX_SIZE.get() { - if let Some(byte) = seq.next_element::()? { - val |= (i32::from(byte) & 0b0111_1111) << (i * 7); - if byte & 0b1000_0000 == 0 { - return Ok(VarInt(val)); - } - } else { - break; - } - } - Err(serde::de::Error::custom("VarInt was too large")) - } - } - - deserializer.deserialize_seq(VarIntVisitor) - } -} - impl PacketWrite for VarInt { fn write(&self, writer: &mut W) -> Result<(), Error> { let mut val = ((self.0 << 1) ^ (self.0 >> 31)) as u32; diff --git a/pumpkin-protocol/src/codec/var_long.rs b/pumpkin-protocol/src/codec/var_long.rs index 066c3a643..abec729c9 100644 --- a/pumpkin-protocol/src/codec/var_long.rs +++ b/pumpkin-protocol/src/codec/var_long.rs @@ -4,11 +4,6 @@ use std::{ ops::Deref, }; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{self, SeqAccess, Visitor}, -}; - use crate::{ WritingError, ser::{NetworkReadExt, NetworkWriteExt, ReadingError}, @@ -92,53 +87,6 @@ impl Deref for VarLong { } } -impl Serialize for VarLong { - fn serialize(&self, serializer: S) -> Result { - let mut value = self.0 as u64; - let mut buf = Vec::new(); - - while value > 0x7F { - buf.push(value as u8 | 0x80); - value >>= 7; - } - - buf.push(value as u8); - - serializer.serialize_bytes(&buf) - } -} - -impl<'de> Deserialize<'de> for VarLong { - fn deserialize>(deserializer: D) -> Result { - struct VarLongVisitor; - - impl<'de> Visitor<'de> for VarLongVisitor { - type Value = VarLong; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - let mut val = 0; - for i in 0..VarLong::MAX_SIZE.get() { - if let Some(byte) = seq.next_element::()? { - val |= (i64::from(byte) & 0b0111_1111) << (i * 7); - if byte & 0b1000_0000 == 0 { - return Ok(VarLong(val)); - } - } else { - break; - } - } - Err(de::Error::custom("VarInt was too large")) - } - } - - deserializer.deserialize_seq(VarLongVisitor) - } -} - impl PacketRead for VarLong { fn read(reader: &mut R) -> Result { let mut val: u64 = 0; diff --git a/pumpkin-protocol/src/codec/var_uint.rs b/pumpkin-protocol/src/codec/var_uint.rs index c99c844be..9f57ec455 100644 --- a/pumpkin-protocol/src/codec/var_uint.rs +++ b/pumpkin-protocol/src/codec/var_uint.rs @@ -3,11 +3,6 @@ use std::{ num::NonZeroUsize, }; -use bytes::BufMut; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{SeqAccess, Visitor}, -}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use crate::{ @@ -136,53 +131,6 @@ gen_from!(u64); gen_from!(isize); gen_from!(usize); -impl Serialize for VarUInt { - fn serialize(&self, serializer: S) -> Result { - let mut value = self.0; - let mut buf = Vec::with_capacity(5); - - while value > 0x7F { - buf.put_u8(value as u8 | 0x80); - value >>= 7; - } - - buf.put_u8(value as u8); - - serializer.serialize_bytes(&buf) - } -} - -impl<'de> Deserialize<'de> for VarUInt { - fn deserialize>(deserializer: D) -> Result { - struct VarIntVisitor; - - impl<'de> Visitor<'de> for VarIntVisitor { - type Value = VarUInt; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - let mut val = 0; - for i in 0..VarUInt::MAX_SIZE.get() { - if let Some(byte) = seq.next_element::()? { - val |= (u32::from(byte) & 0b0111_1111) << (i * 7); - if byte & 0b1000_0000 == 0 { - return Ok(VarUInt(val)); - } - } else { - break; - } - } - Err(serde::de::Error::custom("VarInt was too large")) - } - } - - deserializer.deserialize_seq(VarIntVisitor) - } -} - impl PacketWrite for VarUInt { fn write(&self, writer: &mut W) -> Result<(), Error> { let mut val = self.0; diff --git a/pumpkin-protocol/src/codec/var_ulong.rs b/pumpkin-protocol/src/codec/var_ulong.rs index ed801e4ac..6bbd659c2 100644 --- a/pumpkin-protocol/src/codec/var_ulong.rs +++ b/pumpkin-protocol/src/codec/var_ulong.rs @@ -4,11 +4,6 @@ use std::{ ops::Deref, }; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{self, SeqAccess, Visitor}, -}; - use crate::{ WritingError, ser::{NetworkReadExt, NetworkWriteExt, ReadingError}, @@ -115,53 +110,6 @@ impl Deref for VarULong { } } -impl Serialize for VarULong { - fn serialize(&self, serializer: S) -> Result { - let mut value = self.0; - let mut buf = Vec::new(); - - while value > 0x7F { - buf.push(value as u8 | 0x80); - value >>= 7; - } - - buf.push(value as u8); - - serializer.serialize_bytes(&buf) - } -} - -impl<'de> Deserialize<'de> for VarULong { - fn deserialize>(deserializer: D) -> Result { - struct VarLongVisitor; - - impl<'de> Visitor<'de> for VarLongVisitor { - type Value = VarULong; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - let mut val = 0; - for i in 0..VarULong::MAX_SIZE.get() { - if let Some(byte) = seq.next_element::()? { - val |= (u64::from(byte) & 0b0111_1111) << (i * 7); - if byte & 0b1000_0000 == 0 { - return Ok(VarULong(val)); - } - } else { - break; - } - } - Err(de::Error::custom("VarInt was too large")) - } - } - - deserializer.deserialize_seq(VarLongVisitor) - } -} - impl PacketWrite for VarULong { fn write(&self, writer: &mut W) -> Result<(), Error> { let mut x = self.0; diff --git a/pumpkin-protocol/src/java/client/config/add_resource_pack.rs b/pumpkin-protocol/src/java/client/config/add_resource_pack.rs index 9afa28365..960f53b97 100644 --- a/pumpkin-protocol/src/java/client/config/add_resource_pack.rs +++ b/pumpkin-protocol/src/java/client/config/add_resource_pack.rs @@ -1,14 +1,15 @@ use pumpkin_util::text::TextComponent; use pumpkin_macros::java_packet; -use serde::Serialize; use pumpkin_data::packet::clientbound::CONFIG_RESOURCE_PACK_PUSH; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_RESOURCE_PACK_PUSH)] pub struct CConfigAddResourcePack<'a> { - #[serde(with = "uuid::serde::compact")] pub uuid: &'a uuid::Uuid, pub url: &'a str, pub hash: &'a str, // max 40 @@ -34,3 +35,23 @@ impl<'a> CConfigAddResourcePack<'a> { } } } + +impl ClientPacket for CConfigAddResourcePack<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_uuid(self.uuid)?; + write.write_string(self.url)?; + write.write_string(self.hash)?; + write.write_bool(self.forced)?; + if let Some(_prompt) = &self.prompt_message { + write.write_bool(true)?; + // TODO + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/clear_dialog.rs b/pumpkin-protocol/src/java/client/config/clear_dialog.rs index 044a3009b..e34fb6eec 100644 --- a/pumpkin-protocol/src/java/client/config/clear_dialog.rs +++ b/pumpkin-protocol/src/java/client/config/clear_dialog.rs @@ -1,8 +1,9 @@ use pumpkin_data::packet::clientbound::CONFIG_CLEAR_DIALOG; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_CLEAR_DIALOG)] pub struct CConfigClearDialog; @@ -18,3 +19,13 @@ impl Default for CConfigClearDialog { Self::new() } } + +impl ClientPacket for CConfigClearDialog { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/config_disconnect.rs b/pumpkin-protocol/src/java/client/config/config_disconnect.rs index 4b7e4531c..44531e2f8 100644 --- a/pumpkin-protocol/src/java/client/config/config_disconnect.rs +++ b/pumpkin-protocol/src/java/client/config/config_disconnect.rs @@ -1,8 +1,10 @@ use pumpkin_data::packet::clientbound::CONFIG_DISCONNECT; use pumpkin_macros::java_packet; -use serde::Deserialize; -#[derive(serde::Serialize, Deserialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_DISCONNECT)] pub struct CConfigDisconnect<'a> { pub reason: &'a str, @@ -14,3 +16,14 @@ impl<'a> CConfigDisconnect<'a> { Self { reason } } } + +impl ClientPacket for CConfigDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.reason)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/cookie_request.rs b/pumpkin-protocol/src/java/client/config/cookie_request.rs index f39de054e..9f19b3d2c 100644 --- a/pumpkin-protocol/src/java/client/config/cookie_request.rs +++ b/pumpkin-protocol/src/java/client/config/cookie_request.rs @@ -2,7 +2,10 @@ use pumpkin_data::packet::clientbound::CONFIG_COOKIE_REQUEST; use pumpkin_macros::java_packet; use pumpkin_util::resource_location::ResourceLocation; -#[derive(serde::Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_COOKIE_REQUEST)] /// Requests a cookie that was previously stored. pub struct CCookieRequest<'a> { @@ -15,3 +18,14 @@ impl<'a> CCookieRequest<'a> { Self { key } } } + +impl ClientPacket for CCookieRequest<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.key)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/finish_config.rs b/pumpkin-protocol/src/java/client/config/finish_config.rs index da2e69f01..2a0893679 100644 --- a/pumpkin-protocol/src/java/client/config/finish_config.rs +++ b/pumpkin-protocol/src/java/client/config/finish_config.rs @@ -1,7 +1,18 @@ use pumpkin_data::packet::clientbound::CONFIG_FINISH_CONFIGURATION; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_FINISH_CONFIGURATION)] pub struct CFinishConfig; + +impl ClientPacket for CFinishConfig { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/known_packs.rs b/pumpkin-protocol/src/java/client/config/known_packs.rs index af8f6483e..de4c2756b 100644 --- a/pumpkin-protocol/src/java/client/config/known_packs.rs +++ b/pumpkin-protocol/src/java/client/config/known_packs.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::CONFIG_SELECT_KNOWN_PACKS; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::KnownPack; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(CONFIG_SELECT_KNOWN_PACKS)] pub struct CKnownPacks<'a> { pub known_packs: &'a [KnownPack<'a>], @@ -16,3 +17,17 @@ impl<'a> CKnownPacks<'a> { Self { known_packs } } } + +impl ClientPacket for CKnownPacks<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&crate::VarInt(self.known_packs.len() as i32))?; + for pack in self.known_packs { + pack.write(&mut write)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/server_links.rs b/pumpkin-protocol/src/java/client/config/server_links.rs index 13ecc8259..f7b2ae454 100644 --- a/pumpkin-protocol/src/java/client/config/server_links.rs +++ b/pumpkin-protocol/src/java/client/config/server_links.rs @@ -1,9 +1,11 @@ use crate::Link; use pumpkin_data::packet::clientbound::CONFIG_SERVER_LINKS; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_SERVER_LINKS)] pub struct CConfigServerLinks<'a> { pub links: &'a [Link<'a>], @@ -15,3 +17,17 @@ impl<'a> CConfigServerLinks<'a> { Self { links } } } + +impl ClientPacket for CConfigServerLinks<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&crate::VarInt(self.links.len() as i32))?; + for link in self.links { + link.write(&mut write)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/show_dialog.rs b/pumpkin-protocol/src/java/client/config/show_dialog.rs index dfe0a8a52..cd9a07f12 100644 --- a/pumpkin-protocol/src/java/client/config/show_dialog.rs +++ b/pumpkin-protocol/src/java/client/config/show_dialog.rs @@ -2,9 +2,10 @@ use crate::IdOr; use crate::java::client::dialog::DialogNBT; use pumpkin_data::packet::clientbound::CONFIG_SHOW_DIALOG; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_SHOW_DIALOG)] pub struct CConfigShowDialog<'a> { pub dialog: IdOr>, @@ -16,3 +17,15 @@ impl<'a> CConfigShowDialog<'a> { Self { dialog } } } + +impl ClientPacket for CConfigShowDialog<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + self.dialog + .write(&mut write, |w, dialog| dialog.write_packet_data(w, version))?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/store_cookie.rs b/pumpkin-protocol/src/java/client/config/store_cookie.rs index 11a6791aa..863cede41 100644 --- a/pumpkin-protocol/src/java/client/config/store_cookie.rs +++ b/pumpkin-protocol/src/java/client/config/store_cookie.rs @@ -2,7 +2,10 @@ use pumpkin_data::packet::clientbound::CONFIG_STORE_COOKIE; use pumpkin_macros::java_packet; use pumpkin_util::resource_location::ResourceLocation; -#[derive(serde::Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_STORE_COOKIE)] /// Stores some arbitrary data on the client, which persists between server transfers. /// The Notchian (vanilla) client only accepts cookies of up to 5 KiB in size. @@ -17,3 +20,16 @@ impl<'a> CStoreCookie<'a> { Self { key, payload } } } + +impl ClientPacket for CStoreCookie<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.key)?; + write.write_var_int(&crate::VarInt(self.payload.len() as i32))?; + write.write_all(self.payload)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/config/transfer.rs b/pumpkin-protocol/src/java/client/config/transfer.rs index 2e0ba0a8d..78435d282 100644 --- a/pumpkin-protocol/src/java/client/config/transfer.rs +++ b/pumpkin-protocol/src/java/client/config/transfer.rs @@ -1,9 +1,11 @@ use crate::VarInt; use pumpkin_data::packet::clientbound::CONFIG_TRANSFER; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(CONFIG_TRANSFER)] pub struct CTransfer<'a> { pub host: &'a str, @@ -16,3 +18,15 @@ impl<'a> CTransfer<'a> { Self { host, port } } } + +impl ClientPacket for CTransfer<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.host)?; + write.write_var_int(self.port)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/dialog.rs b/pumpkin-protocol/src/java/client/dialog.rs index 3166a95d1..77f8f8423 100644 --- a/pumpkin-protocol/src/java/client/dialog.rs +++ b/pumpkin-protocol/src/java/client/dialog.rs @@ -1,7 +1,8 @@ use pumpkin_util::text::TextComponent; -use serde::Serialize; -#[derive(Serialize)] +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + pub struct DialogNBT<'a>(pub DialogNBTSource<'a>); impl<'a> DialogNBT<'a> { @@ -14,70 +15,67 @@ impl<'a> DialogNBT<'a> { pub const fn from_nbt(compound: &'a pumpkin_nbt::compound::NbtCompound) -> Self { Self(DialogNBTSource::Nbt(compound)) } + + #[allow(clippy::todo)] + pub fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + match &self.0 { + DialogNBTSource::Struct(_dialog) => { + todo!("Serialize Dialog to NbtCompound manually") + } + DialogNBTSource::Nbt(nbt) => { + let tag = pumpkin_nbt::tag::NbtTag::Compound((*nbt).clone()); + write.write_nbt(tag)?; + Ok(()) + } + } + } } -#[derive(Serialize)] -#[serde(untagged)] pub enum DialogNBTSource<'a> { Struct(&'a Dialog), Nbt(&'a pumpkin_nbt::compound::NbtCompound), } -#[derive(Serialize)] pub struct Dialog { pub r#type: String, pub title: TextComponent, - #[serde(skip_serializing_if = "Vec::is_empty")] pub body: Vec, - #[serde(skip_serializing_if = "Vec::is_empty")] pub inputs: Vec, - #[serde(skip_serializing_if = "Vec::is_empty")] pub buttons: Vec, - #[serde(skip_serializing_if = "Vec::is_empty")] pub links: Vec, - #[serde(skip_serializing_if = "Option::is_none")] pub exit_action: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub after_action: Option, pub can_close_with_escape: bool, - #[serde(skip_serializing_if = "Option::is_none")] pub external_title: Option, } -#[derive(Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] pub enum DialogBody { - #[serde(rename = "minecraft:plain_message")] PlainMessage { contents: TextComponent }, - #[serde(rename = "minecraft:item")] Item { item: i32 }, // TODO: ItemStack serialization to NBT } -#[derive(Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] pub enum DialogInput { - #[serde(rename = "minecraft:boolean")] Boolean { label: TextComponent, default_value: bool, }, - #[serde(rename = "minecraft:text")] Text { label: TextComponent, placeholder: TextComponent, default_value: String, }, - #[serde(rename = "minecraft:number_range")] NumberRange { label: TextComponent, min: f32, max: f32, initial: f32, step: f32, - #[serde(skip_serializing_if = "Option::is_none")] label_format: Option, }, - #[serde(rename = "minecraft:single_option")] SingleOption { label: TextComponent, options: Vec, @@ -85,30 +83,23 @@ pub enum DialogInput { }, } -#[derive(Serialize)] pub struct ActionButton { pub text: TextComponent, - #[serde(skip_serializing_if = "Option::is_none")] pub tooltip: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub width: Option, pub action: DialogAction, } -#[derive(Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] pub enum DialogAction { - #[serde(rename = "minecraft:open_url")] - OpenUrl { url: String }, - #[serde(rename = "minecraft:custom")] + OpenUrl { + url: String, + }, Custom { id: String, - #[serde(skip_serializing_if = "Option::is_none")] payload: Option>, }, } -#[derive(Serialize)] pub struct DialogLink { pub label: crate::Label, pub url: String, diff --git a/pumpkin-protocol/src/java/client/login/cookie_request.rs b/pumpkin-protocol/src/java/client/login/cookie_request.rs index 0ea4e0adf..3cbba6c40 100644 --- a/pumpkin-protocol/src/java/client/login/cookie_request.rs +++ b/pumpkin-protocol/src/java/client/login/cookie_request.rs @@ -1,13 +1,15 @@ use pumpkin_data::packet::clientbound::LOGIN_COOKIE_REQUEST; use pumpkin_macros::java_packet; use pumpkin_util::resource_location::ResourceLocation; -use serde::Serialize; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to retrieve a previously stored cookie from the client. /// /// This occurs during the login phase, allowing the server to identify /// returning players or retrieve session data stored during a previous visit. -#[derive(Serialize)] #[java_packet(LOGIN_COOKIE_REQUEST)] pub struct CLoginCookieRequest<'a> { /// The unique identifier of the cookie being requested. @@ -20,3 +22,14 @@ impl<'a> CLoginCookieRequest<'a> { Self { key } } } + +impl ClientPacket for CLoginCookieRequest<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.key)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/login/encryption_request.rs b/pumpkin-protocol/src/java/client/login/encryption_request.rs index 4af8401bd..90a0380ca 100644 --- a/pumpkin-protocol/src/java/client/login/encryption_request.rs +++ b/pumpkin-protocol/src/java/client/login/encryption_request.rs @@ -1,13 +1,15 @@ use pumpkin_data::packet::clientbound::LOGIN_HELLO; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to initiate the encryption handshake. /// /// This packet provides the client with the server's public key and a /// verification token, allowing the client to generate a shared secret /// for secure communication. -#[derive(Serialize, Deserialize)] #[java_packet(LOGIN_HELLO)] pub struct CEncryptionRequest<'a> { /// The server's ID string. In modern Minecraft, this is usually @@ -39,3 +41,19 @@ impl<'a> CEncryptionRequest<'a> { } } } + +impl ClientPacket for CEncryptionRequest<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.server_id)?; + write.write_var_int(&crate::VarInt(self.public_key.len() as i32))?; + write.write_all(self.public_key)?; + write.write_var_int(&crate::VarInt(self.verify_token.len() as i32))?; + write.write_all(self.verify_token)?; + write.write_bool(self.should_authenticate)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/login/login_disconnect.rs b/pumpkin-protocol/src/java/client/login/login_disconnect.rs index cd0b12ba3..484f81a12 100644 --- a/pumpkin-protocol/src/java/client/login/login_disconnect.rs +++ b/pumpkin-protocol/src/java/client/login/login_disconnect.rs @@ -1,12 +1,14 @@ use pumpkin_data::packet::clientbound::LOGIN_LOGIN_DISCONNECT; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to reject a login attempt or kick a player during the login phase /// /// This is used for reasons such as the server being full, the player being banned, /// or version mismatches. After this packet is sent, the connection is closed. -#[derive(Serialize, Deserialize)] #[java_packet(LOGIN_LOGIN_DISCONNECT)] pub struct CLoginDisconnect { /// A JSON-encoded chat component explaining why the player was disconnected. @@ -19,3 +21,14 @@ impl CLoginDisconnect { Self { json_reason } } } + +impl ClientPacket for CLoginDisconnect { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(&self.json_reason)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/login/login_success.rs b/pumpkin-protocol/src/java/client/login/login_success.rs index 89fbb3432..67fa2b289 100644 --- a/pumpkin-protocol/src/java/client/login/login_success.rs +++ b/pumpkin-protocol/src/java/client/login/login_success.rs @@ -1,6 +1,5 @@ use pumpkin_data::packet::clientbound::{LOGIN_GAME_PROFILE, LOGIN_LOGIN_FINISHED}; use pumpkin_util::version::JavaMinecraftVersion; -use serde::Serialize; use crate::{ClientPacket, Property, packet::MultiVersionJavaPacket, ser::NetworkWriteExt}; @@ -60,10 +59,7 @@ impl ClientPacket for CLoginSuccess<'_> { ) -> Result<(), crate::ser::WritingError> { write.write_uuid(self.uuid)?; write.write_string(self.username)?; - write.write_list(self.properties, |write, property| { - let mut serializer = crate::ser::serializer::Serializer::new(write); - property.serialize(&mut serializer) - })?; + write.write_list(self.properties, |write, property| property.write(write))?; if version < &JavaMinecraftVersion::V_1_21_2 { write.write_bool(self.strict_error_handling)?; } else if version >= &JavaMinecraftVersion::V_26_2 { diff --git a/pumpkin-protocol/src/java/client/login/set_compression.rs b/pumpkin-protocol/src/java/client/login/set_compression.rs index 6f15f6162..34715974f 100644 --- a/pumpkin-protocol/src/java/client/login/set_compression.rs +++ b/pumpkin-protocol/src/java/client/login/set_compression.rs @@ -1,14 +1,15 @@ use pumpkin_data::packet::clientbound::LOGIN_LOGIN_COMPRESSION; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to enable network compression for all subsequent packets. /// /// Once this packet is sent, both the server and the client must compress any /// packet with a size equal to or greater than the specified threshold. -#[derive(Serialize, Deserialize)] #[java_packet(LOGIN_LOGIN_COMPRESSION)] pub struct CSetCompression { /// The packet size threshold (in bytes) at which compression is applied. @@ -24,3 +25,14 @@ impl CSetCompression { Self { threshold } } } + +impl ClientPacket for CSetCompression { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.threshold)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/acknowledge_block.rs b/pumpkin-protocol/src/java/client/play/acknowledge_block.rs index c847026ec..cedb40159 100644 --- a/pumpkin-protocol/src/java/client/play/acknowledge_block.rs +++ b/pumpkin-protocol/src/java/client/play/acknowledge_block.rs @@ -1,14 +1,13 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_BLOCK_CHANGED_ACK; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; - -use crate::VarInt; - +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to acknowledge a sequence of block changes initiated by the client. /// /// This packet is critical for preventing "ghost blocks" and synchronization issues. /// It tells the client that the server has processed all actions up to a specific point. -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_BLOCK_CHANGED_ACK)] pub struct CAcknowledgeBlockChange { /// The ID of the last sequence processed by the server. @@ -25,3 +24,14 @@ impl CAcknowledgeBlockChange { Self { sequence_id } } } + +impl ClientPacket for CAcknowledgeBlockChange { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.sequence_id)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/actionbar.rs b/pumpkin-protocol/src/java/client/play/actionbar.rs index fcb7404db..efa61c90c 100644 --- a/pumpkin-protocol/src/java/client/play/actionbar.rs +++ b/pumpkin-protocol/src/java/client/play/actionbar.rs @@ -1,15 +1,15 @@ use pumpkin_data::packet::clientbound::PLAY_SET_ACTION_BAR_TEXT; use pumpkin_util::text::TextComponent; +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use pumpkin_macros::java_packet; -use serde::Serialize; - +use pumpkin_util::version::JavaMinecraftVersion; /// Updates the text displayed above the player's hotbar (the Action Bar). /// /// Unlike chat messages, Action Bar text is transient and generally used for /// non-critical status information like "Now entering: Wilderness" or /// mana/stamina counters. -#[derive(Serialize)] #[java_packet(PLAY_SET_ACTION_BAR_TEXT)] pub struct CActionBar<'a> { /// The text component to be displayed. @@ -22,3 +22,14 @@ impl<'a> CActionBar<'a> { Self { action_bar } } } + +impl ClientPacket for CActionBar<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.action_bar.encode())?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/award_stats.rs b/pumpkin-protocol/src/java/client/play/award_stats.rs index 739f178a2..853a4035e 100644 --- a/pumpkin-protocol/src/java/client/play/award_stats.rs +++ b/pumpkin-protocol/src/java/client/play/award_stats.rs @@ -1,17 +1,41 @@ use pumpkin_data::packet::clientbound::PLAY_AWARD_STATS; use pumpkin_macros::java_packet; +use crate::ClientPacket; use crate::codec::var_int::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(serde::Serialize)] #[java_packet(PLAY_AWARD_STATS)] pub struct CAwardStats<'a> { pub stats: &'a [Statistic], } -#[derive(serde::Serialize)] pub struct Statistic { pub category_id: VarInt, pub statistic_id: VarInt, pub value: VarInt, } + +impl Statistic { + pub fn write(&self, mut write: impl std::io::Write) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.category_id)?; + write.write_var_int(&self.statistic_id)?; + write.write_var_int(&self.value)?; + Ok(()) + } +} + +impl ClientPacket for CAwardStats<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&VarInt(self.stats.len() as i32))?; + for stat in self.stats { + stat.write(&mut write)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs b/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs index e3059b52a..2d3111059 100644 --- a/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs +++ b/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs @@ -1,16 +1,16 @@ use pumpkin_data::packet::clientbound::PLAY_BLOCK_DESTRUCTION; use pumpkin_util::math::position::BlockPos; -use pumpkin_macros::java_packet; -use serde::Serialize; - +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_macros::java_packet; +use pumpkin_util::version::JavaMinecraftVersion; /// Updates the visual "breaking" progress of a block for all clients. /// /// This packet controls the overlay of cracks that appear on a block when /// it is being mined. It is often used to show other players' mining progress. -#[derive(Serialize)] #[java_packet(PLAY_BLOCK_DESTRUCTION)] pub struct CSetBlockDestroyStage { /// A unique ID for this destruction instance. Usually the miner's Entity ID. @@ -33,3 +33,16 @@ impl CSetBlockDestroyStage { } } } + +impl ClientPacket for CSetBlockDestroyStage { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_block_pos(&self.location)?; + write.write_i8(self.destroy_stage)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/block_event.rs b/pumpkin-protocol/src/java/client/play/block_event.rs index d4fab5df8..be9af9581 100644 --- a/pumpkin-protocol/src/java/client/play/block_event.rs +++ b/pumpkin-protocol/src/java/client/play/block_event.rs @@ -1,16 +1,16 @@ use pumpkin_data::packet::clientbound::PLAY_BLOCK_EVENT; use pumpkin_util::math::position::BlockPos; -use pumpkin_macros::java_packet; -use serde::Serialize; - +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_macros::java_packet; +use pumpkin_util::version::JavaMinecraftVersion; /// Triggers a physical block animation or sound effect. /// /// This is used for simple block interactions that don't necessarily change /// NBT data, such as chests opening/closing, pistons extending, or note blocks playing. -#[derive(Serialize)] #[java_packet(PLAY_BLOCK_EVENT)] pub struct CBlockEvent { /// The coordinates where the event occurs. @@ -40,3 +40,17 @@ impl CBlockEvent { } } } + +impl ClientPacket for CBlockEvent { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_block_pos(&self.location)?; + write.write_u8(self.action_id)?; + write.write_u8(self.action_parameter)?; + write.write_var_int(&self.block_type)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/center_chunk.rs b/pumpkin-protocol/src/java/client/play/center_chunk.rs index efff6fb65..384d54284 100644 --- a/pumpkin-protocol/src/java/client/play/center_chunk.rs +++ b/pumpkin-protocol/src/java/client/play/center_chunk.rs @@ -1,15 +1,15 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_SET_CHUNK_CACHE_CENTER; use pumpkin_macros::java_packet; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Updates the center of the player's loaded chunk radius (the "view center"). /// /// This packet tells the client which chunk coordinate it should use as the /// focal point for loading and unloading chunks. It is typically sent when /// a player moves across a chunk boundary. -#[derive(Serialize)] #[java_packet(PLAY_SET_CHUNK_CACHE_CENTER)] pub struct CCenterChunk { /// The X coordinate of the center chunk. @@ -17,3 +17,15 @@ pub struct CCenterChunk { /// The Z coordinate of the center chunk. pub chunk_z: VarInt, } + +impl ClientPacket for CCenterChunk { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.chunk_x)?; + write.write_var_int(&self.chunk_z)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/change_difficulty.rs b/pumpkin-protocol/src/java/client/play/change_difficulty.rs index 580604561..73de70ec8 100644 --- a/pumpkin-protocol/src/java/client/play/change_difficulty.rs +++ b/pumpkin-protocol/src/java/client/play/change_difficulty.rs @@ -1,13 +1,14 @@ +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_CHANGE_DIFFICULTY; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; /// Notifies the client of a change in the world's difficulty level or lock status. /// /// This updates the client's internal state, which affects certain UI elements /// and client-side behavior (though actual game logic like mob damage is /// primarily handled by the server). -#[derive(Serialize)] #[java_packet(PLAY_CHANGE_DIFFICULTY)] pub struct CChangeDifficulty { /// The current difficulty level of the world. @@ -28,3 +29,15 @@ impl CChangeDifficulty { Self { difficulty, locked } } } + +impl ClientPacket for CChangeDifficulty { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_u8(self.difficulty)?; + write.write_bool(self.locked)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs b/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs index ed700798c..41ea0c632 100644 --- a/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs +++ b/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs @@ -1,15 +1,15 @@ +use crate::ClientPacket; +use crate::codec::var_int::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_CHUNK_BATCH_FINISHED; use pumpkin_macros::java_packet; -use serde::Serialize; - -use crate::codec::var_int::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Notifies the client that the server has finished sending a batch of chunks. /// /// Introduced in modern protocol versions to optimize world loading, this packet /// allows the client to acknowledge the receipt of a group of chunks, helping /// the server regulate the flow of data and prevent network congestion. -#[derive(Serialize)] #[java_packet(PLAY_CHUNK_BATCH_FINISHED)] pub struct CChunkBatchEnd { /// The number of chunks sent in the batch that just finished. @@ -24,3 +24,14 @@ impl CChunkBatchEnd { } } } + +impl ClientPacket for CChunkBatchEnd { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.batch_size)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs b/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs index d17722675..29aa42401 100644 --- a/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs +++ b/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs @@ -1,13 +1,22 @@ +use crate::ClientPacket; use pumpkin_data::packet::clientbound::PLAY_CHUNK_BATCH_START; use pumpkin_macros::java_packet; -use serde::Serialize; - +use pumpkin_util::version::JavaMinecraftVersion; /// Signals the beginning of a new batch of chunk data packets. /// /// This packet initiates a synchronized chunk loading sequence. In modern /// protocol versions, the server must wrap chunk data transmissions between /// a `Start` and `End` packet to manage client-side backpressure and /// network throughput. -#[derive(Serialize)] #[java_packet(PLAY_CHUNK_BATCH_START)] pub struct CChunkBatchStart; + +impl ClientPacket for CChunkBatchStart { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/clear_dialog.rs b/pumpkin-protocol/src/java/client/play/clear_dialog.rs index c5964a3fa..f4d7c682b 100644 --- a/pumpkin-protocol/src/java/client/play/clear_dialog.rs +++ b/pumpkin-protocol/src/java/client/play/clear_dialog.rs @@ -1,8 +1,7 @@ +use crate::ClientPacket; use pumpkin_data::packet::clientbound::PLAY_CLEAR_DIALOG; use pumpkin_macros::java_packet; -use serde::Serialize; - -#[derive(Serialize)] +use pumpkin_util::version::JavaMinecraftVersion; #[java_packet(PLAY_CLEAR_DIALOG)] pub struct CPlayClearDialog; @@ -18,3 +17,13 @@ impl Default for CPlayClearDialog { Self::new() } } + +impl ClientPacket for CPlayClearDialog { + fn write_packet_data( + &self, + _write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/clear_title.rs b/pumpkin-protocol/src/java/client/play/clear_title.rs index 2a2ecb9db..7eda482b8 100644 --- a/pumpkin-protocol/src/java/client/play/clear_title.rs +++ b/pumpkin-protocol/src/java/client/play/clear_title.rs @@ -1,12 +1,12 @@ +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_CLEAR_TITLES; use pumpkin_macros::java_packet; -use serde::Serialize; - +use pumpkin_util::version::JavaMinecraftVersion; /// Removes any currently displayed Title or Subtitle from the player's screen. /// /// This packet is used to immediately hide titles that are currently in their /// "stay" or "fade-out" phases. -#[derive(Serialize)] #[java_packet(PLAY_CLEAR_TITLES)] pub struct CClearTitle { /// If true, the client also resets the title timings (fade-in, stay, fade-out) @@ -23,3 +23,14 @@ impl CClearTitle { Self { reset } } } + +impl ClientPacket for CClearTitle { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_bool(self.reset)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/close_container.rs b/pumpkin-protocol/src/java/client/play/close_container.rs index 5d156b12e..ea05f8f16 100644 --- a/pumpkin-protocol/src/java/client/play/close_container.rs +++ b/pumpkin-protocol/src/java/client/play/close_container.rs @@ -1,15 +1,15 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_CONTAINER_CLOSE; use pumpkin_macros::java_packet; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Notifies the client that a container (inventory, chest, etc.) has been closed. /// /// This is used by the server to force the player's UI to shut, for example, /// if the player moves too far away from a chest or if an NPC's trade window /// is invalidated. -#[derive(Serialize)] #[java_packet(PLAY_CONTAINER_CLOSE)] pub struct CCloseContainer { /// The ID of the container window to close. @@ -24,3 +24,14 @@ impl CCloseContainer { Self { sync_id: window_id } } } + +impl ClientPacket for CCloseContainer { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.sync_id)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/combat_death.rs b/pumpkin-protocol/src/java/client/play/combat_death.rs index 10560bdd1..bc492fff1 100644 --- a/pumpkin-protocol/src/java/client/play/combat_death.rs +++ b/pumpkin-protocol/src/java/client/play/combat_death.rs @@ -1,15 +1,15 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_PLAYER_COMBAT_KILL; use pumpkin_macros::java_packet; use pumpkin_util::text::TextComponent; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Notifies the client that a player has died. /// /// This packet is responsible for triggering the death screen on the client /// and displaying the death message in the chat for the deceased player. -#[derive(Serialize)] #[java_packet(PLAY_PLAYER_COMBAT_KILL)] pub struct CCombatDeath<'a> { /// The Entity ID of the player who died. @@ -24,3 +24,15 @@ impl<'a> CCombatDeath<'a> { Self { player_id, message } } } + +impl ClientPacket for CCombatDeath<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.player_id)?; + write.write_slice(&self.message.encode())?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/command_suggestions.rs b/pumpkin-protocol/src/java/client/play/command_suggestions.rs index 365aea5e2..732cff0ca 100644 --- a/pumpkin-protocol/src/java/client/play/command_suggestions.rs +++ b/pumpkin-protocol/src/java/client/play/command_suggestions.rs @@ -1,16 +1,16 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_COMMAND_SUGGESTIONS; use pumpkin_macros::java_packet; use pumpkin_util::text::TextComponent; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to provide a list of "tab-completion" suggestions. /// /// This packet responds to a client's request for help with commands, /// appearing as a scrollable list of options while the player is typing /// in the chat bar. -#[derive(Serialize)] #[java_packet(PLAY_COMMAND_SUGGESTIONS)] pub struct CCommandSuggestions { /// The unique ID of the request this response is for. @@ -44,7 +44,7 @@ impl CCommandSuggestions { } } -#[derive(PartialEq, Eq, Hash, Debug, Serialize)] +#[derive(PartialEq, Eq, Hash, Debug)] pub struct CommandSuggestion { pub suggestion: String, pub tooltip: Option, @@ -58,4 +58,32 @@ impl CommandSuggestion { tooltip, } } + + pub fn write(&self, mut write: impl std::io::Write) -> Result<(), crate::ser::WritingError> { + write.write_string(&self.suggestion)?; + if let Some(tooltip) = &self.tooltip { + write.write_bool(true)?; + write.write_slice(&tooltip.encode())?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} + +impl ClientPacket for CCommandSuggestions { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.id)?; + write.write_var_int(&self.start)?; + write.write_var_int(&self.length)?; + write.write_var_int(&VarInt(self.matches.len() as i32))?; + for match_ in &self.matches { + match_.write(&mut write)?; + } + Ok(()) + } } diff --git a/pumpkin-protocol/src/java/client/play/cookie_request.rs b/pumpkin-protocol/src/java/client/play/cookie_request.rs index be945d9be..d001fccb3 100644 --- a/pumpkin-protocol/src/java/client/play/cookie_request.rs +++ b/pumpkin-protocol/src/java/client/play/cookie_request.rs @@ -1,14 +1,15 @@ +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_COOKIE_REQUEST; use pumpkin_macros::java_packet; use pumpkin_util::resource_location::ResourceLocation; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to request a "cookie" (stored data) from the client. /// /// Introduced in modern Minecraft versions, cookies allow servers to store /// small amounts of persistent data on the client side that can be retrieved /// even across different server instances or sub-servers in a network. -#[derive(Serialize)] #[java_packet(PLAY_COOKIE_REQUEST)] pub struct CPlayCookieRequest<'a> { /// The unique identifier (namespace:path) of the cookie to retrieve. @@ -21,3 +22,14 @@ impl<'a> CPlayCookieRequest<'a> { Self { key } } } + +impl ClientPacket for CPlayCookieRequest<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.key)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/damage_event.rs b/pumpkin-protocol/src/java/client/play/damage_event.rs index 537138211..1e4af5b23 100644 --- a/pumpkin-protocol/src/java/client/play/damage_event.rs +++ b/pumpkin-protocol/src/java/client/play/damage_event.rs @@ -1,9 +1,10 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_DAMAGE_EVENT; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Notifies the client that an entity has taken damage. /// @@ -11,7 +12,6 @@ use crate::VarInt; /// directional knockback visuals, and sound effects. It provides the client /// with specific details about the damage source to ensure the visual feedback /// matches the cause. -#[derive(Serialize)] #[java_packet(PLAY_DAMAGE_EVENT)] pub struct CDamageEvent { /// The Entity ID of the entity taking damage. @@ -48,3 +48,25 @@ impl CDamageEvent { } } } + +impl ClientPacket for CDamageEvent { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_var_int(&self.source_type_id)?; + write.write_var_int(&self.source_cause_id)?; + write.write_var_int(&self.source_direct_id)?; + if let Some(pos) = &self.source_position { + write.write_bool(true)?; + write.write_f64(pos.x)?; + write.write_f64(pos.y)?; + write.write_f64(pos.z)?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/disconnect.rs b/pumpkin-protocol/src/java/client/play/disconnect.rs index 8261d323a..c5ccb18df 100644 --- a/pumpkin-protocol/src/java/client/play/disconnect.rs +++ b/pumpkin-protocol/src/java/client/play/disconnect.rs @@ -1,15 +1,16 @@ use pumpkin_data::packet::clientbound::PLAY_DISCONNECT; use pumpkin_util::text::TextComponent; +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; /// Forces the client to disconnect from the server while in the "Play" state. /// /// This packet displays the provided reason to the player on a dedicated /// disconnection screen. It is used for kicks, server shutdowns, or when /// a player is banned. -#[derive(Serialize)] #[java_packet(PLAY_DISCONNECT)] pub struct CPlayDisconnect<'a> { /// The message shown to the player explaining why they were disconnected. @@ -23,3 +24,14 @@ impl<'a> CPlayDisconnect<'a> { Self { reason } } } + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.reason.encode())?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs b/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs index b2a7dca58..728385e80 100644 --- a/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs +++ b/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs @@ -1,17 +1,17 @@ use pumpkin_data::packet::clientbound::PLAY_DISGUISED_CHAT; +use pumpkin_macros::java_packet; use pumpkin_util::text::TextComponent; -use pumpkin_macros::java_packet; -use serde::Serialize; - +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sends a chat message that is not cryptographically signed by a player. /// /// Introduced to support server-side "disguised" identities (like /say or NPC chat), /// this packet bypasses the player-to-player chat signing requirements while /// still allowing the client to format the message using the standard chat registry. -#[derive(Serialize)] #[java_packet(PLAY_DISGUISED_CHAT)] pub struct CDisguisedChatMessage<'a> { /// The raw content of the message. @@ -41,3 +41,22 @@ impl<'a> CDisguisedChatMessage<'a> { } } } + +impl ClientPacket for CDisguisedChatMessage<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.message.encode())?; + write.write_var_int(&self.chat_type)?; + write.write_slice(&self.sender_name.encode())?; + if let Some(target) = self.target_name { + write.write_bool(true)?; + write.write_slice(&target.encode())?; + } else { + write.write_bool(false)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/display_objective.rs b/pumpkin-protocol/src/java/client/play/display_objective.rs index 8aa2885cf..aa4ec3fc7 100644 --- a/pumpkin-protocol/src/java/client/play/display_objective.rs +++ b/pumpkin-protocol/src/java/client/play/display_objective.rs @@ -1,17 +1,17 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::{ packet::clientbound::PLAY_SET_DISPLAY_OBJECTIVE, scoreboard::ScoreboardDisplaySlot, }; use pumpkin_macros::java_packet; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Instructs the client to display a specific scoreboard objective in a given slot. /// /// This packet is the final step in showing a scoreboard to a player. After /// an objective is created and populated with scores, this packet "maps" /// that objective to a visual location like the sidebar or the player list. -#[derive(Serialize)] #[java_packet(PLAY_SET_DISPLAY_OBJECTIVE)] pub struct CDisplayObjective { /// The display slot/position for the objective. @@ -30,3 +30,15 @@ impl CDisplayObjective { } } } + +impl ClientPacket for CDisplayObjective { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.position)?; + write.write_string(&self.score_name)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/entity_animation.rs b/pumpkin-protocol/src/java/client/play/entity_animation.rs index 97e188140..7e5658c9a 100644 --- a/pumpkin-protocol/src/java/client/play/entity_animation.rs +++ b/pumpkin-protocol/src/java/client/play/entity_animation.rs @@ -1,14 +1,14 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_ANIMATE; use pumpkin_macros::java_packet; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Triggers a specific animation for an entity that is visible to the client. /// /// This is primarily used for player-driven animations like swinging an arm /// or showing damage, but it can apply to other entities as well. -#[derive(Serialize)] #[java_packet(PLAY_ANIMATE)] pub struct CEntityAnimation { /// The Entity ID of the entity performing the animation. @@ -28,6 +28,18 @@ impl CEntityAnimation { } } +impl ClientPacket for CEntityAnimation { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_u8(self.animation)?; + Ok(()) + } +} + #[derive(Debug)] pub enum Animation { SwingMainArm, diff --git a/pumpkin-protocol/src/java/client/play/entity_metadata.rs b/pumpkin-protocol/src/java/client/play/entity_metadata.rs index dcd8afecf..0c7817af0 100644 --- a/pumpkin-protocol/src/java/client/play/entity_metadata.rs +++ b/pumpkin-protocol/src/java/client/play/entity_metadata.rs @@ -7,13 +7,22 @@ use pumpkin_data::{ }; use pumpkin_macros::java_packet; use pumpkin_util::version::JavaMinecraftVersion; -use serde::Serialize; use crate::{ ClientPacket, VarInt, - ser::{NetworkWriteExt, WritingError, serializer}, + ser::{NetworkWriteExt, WritingError}, }; +pub trait MetadataSerializer { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError>; +} + +impl MetadataSerializer for &T { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + (*self).write_metadata(writer) + } +} + /// Updates the "Data Tracker" values for an entity. /// /// Entity Metadata (or `DataWatchers`) controls persistent visual states that @@ -41,17 +50,13 @@ impl CSetEntityMetadata { impl ClientPacket for CSetEntityMetadata { fn write_packet_data( &self, - write: impl Write, + mut write: impl Write, _version: &JavaMinecraftVersion, ) -> Result<(), WritingError> { - let mut write = write; - // 1. Entity ID write.write_var_int(&self.entity_id)?; - write - .write_all(&self.metadata) - .map_err(WritingError::IoError) + write.write_slice(&self.metadata) } } @@ -73,10 +78,10 @@ impl Metadata { pub fn write( &self, mut writer: W, - version: &pumpkin_util::version::JavaMinecraftVersion, + version: &JavaMinecraftVersion, ) -> Result<(), WritingError> where - T: Serialize, + T: MetadataSerializer, { let resolved_index = self.index.get(version); @@ -95,12 +100,7 @@ impl Metadata { if self.r#type == MetaDataType::BLOCK_STATE { let mut serialized_value = Vec::new(); - { - let mut serializer = serializer::Serializer::new(&mut serialized_value); - self.value - .serialize(&mut serializer) - .map_err(|e| WritingError::Serde(e.to_string()))?; - }; + self.value.write_metadata(&mut serialized_value)?; let mut cursor = Cursor::new(serialized_value); let decoded_state = VarInt::decode(&mut cursor).map_err(|e| { @@ -115,12 +115,7 @@ impl Metadata { if self.r#type == MetaDataType::ITEM_STACK { let mut serialized_value = Vec::new(); - { - let mut serializer = serializer::Serializer::new(&mut serialized_value); - self.value - .serialize(&mut serializer) - .map_err(|e| WritingError::Serde(e.to_string()))?; - }; + self.value.write_metadata(&mut serialized_value)?; let mut cursor = Cursor::new(serialized_value); let item_count = VarInt::decode(&mut cursor).map_err(|e| { @@ -138,18 +133,134 @@ impl Metadata { writer.write_var_int(&VarInt(i32::from(remapped_id)))?; let remainder_start = cursor.position() as usize; let inner = cursor.into_inner(); - writer.write_all(&inner[remainder_start..]).map_err(|e| { - WritingError::Message(format!("Failed to write item stack remainder: {e}")) - })?; + writer.write_slice(&inner[remainder_start..])?; } return Ok(()); } - let mut serializer = serializer::Serializer::new(&mut writer); - self.value - .serialize(&mut serializer) - .map_err(|e| WritingError::Serde(e.to_string()))?; + self.value.write_metadata(&mut writer)?; Ok(()) } } + +impl MetadataSerializer for bool { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_bool(*self) + } +} + +impl MetadataSerializer for i8 { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_i8(*self) + } +} + +impl MetadataSerializer for u8 { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_u8(*self) + } +} + +impl MetadataSerializer for i16 { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_i16(*self) + } +} + +impl MetadataSerializer for u16 { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_u16(*self) + } +} + +impl MetadataSerializer for i32 { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_i32(*self) + } +} + +impl MetadataSerializer for u32 { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_u32(*self) + } +} + +impl MetadataSerializer for f32 { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_f32(*self) + } +} + +impl MetadataSerializer for VarInt { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_var_int(self) + } +} + +impl MetadataSerializer for String { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_string(self) + } +} + +impl MetadataSerializer for pumpkin_util::text::TextComponent { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_slice(&self.encode()) + } +} + +impl MetadataSerializer for Option { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + if let Some(text) = self { + writer.write_bool(true)?; + writer.write_slice(&text.encode())?; + } else { + writer.write_bool(false)?; + } + Ok(()) + } +} + +impl MetadataSerializer for crate::codec::item_stack_seralizer::ItemStackSerializer<'_> { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + self.write(writer) + } +} + +impl MetadataSerializer for Option { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + if let Some(s) = self { + writer.write_bool(true)?; + writer.write_string(s)?; + } else { + writer.write_bool(false)?; + } + Ok(()) + } +} + +impl MetadataSerializer for pumpkin_util::math::position::BlockPos { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + writer.write_block_pos(self) + } +} + +impl MetadataSerializer for Option { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + if let Some(pos) = self { + writer.write_bool(true)?; + writer.write_block_pos(pos)?; + } else { + writer.write_bool(false)?; + } + Ok(()) + } +} + +impl MetadataSerializer for crate::codec::optional_int::OptionalInt { + fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> { + let val = self.0.map_or(0, |id| id + 1); + writer.write_var_int(&VarInt(val)) + } +} diff --git a/pumpkin-protocol/src/java/client/play/entity_position_sync.rs b/pumpkin-protocol/src/java/client/play/entity_position_sync.rs index 51e44b486..8896ed73e 100644 --- a/pumpkin-protocol/src/java/client/play/entity_position_sync.rs +++ b/pumpkin-protocol/src/java/client/play/entity_position_sync.rs @@ -1,9 +1,10 @@ +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_ENTITY_POSITION_SYNC; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; -use serde::Serialize; - -use crate::VarInt; +use pumpkin_util::version::JavaMinecraftVersion; /// Updates the exact position, rotation, and velocity of an entity. /// @@ -14,7 +15,6 @@ use crate::VarInt; /// Note: This packet must NOT be used for the player receiving the packet or /// any entity the player is currently riding. #[java_packet(PLAY_ENTITY_POSITION_SYNC)] -#[derive(Serialize)] pub struct CEntityPositionSync { /// The Entity ID of the entity being moved. pub entity_id: VarInt, @@ -51,3 +51,23 @@ impl CEntityPositionSync { } } } + +impl ClientPacket for CEntityPositionSync { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_f64(self.position.x)?; + write.write_f64(self.position.y)?; + write.write_f64(self.position.z)?; + write.write_f64(self.delta.x)?; + write.write_f64(self.delta.y)?; + write.write_f64(self.delta.z)?; + write.write_f32(self.yaw)?; + write.write_f32(self.pitch)?; + write.write_bool(self.on_ground)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs b/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs index 6174ede0a..a5f543f99 100644 --- a/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs +++ b/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs @@ -65,12 +65,15 @@ impl ClientPacket for CEntitySoundEffect { IdOr::Value(value) => IdOr::Value(value.clone()), }; - write.write_serialize(&sound_event)?; - write.write_serialize(&self.sound_category)?; - write.write_serialize(&self.entity_id)?; - write.write_serialize(&self.volume)?; - write.write_serialize(&self.pitch)?; - write.write_serialize(&self.seed) + crate::IdOr::::write(&sound_event, &mut write, |w, e| { + w.write_string(&e.sound_name)?; + w.write_option(&e.range, |w2, r| w2.write_f32(*r)) + })?; + write.write_var_int(&self.sound_category)?; + write.write_var_int(&self.entity_id)?; + write.write_f32(self.volume)?; + write.write_f32(self.pitch)?; + write.write_i64(self.seed) } } diff --git a/pumpkin-protocol/src/java/client/play/entity_status.rs b/pumpkin-protocol/src/java/client/play/entity_status.rs index 240eb632e..4fc9a8af5 100644 --- a/pumpkin-protocol/src/java/client/play/entity_status.rs +++ b/pumpkin-protocol/src/java/client/play/entity_status.rs @@ -1,6 +1,9 @@ use pumpkin_data::packet::clientbound::PLAY_ENTITY_EVENT; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sends a status update for a specific entity. /// @@ -8,7 +11,6 @@ use serde::{Deserialize, Serialize}; /// warrant a complex packet of their own. It primarily handles visual /// and logical state triggers, such as tool breaking, totem usage, /// or sheep shearing. -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_ENTITY_EVENT)] pub struct CEntityStatus { /// The Entity ID of the entity affected by the status change. @@ -27,3 +29,15 @@ impl CEntityStatus { } } } + +impl ClientPacket for CEntityStatus { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i32_be(self.entity_id)?; + write.write_i8(self.entity_status)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/explode.rs b/pumpkin-protocol/src/java/client/play/explode.rs index 61aac0cb0..d710e2a13 100644 --- a/pumpkin-protocol/src/java/client/play/explode.rs +++ b/pumpkin-protocol/src/java/client/play/explode.rs @@ -1,16 +1,17 @@ use pumpkin_data::packet::clientbound::PLAY_EXPLODE; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; -use serde::Serialize; +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use crate::{IdOr, SoundEvent, codec::var_int::VarInt}; +use pumpkin_util::version::JavaMinecraftVersion; /// Notifies the client that an explosion has occurred. /// /// This is a high-level packet that handles the visual, auditory, and physical /// effects of an explosion in a single call. It triggers the explosion particles, /// plays the sound at the source, and applies knockback to the player. -#[derive(Serialize)] #[java_packet(PLAY_EXPLODE)] pub struct CExplosion { /// The center coordinates of the explosion. @@ -54,3 +55,34 @@ impl CExplosion { } } } + +impl ClientPacket for CExplosion { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f64_be(self.center.x)?; + write.write_f64_be(self.center.y)?; + write.write_f64_be(self.center.z)?; + write.write_f32_be(self.radius)?; + write.write_i32_be(self.block_count)?; + write.write_option(&self.knockback, |w, k| { + w.write_f64_be(k.x)?; + w.write_f64_be(k.y)?; + w.write_f64_be(k.z)?; + Ok(()) + })?; + write.write_var_int(&self.particle)?; + match &self.sound { + IdOr::Id(id) => write.write_var_int(&VarInt((*id + 1) as i32))?, + IdOr::Value(event) => { + write.write_var_int(&VarInt(0))?; + write.write_string(&event.sound_name)?; + write.write_option(&event.range, |w, r| w.write_f32_be(*r))?; + } + } + write.write_var_int(&self.block_particles_pool_size)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/game_event.rs b/pumpkin-protocol/src/java/client/play/game_event.rs index e99fdad54..547124e34 100644 --- a/pumpkin-protocol/src/java/client/play/game_event.rs +++ b/pumpkin-protocol/src/java/client/play/game_event.rs @@ -1,13 +1,15 @@ use pumpkin_data::packet::clientbound::PLAY_GAME_EVENT; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Updates the game state or triggers specific environmental changes. /// /// This packet is the primary way the server communicates global or /// context-specific transitions, such as changing the weather, /// altering the player's gamemode, or displaying the credits. -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_GAME_EVENT)] pub struct CGameEvent { /// The ID of the event type. @@ -43,3 +45,15 @@ pub enum GameEvent { LimitedCrafting, StartWaitingChunks, } + +impl ClientPacket for CGameEvent { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_u8(self.event)?; + write.write_f32_be(self.value)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/head_rot.rs b/pumpkin-protocol/src/java/client/play/head_rot.rs index 04fe4b7aa..1df98e1e6 100644 --- a/pumpkin-protocol/src/java/client/play/head_rot.rs +++ b/pumpkin-protocol/src/java/client/play/head_rot.rs @@ -1,8 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_ROTATE_HEAD; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Rotates an entity's head to a specific yaw. /// @@ -10,7 +12,6 @@ use crate::VarInt; /// While standard movement packets update the body, this packet is /// required to make an entity (like a player or a mob) look in a /// specific direction without necessarily turning its entire body. -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_ROTATE_HEAD)] pub struct CHeadRot { /// The Entity ID of the entity whose head is rotating. @@ -28,3 +29,15 @@ impl CHeadRot { } } } + +impl ClientPacket for CHeadRot { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_u8(self.head_yaw)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/hurt_animation.rs b/pumpkin-protocol/src/java/client/play/hurt_animation.rs index 4b19fa7cd..301b361d1 100644 --- a/pumpkin-protocol/src/java/client/play/hurt_animation.rs +++ b/pumpkin-protocol/src/java/client/play/hurt_animation.rs @@ -1,15 +1,16 @@ use pumpkin_data::packet::clientbound::PLAY_HURT_ANIMATION; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Triggers the "hurt" visual effect on an entity. /// /// This packet causes the entity to turn red and perform a directional /// camera shake or model tilt. It is typically sent immediately after /// an entity's health is reduced. -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_HURT_ANIMATION)] pub struct CHurtAnimation { /// The Entity ID of the entity that was hurt. @@ -25,3 +26,15 @@ impl CHurtAnimation { Self { entity_id, yaw } } } + +impl ClientPacket for CHurtAnimation { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_f32_be(self.yaw)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/initialize_world_border.rs b/pumpkin-protocol/src/java/client/play/initialize_world_border.rs index e58274b4e..6b81a93d4 100644 --- a/pumpkin-protocol/src/java/client/play/initialize_world_border.rs +++ b/pumpkin-protocol/src/java/client/play/initialize_world_border.rs @@ -1,15 +1,16 @@ use pumpkin_data::packet::clientbound::PLAY_INITIALIZE_BORDER; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use crate::{VarInt, codec::var_long::VarLong}; +use pumpkin_util::version::JavaMinecraftVersion; /// Fully initializes the world border for the client. /// /// This packet is sent when a player joins the world or changes dimensions. /// It synchronizes the current position, size, and all warning parameters /// to ensure the client-side visual barrier matches the server's authority. -#[derive(Serialize)] #[java_packet(PLAY_INITIALIZE_BORDER)] pub struct CInitializeWorldBorder { /// The X coordinate of the center of the world border. @@ -57,3 +58,21 @@ impl CInitializeWorldBorder { } } } + +impl ClientPacket for CInitializeWorldBorder { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f64_be(self.x)?; + write.write_f64_be(self.z)?; + write.write_f64_be(self.old_diameter)?; + write.write_f64_be(self.new_diameter)?; + write.write_var_long(&self.speed)?; + write.write_var_int(&self.portal_teleport_boundary)?; + write.write_var_int(&self.warning_blocks)?; + write.write_var_int(&self.warning_time)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/item_cooldown.rs b/pumpkin-protocol/src/java/client/play/item_cooldown.rs index ef6aebd7a..262a0cc7d 100644 --- a/pumpkin-protocol/src/java/client/play/item_cooldown.rs +++ b/pumpkin-protocol/src/java/client/play/item_cooldown.rs @@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_COOLDOWN; use crate::codec::var_int::VarInt; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_COOLDOWN)] pub struct CItemCooldown { pub group: String, @@ -17,3 +19,15 @@ impl CItemCooldown { Self { group, cooldown } } } + +impl ClientPacket for CItemCooldown { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(&self.group)?; + write.write_var_int(&self.cooldown)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/keep_alive.rs b/pumpkin-protocol/src/java/client/play/keep_alive.rs index 16569b6d0..3b0c5eb1a 100644 --- a/pumpkin-protocol/src/java/client/play/keep_alive.rs +++ b/pumpkin-protocol/src/java/client/play/keep_alive.rs @@ -1,6 +1,9 @@ use pumpkin_data::packet::clientbound::PLAY_KEEP_ALIVE; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Maintains the connection and measures latency (ping) between client and server. /// @@ -8,7 +11,6 @@ use serde::{Deserialize, Serialize}; /// The client must respond with the exact same ID. If the server does not receive /// a response within a timeout period (usually 30 seconds), it will disconnect /// the player with a "Timed Out" message. -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_KEEP_ALIVE)] pub struct CKeepAlive { /// A unique random identifier for this specific keep-alive request. @@ -22,3 +24,14 @@ impl CKeepAlive { Self { keep_alive_id } } } + +impl ClientPacket for CKeepAlive { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i64_be(self.keep_alive_id)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/merchant_offers.rs b/pumpkin-protocol/src/java/client/play/merchant_offers.rs index 48fd6b6fd..98d840c86 100644 --- a/pumpkin-protocol/src/java/client/play/merchant_offers.rs +++ b/pumpkin-protocol/src/java/client/play/merchant_offers.rs @@ -1,11 +1,13 @@ use pumpkin_data::packet::clientbound::PLAY_MERCHANT_OFFERS; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use crate::ClientPacket; use crate::VarInt; use crate::codec::item_stack_seralizer::ItemStackSerializer; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize, Deserialize, Clone)] +#[derive(Clone)] pub struct MerchantOffer { pub base_cost_a: ItemStackSerializer<'static>, // TODO: item cost pub output: ItemStackSerializer<'static>, @@ -19,7 +21,28 @@ pub struct MerchantOffer { pub demand: i32, } -#[derive(Serialize)] +impl MerchantOffer { + fn write( + &self, + mut write: impl std::io::Write, + version: JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + self.base_cost_a.write_with_version(&mut write, &version)?; + self.output.write_with_version(&mut write, &version)?; + write.write_option(&self.cost_b, |w, cost_b| { + cost_b.write_with_version(w, &version) + })?; + write.write_bool(self.is_disabled)?; + write.write_i32_be(self.uses)?; + write.write_i32_be(self.max_uses)?; + write.write_i32_be(self.xp)?; + write.write_i32_be(self.special_price)?; + write.write_f32_be(self.price_multiplier)?; + write.write_i32_be(self.demand)?; + Ok(()) + } +} + #[java_packet(PLAY_MERCHANT_OFFERS)] pub struct CMerchantOffers { pub window_id: VarInt, @@ -50,3 +73,22 @@ impl CMerchantOffers { } } } + +impl ClientPacket for CMerchantOffers { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.window_id)?; + write.write_var_int(&VarInt(self.offers.len() as i32))?; + for offer in &self.offers { + offer.write(&mut write, *version)?; + } + write.write_var_int(&self.villager_level)?; + write.write_var_int(&self.experience)?; + write.write_bool(self.is_regular_villager)?; + write.write_bool(self.can_restock)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/open_screen.rs b/pumpkin-protocol/src/java/client/play/open_screen.rs index 8915422a4..e554c93f2 100644 --- a/pumpkin-protocol/src/java/client/play/open_screen.rs +++ b/pumpkin-protocol/src/java/client/play/open_screen.rs @@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_OPEN_SCREEN; use pumpkin_util::text::TextComponent; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Instructs the client to open a specific type of GUI (inventory, chest, etc.). /// @@ -12,7 +14,6 @@ use crate::VarInt; /// or when a command/plugin forces an interface to open. It establishes a /// `sync_id` which must be used in all subsequent "Set Slot" or "Click Slot" /// packets to ensure the server and client are talking about the same window. -#[derive(Serialize)] #[java_packet(PLAY_OPEN_SCREEN)] pub struct COpenScreen<'a> { /// A unique identifier for the current window session. @@ -40,3 +41,18 @@ impl<'a> COpenScreen<'a> { } } } + +impl ClientPacket for COpenScreen<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.sync_id)?; + write.write_var_int(&self.window_type)?; + // Write the text component as JSON string + let buf = self.window_title.encode(); + write.write_slice(&buf)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/open_sign_editor.rs b/pumpkin-protocol/src/java/client/play/open_sign_editor.rs index 1c5452c51..b21025dd6 100644 --- a/pumpkin-protocol/src/java/client/play/open_sign_editor.rs +++ b/pumpkin-protocol/src/java/client/play/open_sign_editor.rs @@ -1,14 +1,16 @@ use pumpkin_data::packet::clientbound::PLAY_OPEN_SIGN_EDITOR; use pumpkin_macros::java_packet; use pumpkin_util::math::position::BlockPos; -use serde::Serialize; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Opens the sign text input screen for the client. /// /// This packet is sent by the server to force the client to show the /// sign editing interface. This usually happens immediately after a /// player places a sign or interacts with an existing one (if allowed). -#[derive(Serialize)] #[java_packet(PLAY_OPEN_SIGN_EDITOR)] pub struct COpenSignEditor { /// The world coordinates of the sign block to be edited. @@ -27,3 +29,15 @@ impl COpenSignEditor { } } } + +impl ClientPacket for COpenSignEditor { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_block_pos(&self.location)?; + write.write_bool(self.is_front_text)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/ping_response.rs b/pumpkin-protocol/src/java/client/play/ping_response.rs index 4a502ff2b..87384f549 100644 --- a/pumpkin-protocol/src/java/client/play/ping_response.rs +++ b/pumpkin-protocol/src/java/client/play/ping_response.rs @@ -1,9 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_PONG_RESPONSE; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Responds to a client-initiated ping request to synchronize game state. -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_PONG_RESPONSE)] pub struct CPingResponse { /// The unique identifier sent by the client in the initial Ping packet. @@ -17,3 +19,14 @@ impl CPingResponse { Self { payload } } } + +impl ClientPacket for CPingResponse { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i64_be(self.payload)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/player_abilities.rs b/pumpkin-protocol/src/java/client/play/player_abilities.rs index 72fcbd5b2..e3cdab198 100644 --- a/pumpkin-protocol/src/java/client/play/player_abilities.rs +++ b/pumpkin-protocol/src/java/client/play/player_abilities.rs @@ -1,13 +1,15 @@ use pumpkin_data::packet::clientbound::PLAY_PLAYER_ABILITIES; use pumpkin_macros::java_packet; -use serde::Serialize; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Updates the player's movement and interaction abilities. /// /// This packet informs the client about the player's state (flying, invulnerable) /// and sets the movement speeds. While the client applies these visuals, /// the server must still validate these states to prevent cheating. -#[derive(Serialize)] #[java_packet(PLAY_PLAYER_ABILITIES)] pub struct CPlayerAbilities { /// A bitmask of player states. @@ -34,3 +36,16 @@ impl CPlayerAbilities { } } } + +impl ClientPacket for CPlayerAbilities { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i8(self.flags)?; + write.write_f32_be(self.flying_speed)?; + write.write_f32_be(self.field_of_view)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/player_info_update.rs b/pumpkin-protocol/src/java/client/play/player_info_update.rs index 6491b5bf7..b5a5910a8 100644 --- a/pumpkin-protocol/src/java/client/play/player_info_update.rs +++ b/pumpkin-protocol/src/java/client/play/player_info_update.rs @@ -107,7 +107,9 @@ impl ClientPacket for CPlayerInfoUpdate<'_> { PlayerAction::UpdateListed(listed) => p.write_bool(*listed)?, PlayerAction::UpdateLatency(latency) => p.write_var_int(latency)?, PlayerAction::UpdateDisplayName(display_name) => { - p.write_option(display_name, crate::ser::NetworkWriteExt::write_serialize)?; + p.write_option(display_name, |w, text_component| { + w.write_slice(&text_component.encode()) + })?; } PlayerAction::UpdateListOrder(order) => p.write_var_int(order)?, } diff --git a/pumpkin-protocol/src/java/client/play/player_remove.rs b/pumpkin-protocol/src/java/client/play/player_remove.rs index 91dd0d75b..fec987267 100644 --- a/pumpkin-protocol/src/java/client/play/player_remove.rs +++ b/pumpkin-protocol/src/java/client/play/player_remove.rs @@ -1,19 +1,18 @@ use pumpkin_data::packet::clientbound::PLAY_PLAYER_INFO_REMOVE; use pumpkin_macros::java_packet; -use serde::{Serialize, ser::SerializeSeq}; + +use crate::ClientPacket; +use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to remove one or more players from the client's player list (tab list). /// /// This packet is typically used when a player leaves the server or becomes invisible /// to the recipient (e.g., moving out of tracking range). -#[derive(Serialize)] #[java_packet(PLAY_PLAYER_INFO_REMOVE)] pub struct CRemovePlayerInfo<'a> { /// A list of UUIDs corresponding to the players that should be removed. - /// - /// The field uses a custom serializer to format the UUIDs according to the - /// Minecraft protocol's VarInt-prefixed array format. - #[serde(serialize_with = "serialize_slice_uuids")] pub players: &'a [uuid::Uuid], } @@ -24,13 +23,16 @@ impl<'a> CRemovePlayerInfo<'a> { } } -fn serialize_slice_uuids( - uuids: &[uuid::Uuid], - serializer: S, -) -> Result { - let mut seq = serializer.serialize_seq(Some(uuids.len()))?; - for uuid in uuids { - seq.serialize_element(uuid.as_bytes())?; +impl ClientPacket for CRemovePlayerInfo<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&VarInt(self.players.len() as i32))?; + for uuid in self.players { + write.write_uuid(uuid)?; + } + Ok(()) } - seq.end() } diff --git a/pumpkin-protocol/src/java/client/play/remove_entities.rs b/pumpkin-protocol/src/java/client/play/remove_entities.rs index 9bd02eb0e..ea0b0911d 100644 --- a/pumpkin-protocol/src/java/client/play/remove_entities.rs +++ b/pumpkin-protocol/src/java/client/play/remove_entities.rs @@ -1,14 +1,15 @@ use pumpkin_data::packet::clientbound::PLAY_REMOVE_ENTITIES; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to instruct the client to remove (despawn) one or more entities. /// /// This is typically sent when an entity leaves the player's tracking range, /// is killed, or is otherwise removed from the world. -#[derive(Serialize)] #[java_packet(PLAY_REMOVE_ENTITIES)] pub struct CRemoveEntities<'a> { /// A list of entity IDs to be removed. @@ -21,3 +22,17 @@ impl<'a> CRemoveEntities<'a> { Self { entity_ids } } } + +impl ClientPacket for CRemoveEntities<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&VarInt(self.entity_ids.len() as i32))?; + for id in self.entity_ids { + write.write_var_int(id)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/remove_mob_effect.rs b/pumpkin-protocol/src/java/client/play/remove_mob_effect.rs index 82df113d5..05739bfce 100644 --- a/pumpkin-protocol/src/java/client/play/remove_mob_effect.rs +++ b/pumpkin-protocol/src/java/client/play/remove_mob_effect.rs @@ -1,9 +1,11 @@ use crate::codec::var_int::VarInt; use pumpkin_data::packet::clientbound::PLAY_REMOVE_MOB_EFFECT; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_REMOVE_MOB_EFFECT)] pub struct CRemoveMobEffect { pub entity_id: VarInt, @@ -19,3 +21,15 @@ impl CRemoveMobEffect { } } } + +impl ClientPacket for CRemoveMobEffect { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_var_int(&self.effect_id)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/reset_score.rs b/pumpkin-protocol/src/java/client/play/reset_score.rs index 26611b3cd..5edfad375 100644 --- a/pumpkin-protocol/src/java/client/play/reset_score.rs +++ b/pumpkin-protocol/src/java/client/play/reset_score.rs @@ -1,8 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_RESET_SCORE; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_RESET_SCORE)] pub struct CResetScore { pub entity_name: String, @@ -18,3 +20,15 @@ impl CResetScore { } } } + +impl ClientPacket for CResetScore { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(&self.entity_name)?; + write.write_option(&self.objective_name, |w, obj| w.write_string(obj))?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/select_advancements_tab.rs b/pumpkin-protocol/src/java/client/play/select_advancements_tab.rs index 4889edbff..ba9962617 100644 --- a/pumpkin-protocol/src/java/client/play/select_advancements_tab.rs +++ b/pumpkin-protocol/src/java/client/play/select_advancements_tab.rs @@ -1,9 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SELECT_ADVANCEMENTS_TAB; use pumpkin_macros::java_packet; use pumpkin_util::identifier::Identifier; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_SELECT_ADVANCEMENTS_TAB)] pub struct CSelectAdvancementsTab { pub tab_id: Option, @@ -15,3 +17,14 @@ impl CSelectAdvancementsTab { Self { tab_id } } } + +impl ClientPacket for CSelectAdvancementsTab { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_option(&self.tab_id, |w, id| w.write_string(&id.to_string()))?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/server_links.rs b/pumpkin-protocol/src/java/client/play/server_links.rs index b761e9da8..9e29ef94a 100644 --- a/pumpkin-protocol/src/java/client/play/server_links.rs +++ b/pumpkin-protocol/src/java/client/play/server_links.rs @@ -1,9 +1,7 @@ use crate::Link; use pumpkin_data::packet::clientbound::PLAY_SERVER_LINKS; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] #[java_packet(PLAY_SERVER_LINKS)] pub struct CPlayServerLinks<'a> { pub links: &'a [Link<'a>], diff --git a/pumpkin-protocol/src/java/client/play/set_border_center.rs b/pumpkin-protocol/src/java/client/play/set_border_center.rs index 0efd24ddd..a02935a06 100644 --- a/pumpkin-protocol/src/java/client/play/set_border_center.rs +++ b/pumpkin-protocol/src/java/client/play/set_border_center.rs @@ -1,8 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_CENTER; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_SET_BORDER_CENTER)] pub struct CSetBorderCenter { pub x: f64, @@ -15,3 +17,15 @@ impl CSetBorderCenter { Self { x, z } } } + +impl ClientPacket for CSetBorderCenter { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f64_be(self.x)?; + write.write_f64_be(self.z)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_border_lerp_size.rs b/pumpkin-protocol/src/java/client/play/set_border_lerp_size.rs index d5f48817c..07724adf2 100644 --- a/pumpkin-protocol/src/java/client/play/set_border_lerp_size.rs +++ b/pumpkin-protocol/src/java/client/play/set_border_lerp_size.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_LERP_SIZE; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::codec::var_long::VarLong; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_BORDER_LERP_SIZE)] pub struct CSetBorderLerpSize { pub old_diameter: f64, @@ -22,3 +23,16 @@ impl CSetBorderLerpSize { } } } + +impl ClientPacket for CSetBorderLerpSize { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f64_be(self.old_diameter)?; + write.write_f64_be(self.new_diameter)?; + write.write_var_long(&self.speed)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_border_size.rs b/pumpkin-protocol/src/java/client/play/set_border_size.rs index e6f3bb2bb..3ef8999b5 100644 --- a/pumpkin-protocol/src/java/client/play/set_border_size.rs +++ b/pumpkin-protocol/src/java/client/play/set_border_size.rs @@ -1,8 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_SIZE; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_SET_BORDER_SIZE)] pub struct CSetBorderSize { pub diameter: f64, @@ -14,3 +16,14 @@ impl CSetBorderSize { Self { diameter } } } + +impl ClientPacket for CSetBorderSize { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f64_be(self.diameter)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_border_warning_delay.rs b/pumpkin-protocol/src/java/client/play/set_border_warning_delay.rs index ffc38ac7e..2fb3b5968 100644 --- a/pumpkin-protocol/src/java/client/play/set_border_warning_delay.rs +++ b/pumpkin-protocol/src/java/client/play/set_border_warning_delay.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_WARNING_DELAY; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_BORDER_WARNING_DELAY)] pub struct CSetBorderWarningDelay { pub warning_time: VarInt, @@ -16,3 +17,14 @@ impl CSetBorderWarningDelay { Self { warning_time } } } + +impl ClientPacket for CSetBorderWarningDelay { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.warning_time)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_border_warning_distance.rs b/pumpkin-protocol/src/java/client/play/set_border_warning_distance.rs index 981aeeb5e..ed75175f2 100644 --- a/pumpkin-protocol/src/java/client/play/set_border_warning_distance.rs +++ b/pumpkin-protocol/src/java/client/play/set_border_warning_distance.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_WARNING_DISTANCE; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_BORDER_WARNING_DISTANCE)] pub struct CSetBorderWarningDistance { pub warning_blocks: VarInt, @@ -16,3 +17,14 @@ impl CSetBorderWarningDistance { Self { warning_blocks } } } + +impl ClientPacket for CSetBorderWarningDistance { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.warning_blocks)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_camera.rs b/pumpkin-protocol/src/java/client/play/set_camera.rs index 3c37518f8..2459738cb 100644 --- a/pumpkin-protocol/src/java/client/play/set_camera.rs +++ b/pumpkin-protocol/src/java/client/play/set_camera.rs @@ -1,10 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_SET_CAMERA; use pumpkin_util::version::JavaMinecraftVersion; -use serde::Serialize; +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use crate::{VarInt, packet::MultiVersionJavaPacket}; -#[derive(Serialize)] pub struct CSetCamera { pub camera_id: VarInt, } @@ -16,6 +16,17 @@ impl CSetCamera { } } +impl ClientPacket for CSetCamera { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.camera_id)?; + Ok(()) + } +} + impl MultiVersionJavaPacket for CSetCamera { fn to_id(version: JavaMinecraftVersion) -> i32 { PLAY_SET_CAMERA.to_id(version) diff --git a/pumpkin-protocol/src/java/client/play/set_container_property.rs b/pumpkin-protocol/src/java/client/play/set_container_property.rs index 62a66a403..022a34a05 100644 --- a/pumpkin-protocol/src/java/client/play/set_container_property.rs +++ b/pumpkin-protocol/src/java/client/play/set_container_property.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_CONTAINER_SET_DATA; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_CONTAINER_SET_DATA)] pub struct CSetContainerProperty { pub window_id: VarInt, @@ -22,3 +23,16 @@ impl CSetContainerProperty { } } } + +impl ClientPacket for CSetContainerProperty { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.window_id)?; + write.write_i16(self.property)?; + write.write_i16(self.value)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_cursor_slot.rs b/pumpkin-protocol/src/java/client/play/set_cursor_slot.rs index ea827293e..cab881c4b 100644 --- a/pumpkin-protocol/src/java/client/play/set_cursor_slot.rs +++ b/pumpkin-protocol/src/java/client/play/set_cursor_slot.rs @@ -22,9 +22,9 @@ impl<'a> CSetCursorItem<'a> { impl ClientPacket for CSetCursorItem<'_> { fn write_packet_data( &self, - write: impl Write, + mut write: impl Write, version: &JavaMinecraftVersion, ) -> Result<(), WritingError> { - self.stack.write_with_version(write, version) + self.stack.write_with_version(&mut write, version) } } diff --git a/pumpkin-protocol/src/java/client/play/set_experience.rs b/pumpkin-protocol/src/java/client/play/set_experience.rs index 49f2cacf2..9671fed29 100644 --- a/pumpkin-protocol/src/java/client/play/set_experience.rs +++ b/pumpkin-protocol/src/java/client/play/set_experience.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_EXPERIENCE; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_EXPERIENCE)] pub struct CSetExperience { pub progress: f32, @@ -22,3 +23,16 @@ impl CSetExperience { } } } + +impl ClientPacket for CSetExperience { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f32(self.progress)?; + write.write_var_int(&self.level)?; + write.write_var_int(&self.total_experience)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_health.rs b/pumpkin-protocol/src/java/client/play/set_health.rs index f377b2a98..50872f7e0 100644 --- a/pumpkin-protocol/src/java/client/play/set_health.rs +++ b/pumpkin-protocol/src/java/client/play/set_health.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_HEALTH; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_HEALTH)] pub struct CSetHealth { pub health: f32, @@ -22,3 +23,16 @@ impl CSetHealth { } } } + +impl ClientPacket for CSetHealth { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f32(self.health)?; + write.write_var_int(&self.food)?; + write.write_f32(self.food_saturation)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_held_item.rs b/pumpkin-protocol/src/java/client/play/set_held_item.rs index 11530b1fc..e3acb7816 100644 --- a/pumpkin-protocol/src/java/client/play/set_held_item.rs +++ b/pumpkin-protocol/src/java/client/play/set_held_item.rs @@ -1,10 +1,10 @@ use pumpkin_data::packet::clientbound::{PLAY_SET_CARRIED_ITEM, PLAY_SET_HELD_SLOT}; use pumpkin_util::version::JavaMinecraftVersion; -use serde::Serialize; +use crate::ClientPacket; use crate::packet::MultiVersionJavaPacket; +use crate::ser::NetworkWriteExt; -#[derive(Serialize)] pub struct CSetSelectedSlot { pub slot: i8, } @@ -16,6 +16,17 @@ impl CSetSelectedSlot { } } +impl ClientPacket for CSetSelectedSlot { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i8(self.slot)?; + Ok(()) + } +} + impl MultiVersionJavaPacket for CSetSelectedSlot { fn to_id(version: JavaMinecraftVersion) -> i32 { if version >= JavaMinecraftVersion::V_1_21_2 { diff --git a/pumpkin-protocol/src/java/client/play/set_passengers.rs b/pumpkin-protocol/src/java/client/play/set_passengers.rs index 365ff4743..7325656ff 100644 --- a/pumpkin-protocol/src/java/client/play/set_passengers.rs +++ b/pumpkin-protocol/src/java/client/play/set_passengers.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_PASSENGERS; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_PASSENGERS)] pub struct CSetPassengers<'a> { pub entity_id: VarInt, @@ -20,3 +21,18 @@ impl<'a> CSetPassengers<'a> { } } } + +impl ClientPacket for CSetPassengers<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_var_int(&crate::VarInt(self.passengers.len() as i32))?; + for passenger in self.passengers { + write.write_var_int(passenger)?; + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_player_inventory.rs b/pumpkin-protocol/src/java/client/play/set_player_inventory.rs index c48bcf4b8..a08c7b1b6 100644 --- a/pumpkin-protocol/src/java/client/play/set_player_inventory.rs +++ b/pumpkin-protocol/src/java/client/play/set_player_inventory.rs @@ -29,6 +29,6 @@ impl ClientPacket for CSetPlayerInventory<'_> { ) -> Result<(), WritingError> { let mut write = write; write.write_var_int(&self.slot)?; - self.item.write_with_version(write, version) + self.item.write_with_version(&mut write, version) } } diff --git a/pumpkin-protocol/src/java/client/play/set_title.rs b/pumpkin-protocol/src/java/client/play/set_title.rs index 45b3da258..c2aba3d00 100644 --- a/pumpkin-protocol/src/java/client/play/set_title.rs +++ b/pumpkin-protocol/src/java/client/play/set_title.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_TITLE_TEXT; use pumpkin_util::text::TextComponent; +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_TITLE_TEXT)] pub struct CTitleText<'a> { pub title: &'a TextComponent, @@ -16,3 +17,14 @@ impl<'a> CTitleText<'a> { Self { title } } } + +impl ClientPacket for CTitleText<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.title.encode())?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/set_title_animation.rs b/pumpkin-protocol/src/java/client/play/set_title_animation.rs index d2fce2144..0360bd2c2 100644 --- a/pumpkin-protocol/src/java/client/play/set_title_animation.rs +++ b/pumpkin-protocol/src/java/client/play/set_title_animation.rs @@ -1,9 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_SET_TITLES_ANIMATION; +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SET_TITLES_ANIMATION)] pub struct CTitleAnimation { pub fade_in_ticks: i32, @@ -21,3 +22,16 @@ impl CTitleAnimation { } } } + +impl ClientPacket for CTitleAnimation { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i32_be(self.fade_in_ticks)?; + write.write_i32_be(self.stay_ticks)?; + write.write_i32_be(self.fade_out_ticks)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/show_dialog.rs b/pumpkin-protocol/src/java/client/play/show_dialog.rs index 4ba14f4ca..13e66103c 100644 --- a/pumpkin-protocol/src/java/client/play/show_dialog.rs +++ b/pumpkin-protocol/src/java/client/play/show_dialog.rs @@ -1,10 +1,9 @@ -use crate::IdOr; use crate::java::client::dialog::DialogNBT; +use crate::{ClientPacket, IdOr, ser::WritingError}; use pumpkin_data::packet::clientbound::PLAY_SHOW_DIALOG; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_SHOW_DIALOG)] pub struct CPlayShowDialog<'a> { pub dialog: IdOr>, @@ -16,3 +15,14 @@ impl<'a> CPlayShowDialog<'a> { Self { dialog } } } + +impl ClientPacket for CPlayShowDialog<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + self.dialog + .write(&mut write, |w, value| value.write_packet_data(w, version)) + } +} diff --git a/pumpkin-protocol/src/java/client/play/sound_effect.rs b/pumpkin-protocol/src/java/client/play/sound_effect.rs index 91d76f482..bb17817dc 100644 --- a/pumpkin-protocol/src/java/client/play/sound_effect.rs +++ b/pumpkin-protocol/src/java/client/play/sound_effect.rs @@ -55,12 +55,17 @@ impl ClientPacket for CSoundEffect { IdOr::Value(value) => IdOr::Value(value.clone()), }; - write.write_serialize(&sound_event)?; - write.write_serialize(&self.sound_category)?; - write.write_serialize(&self.position)?; - write.write_serialize(&self.volume)?; - write.write_serialize(&self.pitch)?; - write.write_serialize(&self.seed) + crate::IdOr::::write(&sound_event, &mut write, |w, e| { + w.write_string(&e.sound_name)?; + w.write_option(&e.range, |w2, r| w2.write_f32_be(*r)) + })?; + write.write_var_int(&self.sound_category)?; + write.write_i32_be(self.position.x * 8)?; + write.write_i32_be(self.position.y * 8)?; + write.write_i32_be(self.position.z * 8)?; + write.write_f32_be(self.volume)?; + write.write_f32_be(self.pitch)?; + write.write_i64_be(self.seed as i64) } } diff --git a/pumpkin-protocol/src/java/client/play/store_cookie.rs b/pumpkin-protocol/src/java/client/play/store_cookie.rs index 4f503e55d..d1c387fb3 100644 --- a/pumpkin-protocol/src/java/client/play/store_cookie.rs +++ b/pumpkin-protocol/src/java/client/play/store_cookie.rs @@ -1,11 +1,13 @@ use pumpkin_data::packet::clientbound::PLAY_STORE_COOKIE; use pumpkin_macros::java_packet; use pumpkin_util::resource_location::ResourceLocation; -use serde::Serialize; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Stores some arbitrary data on the client, which persists between server transfers. /// The Notchian client only accepts cookies of up to 5 kiB in size. -#[derive(Serialize)] #[java_packet(PLAY_STORE_COOKIE)] pub struct CStoreCookie<'a> { pub key: &'a ResourceLocation, @@ -18,3 +20,18 @@ impl<'a> CStoreCookie<'a> { Self { key, payload } } } + +impl ClientPacket for CStoreCookie<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.key)?; + write.write_var_int(&crate::VarInt(self.payload.len() as i32))?; + write + .write_all(self.payload) + .map_err(|_| crate::ser::WritingError::Message("IO Error".into()))?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/subtitle.rs b/pumpkin-protocol/src/java/client/play/subtitle.rs index f810bca94..b66423355 100644 --- a/pumpkin-protocol/src/java/client/play/subtitle.rs +++ b/pumpkin-protocol/src/java/client/play/subtitle.rs @@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_SUBTITLE_TEXT; use pumpkin_util::text::TextComponent; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_SET_SUBTITLE_TEXT)] pub struct CSubtitle<'a> { pub subtitle: &'a TextComponent, @@ -16,3 +18,14 @@ impl<'a> CSubtitle<'a> { Self { subtitle } } } + +impl ClientPacket for CSubtitle<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.subtitle.encode())?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/system_chat_message.rs b/pumpkin-protocol/src/java/client/play/system_chat_message.rs index e0ae64b88..ea0f40d47 100644 --- a/pumpkin-protocol/src/java/client/play/system_chat_message.rs +++ b/pumpkin-protocol/src/java/client/play/system_chat_message.rs @@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_SYSTEM_CHAT; use pumpkin_util::text::TextComponent; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_SYSTEM_CHAT)] pub struct CSystemChatMessage<'a> { pub content: &'a TextComponent, @@ -17,3 +19,15 @@ impl<'a> CSystemChatMessage<'a> { Self { content, overlay } } } + +impl ClientPacket for CSystemChatMessage<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.content.encode())?; + write.write_bool(self.overlay)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/tab_list.rs b/pumpkin-protocol/src/java/client/play/tab_list.rs index 7f134a067..ae76a5e10 100644 --- a/pumpkin-protocol/src/java/client/play/tab_list.rs +++ b/pumpkin-protocol/src/java/client/play/tab_list.rs @@ -1,10 +1,12 @@ use pumpkin_data::packet::clientbound::PLAY_TAB_LIST; use pumpkin_macros::java_packet; use pumpkin_util::text::TextComponent; -use serde::Serialize; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Updates the header and footer of the player list (Tab List). -#[derive(Serialize)] #[java_packet(PLAY_TAB_LIST)] pub struct CTabList<'a> { /// The text to be displayed at the top of the player list. @@ -19,3 +21,15 @@ impl<'a> CTabList<'a> { Self { header, footer } } } + +impl ClientPacket for CTabList<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_slice(&self.header.encode())?; + write.write_slice(&self.footer.encode())?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/take_item.rs b/pumpkin-protocol/src/java/client/play/take_item.rs index 9a5f4a263..b95fede80 100644 --- a/pumpkin-protocol/src/java/client/play/take_item.rs +++ b/pumpkin-protocol/src/java/client/play/take_item.rs @@ -1,9 +1,11 @@ use crate::VarInt; use pumpkin_data::packet::clientbound::PLAY_TAKE_ITEM_ENTITY; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_TAKE_ITEM_ENTITY)] pub struct CTakeItemEntity { /// The entity id of the item entity. @@ -24,3 +26,16 @@ impl CTakeItemEntity { } } } + +impl ClientPacket for CTakeItemEntity { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_var_int(&self.collector_entity_id)?; + write.write_var_int(&self.stack_amount)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/ticking_state.rs b/pumpkin-protocol/src/java/client/play/ticking_state.rs index 89b37c298..51b25fdee 100644 --- a/pumpkin-protocol/src/java/client/play/ticking_state.rs +++ b/pumpkin-protocol/src/java/client/play/ticking_state.rs @@ -1,8 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_TICKING_STATE; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_TICKING_STATE)] pub struct CTickingState { pub tick_rate: f32, @@ -18,3 +20,15 @@ impl CTickingState { } } } + +impl ClientPacket for CTickingState { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_f32_be(self.tick_rate)?; + write.write_bool(self.is_frozen)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/ticking_step.rs b/pumpkin-protocol/src/java/client/play/ticking_step.rs index a5faf98b3..9ddc0b4c5 100644 --- a/pumpkin-protocol/src/java/client/play/ticking_step.rs +++ b/pumpkin-protocol/src/java/client/play/ticking_step.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_TICKING_STEP; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_TICKING_STEP)] pub struct CTickingStep { pub tick_steps: VarInt, @@ -16,3 +17,14 @@ impl CTickingStep { Self { tick_steps } } } + +impl ClientPacket for CTickingStep { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.tick_steps)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/transfer.rs b/pumpkin-protocol/src/java/client/play/transfer.rs index 5a690f4f3..73ec248b4 100644 --- a/pumpkin-protocol/src/java/client/play/transfer.rs +++ b/pumpkin-protocol/src/java/client/play/transfer.rs @@ -1,9 +1,10 @@ +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; use pumpkin_data::packet::clientbound::PLAY_TRANSFER; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_TRANSFER)] pub struct CTransfer<'a> { pub host: &'a str, @@ -16,3 +17,15 @@ impl<'a> CTransfer<'a> { Self { host, port } } } + +impl ClientPacket for CTransfer<'_> { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(self.host)?; + write.write_var_int(&self.port)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/unload_chunk.rs b/pumpkin-protocol/src/java/client/play/unload_chunk.rs index 5e88fde33..d17bf4714 100644 --- a/pumpkin-protocol/src/java/client/play/unload_chunk.rs +++ b/pumpkin-protocol/src/java/client/play/unload_chunk.rs @@ -1,8 +1,10 @@ use pumpkin_data::packet::clientbound::PLAY_FORGET_LEVEL_CHUNK; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(Serialize)] +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; + #[java_packet(PLAY_FORGET_LEVEL_CHUNK)] pub struct CUnloadChunk { pub z: i32, @@ -15,3 +17,15 @@ impl CUnloadChunk { Self { z, x } } } + +impl ClientPacket for CUnloadChunk { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i32_be(self.z)?; + write.write_i32_be(self.x)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/update_advancement.rs b/pumpkin-protocol/src/java/client/play/update_advancement.rs index f340bff9d..94d54b0d7 100644 --- a/pumpkin-protocol/src/java/client/play/update_advancement.rs +++ b/pumpkin-protocol/src/java/client/play/update_advancement.rs @@ -1,82 +1,18 @@ -use crate::codec::item_stack_seralizer::ItemStackTemplateSerializer; use crate::codec::var_int::VarInt; use pumpkin_data::Advancement; -use pumpkin_data::advancement_data::{AdvancementDisplay, AdvancementProgressData}; +use pumpkin_data::advancement_data::AdvancementProgressData; use pumpkin_data::packet::clientbound::PLAY_UPDATE_ADVANCEMENTS; use pumpkin_macros::java_packet; use pumpkin_util::identifier::Identifier; -use serde::ser::SerializeStruct; -use serde::{Serialize, Serializer, ser::SerializeSeq}; -fn serialize_advancements( - advancements: &[&'static Advancement], - serializer: S, -) -> Result { - let mut seq = serializer.serialize_seq(Some(advancements.len()))?; - for adv in advancements { - seq.serialize_element(&AdvancementSer(adv))?; - } - seq.end() -} +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -pub struct AdvancementSer<'a>(pub &'a Advancement); - -impl Serialize for AdvancementSer<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let adv = self.0; - let mut state = serializer.serialize_struct("Advancement", 5)?; - state.serialize_field("id", &adv.id.to_string())?; - state.serialize_field("parent", &adv.parent.clone().map(|p| p.to_string()))?; - if let Some(display) = adv.display { - state.serialize_field("display", &Some(DisplaySerializer(display)))?; - } else { - state.serialize_field("display", &None::<&DisplaySerializer>)?; - } - state.serialize_field("requirements", adv.requirements)?; - state.serialize_field("send_telemetry", &adv.send_telemetry)?; - state.end() - } -} - -pub struct DisplaySerializer<'a>(pub &'a AdvancementDisplay); - -impl Serialize for DisplaySerializer<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let display = self.0; - let mut state = serializer.serialize_struct("AdvancementDisplay", 8)?; - - state.serialize_field("title", &display.get_title())?; - state.serialize_field("description", &display.get_description())?; - state.serialize_field( - "icon", - &ItemStackTemplateSerializer::from(display.item_icon.clone()), - )?; - state.serialize_field("frame_type", &VarInt(display.frame_type as i32))?; - let flags = (display.has_background() as i32) - | ((display.show_toast as i32) << 1) - | ((display.hidden as i32) << 2); - state.serialize_field("flags", &flags)?; - if let Some(bg) = display.background_texture { - state.serialize_field("background_texture", bg)?; - } - state.serialize_field("x", &display.x)?; - state.serialize_field("y", &display.y)?; - state.end() - } -} - -#[derive(Serialize)] #[java_packet(PLAY_UPDATE_ADVANCEMENTS)] #[allow(unused)] pub struct CUpdateAdvancements { pub reset: bool, - #[serde(serialize_with = "serialize_advancements")] pub added: Vec<&'static Advancement>, pub removed: Vec, pub progress: Vec, @@ -102,3 +38,82 @@ impl CUpdateAdvancements { } } } + +impl ClientPacket for CUpdateAdvancements { + #[allow(clippy::unimplemented)] + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_bool(self.reset)?; + + write.write_var_int(&VarInt(self.added.len() as i32))?; + for adv in &self.added { + write.write_string(&adv.id.to_string())?; + + let has_parent = adv.parent.is_some(); + write.write_bool(has_parent)?; + if let Some(ref p) = adv.parent { + write.write_string(&p.to_string())?; + } + + let has_display = adv.display.is_some(); + write.write_bool(has_display)?; + if let Some(display) = adv.display { + write.write_slice(&display.get_title().encode())?; + write.write_slice(&display.get_description().encode())?; + + // Item icon + // TODO: serialize item_icon (ItemStack) + unimplemented!("Item serialization is not yet implemented without serde"); + + /* + write.write_var_int(&VarInt(display.frame_type as i32))?; + let flags = (display.has_background() as i32) + | ((display.show_toast as i32) << 1) + | ((display.hidden as i32) << 2); + write.write_i32_be(flags)?; + if let Some(bg) = display.background_texture { + write.write_string(bg)?; + } + write.write_f32_be(display.x)?; + write.write_f32_be(display.y)?; + */ + } + + write.write_var_int(&VarInt(adv.requirements.len() as i32))?; + for req in adv.requirements { + write.write_var_int(&VarInt(req.len() as i32))?; + for r in *req { + write.write_string(r)?; + } + } + + write.write_bool(adv.send_telemetry)?; + } + + write.write_var_int(&VarInt(self.removed.len() as i32))?; + for rem in &self.removed { + write.write_string(&rem.to_string())?; + } + + write.write_var_int(&VarInt(self.progress.len() as i32))?; + for prog in &self.progress { + write.write_string(&prog.id.to_string())?; + write.write_var_int(&VarInt(prog.progress.len() as i32))?; + for crit in &prog.progress { + write.write_string(&crit.criterion_id)?; + let has_date = crit.achieve_date.is_some(); + write.write_bool(has_date)?; + if let Some(date) = crit.achieve_date { + write.write_i64_be(date)?; + } + } + } + + write.write_bool(self.show_advancements)?; + + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/update_attributes.rs b/pumpkin-protocol/src/java/client/play/update_attributes.rs index 657eb4000..17f0f1de3 100644 --- a/pumpkin-protocol/src/java/client/play/update_attributes.rs +++ b/pumpkin-protocol/src/java/client/play/update_attributes.rs @@ -1,24 +1,26 @@ use pumpkin_data::packet::clientbound::PLAY_UPDATE_ATTRIBUTES; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use crate::ClientPacket; use crate::codec::var_int::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] #[java_packet(PLAY_UPDATE_ATTRIBUTES)] pub struct CUpdateAttributes { pub entity_id: VarInt, pub properties: Vec, } -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Property { pub id: VarInt, pub value: f64, pub modifiers: Vec, } -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct AttributeModifier { pub id: String, pub amount: f64, @@ -56,3 +58,25 @@ impl AttributeModifier { } } } + +impl ClientPacket for CUpdateAttributes { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_var_int(&VarInt(self.properties.len() as i32))?; + for prop in &self.properties { + write.write_var_int(&prop.id)?; + write.write_f64(prop.value)?; + write.write_var_int(&VarInt(prop.modifiers.len() as i32))?; + for modifier in &prop.modifiers { + write.write_string(&modifier.id)?; + write.write_f64(modifier.amount)?; + write.write_u8(modifier.operation as u8)?; + } + } + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/update_entity_pos.rs b/pumpkin-protocol/src/java/client/play/update_entity_pos.rs index 0b7916bf5..29687a502 100644 --- a/pumpkin-protocol/src/java/client/play/update_entity_pos.rs +++ b/pumpkin-protocol/src/java/client/play/update_entity_pos.rs @@ -1,11 +1,12 @@ use pumpkin_data::packet::clientbound::PLAY_MOVE_ENTITY_POS; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_MOVE_ENTITY_POS)] pub struct CUpdateEntityPos { pub entity_id: VarInt, @@ -23,3 +24,18 @@ impl CUpdateEntityPos { } } } + +impl ClientPacket for CUpdateEntityPos { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_i16_be(self.delta.x)?; + write.write_i16_be(self.delta.y)?; + write.write_i16_be(self.delta.z)?; + write.write_bool(self.on_ground)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/update_entity_pos_rot.rs b/pumpkin-protocol/src/java/client/play/update_entity_pos_rot.rs index 258e1a8cc..729ec6e01 100644 --- a/pumpkin-protocol/src/java/client/play/update_entity_pos_rot.rs +++ b/pumpkin-protocol/src/java/client/play/update_entity_pos_rot.rs @@ -1,11 +1,12 @@ use pumpkin_data::packet::clientbound::PLAY_MOVE_ENTITY_POS_ROT; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_MOVE_ENTITY_POS_ROT)] pub struct CUpdateEntityPosRot { pub entity_id: VarInt, @@ -33,3 +34,20 @@ impl CUpdateEntityPosRot { } } } + +impl ClientPacket for CUpdateEntityPosRot { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_i16(self.delta.x)?; + write.write_i16(self.delta.y)?; + write.write_i16(self.delta.z)?; + write.write_u8(self.yaw)?; + write.write_u8(self.pitch)?; + write.write_bool(self.on_ground)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/update_entity_rot.rs b/pumpkin-protocol/src/java/client/play/update_entity_rot.rs index 321609557..730eed820 100644 --- a/pumpkin-protocol/src/java/client/play/update_entity_rot.rs +++ b/pumpkin-protocol/src/java/client/play/update_entity_rot.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_MOVE_ENTITY_ROT; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_MOVE_ENTITY_ROT)] pub struct CUpdateEntityRot { pub entity_id: VarInt, @@ -24,3 +25,17 @@ impl CUpdateEntityRot { } } } + +impl ClientPacket for CUpdateEntityRot { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_u8(self.yaw)?; + write.write_u8(self.pitch)?; + write.write_bool(self.on_ground)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/update_mob_effect.rs b/pumpkin-protocol/src/java/client/play/update_mob_effect.rs index 98239a133..0b7aa60ab 100644 --- a/pumpkin-protocol/src/java/client/play/update_mob_effect.rs +++ b/pumpkin-protocol/src/java/client/play/update_mob_effect.rs @@ -1,10 +1,11 @@ use pumpkin_data::packet::clientbound::PLAY_UPDATE_MOB_EFFECT; use pumpkin_macros::java_packet; -use serde::Serialize; +use crate::ClientPacket; use crate::codec::var_int::VarInt; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; -#[derive(Serialize)] #[java_packet(PLAY_UPDATE_MOB_EFFECT)] pub struct CUpdateMobEffect { pub entity_id: VarInt, @@ -32,3 +33,18 @@ impl CUpdateMobEffect { } } } + +impl ClientPacket for CUpdateMobEffect { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_var_int(&self.entity_id)?; + write.write_var_int(&self.effect_id)?; + write.write_var_int(&self.amplifier)?; + write.write_var_int(&self.duration)?; + write.write_i8(self.flags)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/play/update_score.rs b/pumpkin-protocol/src/java/client/play/update_score.rs index a93fd79b3..2dc9f3b9b 100644 --- a/pumpkin-protocol/src/java/client/play/update_score.rs +++ b/pumpkin-protocol/src/java/client/play/update_score.rs @@ -2,15 +2,16 @@ use pumpkin_data::packet::clientbound::PLAY_SET_SCORE; use pumpkin_util::text::TextComponent; use pumpkin_macros::java_packet; -use serde::Serialize; -use crate::{NumberFormat, VarInt}; +use crate::{ + ClientPacket, NumberFormat, VarInt, + ser::{NetworkWriteExt, WritingError}, +}; /// Sent by the server to create or update a score for an entity on a specific objective. /// /// This packet is the primary way to manage scoreboard data. In the latest protocol, /// it also supports optional custom formatting for how the numeric score is displayed. -#[derive(Serialize)] #[java_packet(PLAY_SET_SCORE)] pub struct CUpdateScore { /// The name of the entity whose score is being updated (e.g., a player's username @@ -57,3 +58,17 @@ impl CUpdateScore { } } } + +impl ClientPacket for CUpdateScore { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &pumpkin_util::version::JavaMinecraftVersion, + ) -> Result<(), WritingError> { + write.write_string(&self.entity_name)?; + write.write_string(&self.objective_name)?; + write.write_var_int(&self.value)?; + write.write_option(&self.display_name, |w, t| w.write_slice(&t.encode()))?; + write.write_option(&self.number_format, |w, n| n.write(w)) + } +} diff --git a/pumpkin-protocol/src/java/client/status/ping_response.rs b/pumpkin-protocol/src/java/client/status/ping_response.rs index d8e772de9..7bcd146da 100644 --- a/pumpkin-protocol/src/java/client/status/ping_response.rs +++ b/pumpkin-protocol/src/java/client/status/ping_response.rs @@ -1,12 +1,14 @@ use pumpkin_data::packet::clientbound::STATUS_PONG_RESPONSE; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server to complete a latency check initiated by a `SStatusPingRequest`. /// /// This is the final packet in the Server List Ping (SLP) sequence. It allows the /// client to calculate the round-trip time (ping) to the server. -#[derive(Serialize, Deserialize)] #[java_packet(STATUS_PONG_RESPONSE)] pub struct CPingResponse { /// The exact 64-bit integer received from the client's ping request. @@ -22,3 +24,14 @@ impl CPingResponse { Self { payload } } } + +impl ClientPacket for CPingResponse { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_i64(self.payload)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/client/status/status_response.rs b/pumpkin-protocol/src/java/client/status/status_response.rs index b54de8f5c..96e9917cb 100644 --- a/pumpkin-protocol/src/java/client/status/status_response.rs +++ b/pumpkin-protocol/src/java/client/status/status_response.rs @@ -1,12 +1,14 @@ use pumpkin_data::packet::clientbound::STATUS_STATUS_RESPONSE; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; + +use crate::ClientPacket; +use crate::ser::NetworkWriteExt; +use pumpkin_util::version::JavaMinecraftVersion; /// Sent by the server in response to a `SStatusRequest`. /// /// This packet provides the client with the information required to display the /// server in the multiplayer menu, including the MOTD, player count, and icon -#[derive(Serialize, Deserialize)] #[java_packet(STATUS_STATUS_RESPONSE)] pub struct CStatusResponse { /// A JSON-encoded string containing the server's status data. @@ -21,3 +23,14 @@ impl CStatusResponse { Self { json_response } } } + +impl ClientPacket for CStatusResponse { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::ser::WritingError> { + write.write_string(&self.json_response)?; + Ok(()) + } +} diff --git a/pumpkin-protocol/src/java/packet_encoder.rs b/pumpkin-protocol/src/java/packet_encoder.rs index d92f187d1..dea5041ca 100644 --- a/pumpkin-protocol/src/java/packet_encoder.rs +++ b/pumpkin-protocol/src/java/packet_encoder.rs @@ -349,10 +349,8 @@ mod tests { use pumpkin_data::packet::clientbound::STATUS_STATUS_RESPONSE; use pumpkin_macros::java_packet; use pumpkin_util::version::JavaMinecraftVersion; - use serde::Serialize; /// Define a custom packet for testing maximum packet size - #[derive(Serialize)] #[java_packet(STATUS_STATUS_RESPONSE)] pub struct MaxSizePacket { data: Vec, @@ -366,6 +364,19 @@ mod tests { } } + impl ClientPacket for MaxSizePacket { + fn write_packet_data( + &self, + mut write: impl std::io::Write, + _version: &JavaMinecraftVersion, + ) -> Result<(), crate::WritingError> { + write + .write_all(&self.data) + .map_err(crate::WritingError::IoError)?; + Ok(()) + } + } + /// Helper function to decode a `VarInt` from bytes fn decode_varint(buffer: &mut &[u8]) -> Result { Ok(buffer.get_var_int()?.0) diff --git a/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs b/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs index eaa122bf8..bcb2ecc5b 100644 --- a/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs +++ b/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs @@ -1,9 +1,17 @@ use pumpkin_data::packet::serverbound::CONFIG_FINISH_CONFIGURATION; use pumpkin_macros::java_packet; -use serde::Serialize; + +use crate::{ServerPacket, ser::ReadingError}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; /// This packet signals to the server that the client is ready to transition /// from the `Configuration` state to the `Play` state. -#[derive(Serialize)] #[java_packet(CONFIG_FINISH_CONFIGURATION)] pub struct SAcknowledgeFinishConfig; + +impl ServerPacket for SAcknowledgeFinishConfig { + fn read(_bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self) + } +} diff --git a/pumpkin-protocol/src/java/server/config/client_information.rs b/pumpkin-protocol/src/java/server/config/client_information.rs index b96be1b2c..624941e88 100644 --- a/pumpkin-protocol/src/java/server/config/client_information.rs +++ b/pumpkin-protocol/src/java/server/config/client_information.rs @@ -1,11 +1,16 @@ use pumpkin_data::packet::serverbound::CONFIG_CLIENT_INFORMATION; use pumpkin_macros::java_packet; -use serde::Serialize; use crate::VarInt; +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + /// Sent by the client to inform the server about its local settings -#[derive(serde::Deserialize, Serialize)] #[java_packet(CONFIG_CLIENT_INFORMATION)] pub struct SClientInformationConfig { /// The language code used by the client (e.g., "`en_us`") @@ -25,3 +30,18 @@ pub struct SClientInformationConfig { /// Whether the player should appear in the server's online player list pub server_listing: bool, } + +impl ServerPacket for SClientInformationConfig { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + locale: bytebuf.get_str()?.into_string(), + view_distance: bytebuf.get_i8()?, + chat_mode: bytebuf.get_var_int()?, + chat_colors: bytebuf.get_bool()?, + skin_parts: bytebuf.get_u8()?, + main_hand: bytebuf.get_var_int()?, + text_filtering: bytebuf.get_bool()?, + server_listing: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/config/custom_click_action.rs b/pumpkin-protocol/src/java/server/config/custom_click_action.rs index 319817782..b44d6f95d 100644 --- a/pumpkin-protocol/src/java/server/config/custom_click_action.rs +++ b/pumpkin-protocol/src/java/server/config/custom_click_action.rs @@ -1,11 +1,25 @@ use pumpkin_data::packet::serverbound::CONFIG_CUSTOM_CLICK_ACTION; use pumpkin_macros::java_packet; use pumpkin_util::resource_location::ResourceLocation; -use serde::Deserialize; -#[derive(Deserialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(CONFIG_CUSTOM_CLICK_ACTION)] pub struct SCustomClickAction { pub action_id: ResourceLocation, pub payload: Option>, } + +impl ServerPacket for SCustomClickAction { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + action_id: bytebuf.get_str()?.into_string(), + payload: bytebuf.get_option(|v| v.read_remaining_to_boxed_slice(32767))?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/config/keep_alive.rs b/pumpkin-protocol/src/java/server/config/keep_alive.rs index d5472b957..fae25aa70 100644 --- a/pumpkin-protocol/src/java/server/config/keep_alive.rs +++ b/pumpkin-protocol/src/java/server/config/keep_alive.rs @@ -1,9 +1,22 @@ use pumpkin_data::packet::serverbound::CONFIG_KEEP_ALIVE; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(CONFIG_KEEP_ALIVE)] pub struct SKeepAlive { pub keep_alive_id: i64, } + +impl ServerPacket for SKeepAlive { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + keep_alive_id: bytebuf.get_i64_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/config/known_packs.rs b/pumpkin-protocol/src/java/server/config/known_packs.rs index 26057c3c4..6e2756e29 100644 --- a/pumpkin-protocol/src/java/server/config/known_packs.rs +++ b/pumpkin-protocol/src/java/server/config/known_packs.rs @@ -1,12 +1,25 @@ use pumpkin_data::packet::serverbound::CONFIG_SELECT_KNOWN_PACKS; use pumpkin_macros::java_packet; -use serde::Serialize; use crate::VarInt; -#[derive(serde::Deserialize, Serialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(CONFIG_SELECT_KNOWN_PACKS)] pub struct SKnownPacks { pub known_pack_count: VarInt, // known_packs: &'a [KnownPack] } + +impl ServerPacket for SKnownPacks { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + known_pack_count: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/config/resource_pack_response.rs b/pumpkin-protocol/src/java/server/config/resource_pack_response.rs index 796f20c3c..0017ba185 100644 --- a/pumpkin-protocol/src/java/server/config/resource_pack_response.rs +++ b/pumpkin-protocol/src/java/server/config/resource_pack_response.rs @@ -1,6 +1,12 @@ use pumpkin_data::packet::serverbound::CONFIG_RESOURCE_PACK; use pumpkin_macros::java_packet; -use serde::Serialize; + +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; @@ -20,16 +26,23 @@ pub enum ResourcePackResponseResult { /// /// This allows the server to know if the player is using the required textures /// or if the download failed. -#[derive(serde::Deserialize, Serialize)] #[java_packet(CONFIG_RESOURCE_PACK)] pub struct SConfigResourcePack { /// The unique identifier of the resource pack this response refers to. - #[serde(with = "uuid::serde::compact")] pub uuid: uuid::Uuid, /// The status code of the operation, mapped to [`ResourcePackResponseResult`]. pub result: VarInt, } +impl ServerPacket for SConfigResourcePack { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + uuid: bytebuf.get_uuid()?, + result: bytebuf.get_var_int()?, + }) + } +} + impl SConfigResourcePack { #[must_use] pub const fn response_result(&self) -> ResourcePackResponseResult { diff --git a/pumpkin-protocol/src/java/server/login/login_response.rs b/pumpkin-protocol/src/java/server/login/login_response.rs index cb2fda05f..5a8dab410 100644 --- a/pumpkin-protocol/src/java/server/login/login_response.rs +++ b/pumpkin-protocol/src/java/server/login/login_response.rs @@ -1,8 +1,16 @@ use pumpkin_data::packet::serverbound::LOGIN_LOGIN_ACKNOWLEDGED; use pumpkin_macros::java_packet; -use serde::Serialize; + +use crate::{ServerPacket, ser::ReadingError}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; /// Acknowledgement to the `CLoginSuccess` packet sent by the server. -#[derive(Serialize)] #[java_packet(LOGIN_LOGIN_ACKNOWLEDGED)] pub struct SLoginAcknowledged; + +impl ServerPacket for SLoginAcknowledged { + fn read(_bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self) + } +} diff --git a/pumpkin-protocol/src/java/server/login/login_start.rs b/pumpkin-protocol/src/java/server/login/login_start.rs index 10398b272..7660d6b0f 100644 --- a/pumpkin-protocol/src/java/server/login/login_start.rs +++ b/pumpkin-protocol/src/java/server/login/login_start.rs @@ -3,7 +3,6 @@ use std::io::Read; use pumpkin_data::packet::serverbound::LOGIN_HELLO; use pumpkin_macros::java_packet; use pumpkin_util::version::JavaMinecraftVersion; -use serde::Serialize; use crate::{ ServerPacket, @@ -11,7 +10,6 @@ use crate::{ }; #[java_packet(LOGIN_HELLO)] -#[derive(Serialize)] pub struct SLoginStart { pub name: Box, // 16 pub uuid: uuid::Uuid, diff --git a/pumpkin-protocol/src/java/server/play/attack.rs b/pumpkin-protocol/src/java/server/play/attack.rs index a9385a092..4f1e2b3f8 100644 --- a/pumpkin-protocol/src/java/server/play/attack.rs +++ b/pumpkin-protocol/src/java/server/play/attack.rs @@ -1,11 +1,26 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_ATTACK; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::codec::var_int::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_ATTACK)] pub struct SAttack { pub entity_id: VarInt, } + +impl ServerPacket for SAttack { + fn read( + mut bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self { + entity_id: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs b/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs index 891bf5a06..b62ce0f04 100644 --- a/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs +++ b/pumpkin-protocol/src/java/server/play/bundle_item_selected.rs @@ -1,12 +1,26 @@ use pumpkin_data::packet::serverbound::PLAY_BUNDLE_ITEM_SELECTED; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; use crate::VarInt; -#[derive(Deserialize, Serialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_BUNDLE_ITEM_SELECTED)] pub struct SBundleItemSelected { pub slot_id: VarInt, pub selected_item_index: VarInt, } + +impl ServerPacket for SBundleItemSelected { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + slot_id: bytebuf.get_var_int()?, + selected_item_index: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/change_game_mode.rs b/pumpkin-protocol/src/java/server/play/change_game_mode.rs index 7630221db..a02705ca9 100644 --- a/pumpkin-protocol/src/java/server/play/change_game_mode.rs +++ b/pumpkin-protocol/src/java/server/play/change_game_mode.rs @@ -1,10 +1,23 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_CHANGE_GAME_MODE; use pumpkin_macros::java_packet; use pumpkin_util::GameMode; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(serde::Deserialize, Serialize)] #[java_packet(PLAY_CHANGE_GAME_MODE)] pub struct SChangeGameMode { pub game_mode: GameMode, } + +impl ServerPacket for SChangeGameMode { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + game_mode: GameMode::try_from(bytebuf.get_u8()? as i8) + .map_err(|()| crate::ser::ReadingError::Message("Invalid game mode".into()))?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/chat_command.rs b/pumpkin-protocol/src/java/server/play/chat_command.rs index 4afd9647e..80adad496 100644 --- a/pumpkin-protocol/src/java/server/play/chat_command.rs +++ b/pumpkin-protocol/src/java/server/play/chat_command.rs @@ -1,9 +1,22 @@ use pumpkin_data::packet::serverbound::PLAY_CHAT_COMMAND; use pumpkin_macros::java_packet; -use serde::Serialize; -#[derive(serde::Deserialize, Serialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_CHAT_COMMAND)] pub struct SChatCommand { pub command: String, } + +impl ServerPacket for SChatCommand { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + command: bytebuf.get_str()?.into_string(), + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/chunk_batch.rs b/pumpkin-protocol/src/java/server/play/chunk_batch.rs index e6860c18b..a2d2baa90 100644 --- a/pumpkin-protocol/src/java/server/play/chunk_batch.rs +++ b/pumpkin-protocol/src/java/server/play/chunk_batch.rs @@ -1,9 +1,21 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_CHUNK_BATCH_RECEIVED; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize)] #[java_packet(PLAY_CHUNK_BATCH_RECEIVED)] pub struct SChunkBatch { pub chunks_per_tick: f32, } + +impl ServerPacket for SChunkBatch { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + chunks_per_tick: bytebuf.get_f32_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/click_container.rs b/pumpkin-protocol/src/java/server/play/click_container.rs index 5ad059c24..fd996bdba 100644 --- a/pumpkin-protocol/src/java/server/play/click_container.rs +++ b/pumpkin-protocol/src/java/server/play/click_container.rs @@ -1,9 +1,13 @@ use crate::VarInt; use crate::codec::item_stack_seralizer::OptionalItemStackHash; +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_CONTAINER_CLICK; use pumpkin_macros::java_packet; -use serde::de::SeqAccess; -use serde::{Deserialize, de}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; #[derive(Debug)] #[java_packet(PLAY_CONTAINER_CLICK)] @@ -18,72 +22,39 @@ pub struct SClickSlot { pub carried_item: OptionalItemStackHash, } -impl<'de> Deserialize<'de> for SClickSlot { - fn deserialize>(deserializer: D) -> Result { - struct Visitor; - impl<'de> de::Visitor<'de> for Visitor { - type Value = SClickSlot; +impl ServerPacket for SClickSlot { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + let sync_id = bytebuf.get_var_int()?; + let revision = bytebuf.get_var_int()?; + let slot = bytebuf.get_i16_be()?; + let button = bytebuf.get_i8()?; + let mode = SlotActionType::read(&mut bytebuf)?; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") - } - - fn visit_seq>(self, mut seq: A) -> Result { - let sync_id = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode u8"))?; - let revision = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode VarInt"))?; - - let slot = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode i16"))?; - let button = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode i8"))?; - let mode = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode VarInt"))?; - let length_of_array = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode VarInt"))?; - let mut array_of_changed_slots = vec![]; - for _ in 0..length_of_array.0 { - let slot_number = seq - .next_element::()? - .ok_or(de::Error::custom("Unable to parse slot"))?; - let slot = seq - .next_element::()? - .ok_or(de::Error::custom("Unable to parse item"))?; - array_of_changed_slots.push((slot_number, slot)); - } - - let carried_item = seq - .next_element::()? - .ok_or(de::Error::custom("Failed to decode carried item"))?; - - let mode: SlotActionType = SlotActionType::try_from(mode.0) - .map_err(|_| de::Error::custom("Invalid slot action type"))?; - - Ok(SClickSlot { - sync_id, - revision, - slot, - button, - mode, - length_of_array, - array_of_changed_slots, - carried_item, - }) - } + let length_of_array = bytebuf.get_var_int()?; + let mut array_of_changed_slots = Vec::with_capacity(length_of_array.0 as usize); + for _ in 0..length_of_array.0 { + array_of_changed_slots.push(( + bytebuf.get_i16_be()?, + OptionalItemStackHash::read(&mut bytebuf)?, + )); } - deserializer.deserialize_seq(Visitor) + let carried_item = OptionalItemStackHash::read(&mut bytebuf)?; + + Ok(Self { + sync_id, + revision, + slot, + button, + mode, + length_of_array, + array_of_changed_slots, + carried_item, + }) } } -#[derive(Deserialize, Debug, PartialEq, Eq, Clone)] +#[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. Pickup, @@ -104,6 +75,14 @@ pub enum SlotActionType { PickupAll, } +impl SlotActionType { + pub fn read(bytebuf: &mut impl Read) -> Result { + let mode = bytebuf.get_var_int()?; + Self::try_from(mode.0) + .map_err(|_| ReadingError::Message("Invalid slot action type".to_string())) + } +} + #[derive(Debug)] pub struct InvalidSlotActionType; diff --git a/pumpkin-protocol/src/java/server/play/client_command.rs b/pumpkin-protocol/src/java/server/play/client_command.rs index fd95c6541..9d3380c8c 100644 --- a/pumpkin-protocol/src/java/server/play/client_command.rs +++ b/pumpkin-protocol/src/java/server/play/client_command.rs @@ -1,11 +1,23 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_CLIENT_COMMAND; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_CLIENT_COMMAND)] pub struct SClientCommand { pub action_id: VarInt, } + +impl ServerPacket for SClientCommand { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + action_id: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/client_information.rs b/pumpkin-protocol/src/java/server/play/client_information.rs index 27a794247..c651486f0 100644 --- a/pumpkin-protocol/src/java/server/play/client_information.rs +++ b/pumpkin-protocol/src/java/server/play/client_information.rs @@ -1,10 +1,14 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_CLIENT_INFORMATION; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(serde::Deserialize, Serialize)] #[java_packet(PLAY_CLIENT_INFORMATION)] pub struct SClientInformationPlay { pub locale: String, // 16 @@ -16,3 +20,18 @@ pub struct SClientInformationPlay { pub text_filtering: bool, pub server_listing: bool, } + +impl ServerPacket for SClientInformationPlay { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + locale: bytebuf.get_str()?.to_string(), + view_distance: bytebuf.get_i8()?, + chat_mode: bytebuf.get_var_int()?, + chat_colors: bytebuf.get_bool()?, + skin_parts: bytebuf.get_u8()?, + main_hand: bytebuf.get_var_int()?, + text_filtering: bytebuf.get_bool()?, + server_listing: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/close_container.rs b/pumpkin-protocol/src/java/server/play/close_container.rs index 3dd86501a..b47dca883 100644 --- a/pumpkin-protocol/src/java/server/play/close_container.rs +++ b/pumpkin-protocol/src/java/server/play/close_container.rs @@ -1,11 +1,23 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_CONTAINER_CLOSE; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_CONTAINER_CLOSE)] pub struct SCloseContainer { pub window_id: VarInt, } + +impl ServerPacket for SCloseContainer { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + window_id: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/command_suggestion.rs b/pumpkin-protocol/src/java/server/play/command_suggestion.rs index 3bedd70bb..55b7e4275 100644 --- a/pumpkin-protocol/src/java/server/play/command_suggestion.rs +++ b/pumpkin-protocol/src/java/server/play/command_suggestion.rs @@ -1,12 +1,26 @@ use pumpkin_data::packet::serverbound::PLAY_COMMAND_SUGGESTION; use pumpkin_macros::java_packet; -use serde::Deserialize; use crate::VarInt; -#[derive(Deserialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_COMMAND_SUGGESTION)] pub struct SCommandSuggestion { pub id: VarInt, pub command: String, } + +impl ServerPacket for SCommandSuggestion { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + id: bytebuf.get_var_int()?, + command: bytebuf.get_str()?.into_string(), + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/confirm_teleport.rs b/pumpkin-protocol/src/java/server/play/confirm_teleport.rs index f9effbc02..5bed271bc 100644 --- a/pumpkin-protocol/src/java/server/play/confirm_teleport.rs +++ b/pumpkin-protocol/src/java/server/play/confirm_teleport.rs @@ -1,11 +1,23 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_ACCEPT_TELEPORTATION; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(serde::Deserialize, Serialize)] #[java_packet(PLAY_ACCEPT_TELEPORTATION)] pub struct SConfirmTeleport { pub teleport_id: VarInt, } + +impl ServerPacket for SConfirmTeleport { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + teleport_id: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/container_button_click.rs b/pumpkin-protocol/src/java/server/play/container_button_click.rs index d4ef53d54..9504438c1 100644 --- a/pumpkin-protocol/src/java/server/play/container_button_click.rs +++ b/pumpkin-protocol/src/java/server/play/container_button_click.rs @@ -1,11 +1,26 @@ use crate::VarInt; use pumpkin_data::packet::serverbound::PLAY_CONTAINER_BUTTON_CLICK; use pumpkin_macros::java_packet; -use serde::Deserialize; -#[derive(Deserialize, Debug)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + +#[derive(Debug)] #[java_packet(PLAY_CONTAINER_BUTTON_CLICK)] pub struct SContainerButtonClick { pub window_id: VarInt, pub button_id: VarInt, } + +impl ServerPacket for SContainerButtonClick { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + window_id: bytebuf.get_var_int()?, + button_id: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/custom_click_action.rs b/pumpkin-protocol/src/java/server/play/custom_click_action.rs index 3cad233c0..2e7dc1490 100644 --- a/pumpkin-protocol/src/java/server/play/custom_click_action.rs +++ b/pumpkin-protocol/src/java/server/play/custom_click_action.rs @@ -1,11 +1,24 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_CUSTOM_CLICK_ACTION; use pumpkin_macros::java_packet; use pumpkin_util::resource_location::ResourceLocation; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize)] #[java_packet(PLAY_CUSTOM_CLICK_ACTION)] pub struct SCustomClickAction { pub action_id: ResourceLocation, pub payload: Option>, } + +impl ServerPacket for SCustomClickAction { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + action_id: bytebuf.get_str()?.to_string(), + payload: bytebuf.get_option(|b| b.read_remaining_to_boxed_slice(32767))?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs b/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs index 17041606b..0ff8aff9a 100644 --- a/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs +++ b/pumpkin-protocol/src/java/server/play/jigsaw_generate.rs @@ -1,14 +1,31 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_JIGSAW_GENERATE; use pumpkin_macros::java_packet; use pumpkin_util::math::position::BlockPos; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::codec::var_int::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_JIGSAW_GENERATE)] pub struct SJigsawGenerate { pub pos: BlockPos, pub levels: VarInt, pub keep_jigsaws: bool, } + +impl ServerPacket for SJigsawGenerate { + fn read( + mut bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self { + pos: BlockPos::from_i64(bytebuf.get_i64_be()?), + levels: bytebuf.get_var_int()?, + keep_jigsaws: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/keep_alive.rs b/pumpkin-protocol/src/java/server/play/keep_alive.rs index d86c3d96d..3680758cc 100644 --- a/pumpkin-protocol/src/java/server/play/keep_alive.rs +++ b/pumpkin-protocol/src/java/server/play/keep_alive.rs @@ -1,9 +1,22 @@ use pumpkin_data::packet::serverbound::PLAY_KEEP_ALIVE; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_KEEP_ALIVE)] pub struct SKeepAlive { pub keep_alive_id: i64, } + +impl ServerPacket for SKeepAlive { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + keep_alive_id: bytebuf.get_i64_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/move_vehicle.rs b/pumpkin-protocol/src/java/server/play/move_vehicle.rs index d4ab59da6..531036f36 100644 --- a/pumpkin-protocol/src/java/server/play/move_vehicle.rs +++ b/pumpkin-protocol/src/java/server/play/move_vehicle.rs @@ -1,8 +1,12 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_MOVE_VEHICLE; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize)] #[java_packet(PLAY_MOVE_VEHICLE)] pub struct SMoveVehicle { pub x: f64, @@ -11,3 +15,18 @@ pub struct SMoveVehicle { pub yaw: f32, pub pitch: f32, } + +impl ServerPacket for SMoveVehicle { + fn read( + mut bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self { + x: bytebuf.get_f64_be()?, + y: bytebuf.get_f64_be()?, + z: bytebuf.get_f64_be()?, + yaw: bytebuf.get_f32_be()?, + pitch: bytebuf.get_f32_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/paddle_boat.rs b/pumpkin-protocol/src/java/server/play/paddle_boat.rs index 284b0ea64..d03361d6b 100644 --- a/pumpkin-protocol/src/java/server/play/paddle_boat.rs +++ b/pumpkin-protocol/src/java/server/play/paddle_boat.rs @@ -1,10 +1,24 @@ use pumpkin_data::packet::serverbound::PLAY_PADDLE_BOAT; use pumpkin_macros::java_packet; -use serde::Deserialize; -#[derive(Deserialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_PADDLE_BOAT)] pub struct SPaddleBoat { pub left_paddle: bool, pub right_paddle: bool, } + +impl ServerPacket for SPaddleBoat { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + left_paddle: bytebuf.get_bool()?, + right_paddle: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/pick_item.rs b/pumpkin-protocol/src/java/server/play/pick_item.rs index a52614da0..fd82988de 100644 --- a/pumpkin-protocol/src/java/server/play/pick_item.rs +++ b/pumpkin-protocol/src/java/server/play/pick_item.rs @@ -1,20 +1,41 @@ use pumpkin_data::packet::serverbound::{PLAY_PICK_ITEM_FROM_BLOCK, PLAY_PICK_ITEM_FROM_ENTITY}; use pumpkin_macros::java_packet; use pumpkin_util::math::position::BlockPos; -use serde::Deserialize; use crate::codec::var_int::VarInt; +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize)] #[java_packet(PLAY_PICK_ITEM_FROM_BLOCK)] pub struct SPickItemFromBlock { pub pos: BlockPos, pub include_data: bool, } -#[derive(Deserialize)] +impl ServerPacket for SPickItemFromBlock { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + pos: BlockPos::from_i64(bytebuf.get_i64_be()?), + include_data: bytebuf.get_bool()?, + }) + } +} + #[java_packet(PLAY_PICK_ITEM_FROM_ENTITY)] pub struct SPickItemFromEntity { pub id: VarInt, pub include_data: bool, } + +impl ServerPacket for SPickItemFromEntity { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + id: bytebuf.get_var_int()?, + include_data: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/ping_request.rs b/pumpkin-protocol/src/java/server/play/ping_request.rs index b03ef8eb2..e30616c95 100644 --- a/pumpkin-protocol/src/java/server/play/ping_request.rs +++ b/pumpkin-protocol/src/java/server/play/ping_request.rs @@ -1,9 +1,22 @@ use pumpkin_data::packet::serverbound::PLAY_PING_REQUEST; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_PING_REQUEST)] pub struct SPlayPingRequest { pub payload: i64, } + +impl ServerPacket for SPlayPingRequest { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + payload: bytebuf.get_i64_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/place_recipe.rs b/pumpkin-protocol/src/java/server/play/place_recipe.rs index f94b0358e..c41e2f774 100644 --- a/pumpkin-protocol/src/java/server/play/place_recipe.rs +++ b/pumpkin-protocol/src/java/server/play/place_recipe.rs @@ -1,13 +1,27 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_PLACE_RECIPE; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_PLACE_RECIPE)] pub struct SPlaceRecipe { pub container_id: i8, pub recipe_display_id: VarInt, pub use_max_items: bool, } + +impl ServerPacket for SPlaceRecipe { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + container_id: bytebuf.get_i8()?, + recipe_display_id: bytebuf.get_var_int()?, + use_max_items: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/player_abilities.rs b/pumpkin-protocol/src/java/server/play/player_abilities.rs index 362ede2fa..768c36685 100644 --- a/pumpkin-protocol/src/java/server/play/player_abilities.rs +++ b/pumpkin-protocol/src/java/server/play/player_abilities.rs @@ -1,11 +1,24 @@ use pumpkin_data::packet::serverbound::PLAY_PLAYER_ABILITIES; use pumpkin_macros::java_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)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_PLAYER_ABILITIES)] pub struct SPlayerAbilities { pub flags: i8, } + +impl ServerPacket for SPlayerAbilities { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + flags: bytebuf.get_i8()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/player_action.rs b/pumpkin-protocol/src/java/server/play/player_action.rs index 799342b9a..d1047fd39 100644 --- a/pumpkin-protocol/src/java/server/play/player_action.rs +++ b/pumpkin-protocol/src/java/server/play/player_action.rs @@ -1,10 +1,15 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_PLAYER_ACTION; use pumpkin_macros::java_packet; use pumpkin_util::math::position::BlockPos; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(serde::Deserialize)] #[java_packet(PLAY_PLAYER_ACTION)] pub struct SPlayerAction { pub status: VarInt, @@ -13,6 +18,20 @@ pub struct SPlayerAction { pub sequence: VarInt, } +impl ServerPacket for SPlayerAction { + fn read( + mut bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self { + status: bytebuf.get_var_int()?, + position: BlockPos::from_i64(bytebuf.get_i64_be()?), + face: bytebuf.get_u8()?, + sequence: bytebuf.get_var_int()?, + }) + } +} + #[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/pumpkin-protocol/src/java/server/play/player_ground.rs b/pumpkin-protocol/src/java/server/play/player_ground.rs index c42c3d741..181ea06c0 100644 --- a/pumpkin-protocol/src/java/server/play/player_ground.rs +++ b/pumpkin-protocol/src/java/server/play/player_ground.rs @@ -1,9 +1,21 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_STATUS_ONLY; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_MOVE_PLAYER_STATUS_ONLY)] pub struct SSetPlayerGround { pub on_ground: bool, } + +impl ServerPacket for SSetPlayerGround { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + on_ground: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/player_input.rs b/pumpkin-protocol/src/java/server/play/player_input.rs index 5281bb394..889802879 100644 --- a/pumpkin-protocol/src/java/server/play/player_input.rs +++ b/pumpkin-protocol/src/java/server/play/player_input.rs @@ -1,8 +1,12 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_PLAYER_INPUT; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_PLAYER_INPUT)] pub struct SPlayerInput { // Yep, exactly how it looks like @@ -18,3 +22,11 @@ impl SPlayerInput { pub const SNEAK: i8 = 32; pub const SPRINT: i8 = 64; } + +impl ServerPacket for SPlayerInput { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + input: bytebuf.get_i8()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/player_loaded.rs b/pumpkin-protocol/src/java/server/play/player_loaded.rs index fd3d2587b..6f6bb8ad2 100644 --- a/pumpkin-protocol/src/java/server/play/player_loaded.rs +++ b/pumpkin-protocol/src/java/server/play/player_loaded.rs @@ -1,7 +1,17 @@ +use crate::{ServerPacket, ser::ReadingError}; use pumpkin_data::packet::serverbound::PLAY_PLAYER_LOADED; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Serialize, Deserialize)] #[java_packet(PLAY_PLAYER_LOADED)] pub struct SPlayerLoaded; + +impl ServerPacket for SPlayerLoaded { + fn read( + _bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self) + } +} diff --git a/pumpkin-protocol/src/java/server/play/player_position.rs b/pumpkin-protocol/src/java/server/play/player_position.rs index d5c06ce98..e27a5f7bc 100644 --- a/pumpkin-protocol/src/java/server/play/player_position.rs +++ b/pumpkin-protocol/src/java/server/play/player_position.rs @@ -1,12 +1,30 @@ use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_POS; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; -use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_MOVE_PLAYER_POS)] pub struct SPlayerPosition { pub position: Vector3, /// bit 0: [`FLAG_ON_GROUND`], bit 1: [`FLAG_IN_WALL`] pub collision: u8, } + +impl ServerPacket for SPlayerPosition { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + position: Vector3::new( + bytebuf.get_f64_be()?, + bytebuf.get_f64_be()?, + bytebuf.get_f64_be()?, + ), + collision: bytebuf.get_u8()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/player_position_rotation.rs b/pumpkin-protocol/src/java/server/play/player_position_rotation.rs index 5d4728eb8..4c47990c9 100644 --- a/pumpkin-protocol/src/java/server/play/player_position_rotation.rs +++ b/pumpkin-protocol/src/java/server/play/player_position_rotation.rs @@ -2,10 +2,16 @@ use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_POS_ROT; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + pub const FLAG_ON_GROUND: u8 = 0x01; pub const FLAG_IN_WALL: u8 = 0x02; -#[derive(serde::Deserialize)] #[java_packet(PLAY_MOVE_PLAYER_POS_ROT)] pub struct SPlayerPositionRotation { pub position: Vector3, @@ -14,3 +20,18 @@ pub struct SPlayerPositionRotation { /// bit 0: [`FLAG_ON_GROUND`], bit 1: [`FLAG_IN_WALL`] pub collision: u8, } + +impl ServerPacket for SPlayerPositionRotation { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + position: Vector3::new( + bytebuf.get_f64_be()?, + bytebuf.get_f64_be()?, + bytebuf.get_f64_be()?, + ), + yaw: bytebuf.get_f32_be()?, + pitch: bytebuf.get_f32_be()?, + collision: bytebuf.get_u8()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/player_rotation.rs b/pumpkin-protocol/src/java/server/play/player_rotation.rs index 802d4802a..70a07ad83 100644 --- a/pumpkin-protocol/src/java/server/play/player_rotation.rs +++ b/pumpkin-protocol/src/java/server/play/player_rotation.rs @@ -1,11 +1,25 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_ROT; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_MOVE_PLAYER_ROT)] pub struct SPlayerRotation { pub yaw: f32, pub pitch: f32, pub ground: bool, } + +impl ServerPacket for SPlayerRotation { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + yaw: bytebuf.get_f32_be()?, + pitch: bytebuf.get_f32_be()?, + ground: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs b/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs index 055e272a1..1e503bbe5 100644 --- a/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs +++ b/pumpkin-protocol/src/java/server/play/recipe_book_change_settings.rs @@ -1,13 +1,27 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_RECIPE_BOOK_CHANGE_SETTINGS; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_RECIPE_BOOK_CHANGE_SETTINGS)] pub struct SRecipeBookChangeSettings { pub book_type: VarInt, pub is_open: bool, pub is_filtering: bool, } + +impl ServerPacket for SRecipeBookChangeSettings { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + book_type: bytebuf.get_var_int()?, + is_open: bytebuf.get_bool()?, + is_filtering: bytebuf.get_bool()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs b/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs index 1a03b893f..328ec5832 100644 --- a/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs +++ b/pumpkin-protocol/src/java/server/play/recipe_book_seen_recipe.rs @@ -1,11 +1,23 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_RECIPE_BOOK_SEEN_RECIPE; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_RECIPE_BOOK_SEEN_RECIPE)] pub struct SRecipeBookSeenRecipe { pub recipe_display_id: VarInt, } + +impl ServerPacket for SRecipeBookSeenRecipe { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + recipe_display_id: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/rename_item.rs b/pumpkin-protocol/src/java/server/play/rename_item.rs index 1e1e4159a..9fc0b2a06 100644 --- a/pumpkin-protocol/src/java/server/play/rename_item.rs +++ b/pumpkin-protocol/src/java/server/play/rename_item.rs @@ -1,9 +1,22 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_RENAME_ITEM; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize, Debug)] +#[derive(Debug)] #[java_packet(PLAY_RENAME_ITEM)] pub struct SRenameItem { pub item_name: String, } + +impl ServerPacket for SRenameItem { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + item_name: bytebuf.get_str_bounded(32767)?.into_string(), + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/seen_advancement.rs b/pumpkin-protocol/src/java/server/play/seen_advancement.rs index d6fc91c43..c2f0b439a 100644 --- a/pumpkin-protocol/src/java/server/play/seen_advancement.rs +++ b/pumpkin-protocol/src/java/server/play/seen_advancement.rs @@ -1,11 +1,35 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_SEEN_ADVANCEMENTS; use pumpkin_macros::java_packet; use pumpkin_util::identifier::Identifier; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize, Debug)] +#[derive(Debug)] #[java_packet(PLAY_SEEN_ADVANCEMENTS)] pub enum SSeenAdvancement { OpenTab(Identifier), CloseTab, } + +impl ServerPacket for SSeenAdvancement { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + let action = bytebuf.get_var_int()?; + match action.0 { + 0 => { + let id_str = bytebuf.get_str()?; + let id = + Identifier::parse(&id_str).map_err(|e| ReadingError::Message(e.to_string()))?; + Ok(Self::OpenTab(id)) + } + 1 => Ok(Self::CloseTab), + _ => Err(ReadingError::Message(format!( + "Invalid SeenAdvancement action: {}", + action.0 + ))), + } + } +} diff --git a/pumpkin-protocol/src/java/server/play/select_trade.rs b/pumpkin-protocol/src/java/server/play/select_trade.rs index d500f54db..467f5323f 100644 --- a/pumpkin-protocol/src/java/server/play/select_trade.rs +++ b/pumpkin-protocol/src/java/server/play/select_trade.rs @@ -1,11 +1,24 @@ use pumpkin_data::packet::serverbound::PLAY_SELECT_TRADE; use pumpkin_macros::java_packet; -use serde::Deserialize; use crate::VarInt; -#[derive(Deserialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_SELECT_TRADE)] pub struct SSelectTrade { pub selected_slot: VarInt, } + +impl ServerPacket for SSelectTrade { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + selected_slot: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/set_beacon.rs b/pumpkin-protocol/src/java/server/play/set_beacon.rs index 3312abec8..deff4c742 100644 --- a/pumpkin-protocol/src/java/server/play/set_beacon.rs +++ b/pumpkin-protocol/src/java/server/play/set_beacon.rs @@ -1,12 +1,25 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_SET_BEACON; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::codec::var_int::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_SET_BEACON)] pub struct SSetBeacon { pub primary_effect: Option, pub secondary_effect: Option, } + +impl ServerPacket for SSetBeacon { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + primary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?, + secondary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/set_command_block.rs b/pumpkin-protocol/src/java/server/play/set_command_block.rs index f5e7ff5ab..5318d0e7b 100644 --- a/pumpkin-protocol/src/java/server/play/set_command_block.rs +++ b/pumpkin-protocol/src/java/server/play/set_command_block.rs @@ -1,11 +1,15 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_SET_COMMAND_BLOCK; use pumpkin_macros::java_packet; use pumpkin_util::math::position::BlockPos; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::codec::var_int::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_SET_COMMAND_BLOCK)] pub struct SSetCommandBlock { pub pos: BlockPos, @@ -19,6 +23,17 @@ pub struct SSetCommandBlock { pub flags: i8, } +impl ServerPacket for SSetCommandBlock { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + pos: BlockPos::from_i64(bytebuf.get_i64_be()?), + command: bytebuf.get_str_bounded(32767)?.into_string(), + mode: bytebuf.get_var_int()?, + flags: bytebuf.get_i8()?, + }) + } +} + pub enum CommandBlockMode { Chain, Repeating, diff --git a/pumpkin-protocol/src/java/server/play/set_creative_slot.rs b/pumpkin-protocol/src/java/server/play/set_creative_slot.rs index fef7bd0aa..eff1c441d 100644 --- a/pumpkin-protocol/src/java/server/play/set_creative_slot.rs +++ b/pumpkin-protocol/src/java/server/play/set_creative_slot.rs @@ -20,7 +20,7 @@ impl ServerPacket for SSetCreativeSlot { _version: &JavaMinecraftVersion, ) -> Result { let slot = read.get_i16_be()?; - let clicked_item = ItemStackSerializer::read_length_prefixed_optional(read)?; + let clicked_item = ItemStackSerializer::read_length_prefixed_optional(&mut read)?; Ok(Self { slot, clicked_item }) } } diff --git a/pumpkin-protocol/src/java/server/play/set_held_item.rs b/pumpkin-protocol/src/java/server/play/set_held_item.rs index 2e7ebe904..c089010bd 100644 --- a/pumpkin-protocol/src/java/server/play/set_held_item.rs +++ b/pumpkin-protocol/src/java/server/play/set_held_item.rs @@ -1,9 +1,21 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_SET_CARRIED_ITEM; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_SET_CARRIED_ITEM)] pub struct SSetHeldItem { pub slot: i16, } + +impl ServerPacket for SSetHeldItem { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + slot: bytebuf.get_i16_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs b/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs index 0b1db569f..7dba9b126 100644 --- a/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs +++ b/pumpkin-protocol/src/java/server/play/set_jigsaw_block.rs @@ -1,11 +1,15 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_SET_JIGSAW_BLOCK; use pumpkin_macros::java_packet; use pumpkin_util::math::position::BlockPos; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::codec::var_int::VarInt; -#[derive(Deserialize)] #[java_packet(PLAY_SET_JIGSAW_BLOCK)] pub struct SSetJigsawBlock { pub pos: BlockPos, @@ -17,3 +21,18 @@ pub struct SSetJigsawBlock { pub selection_priority: VarInt, pub placement_priority: VarInt, } + +impl ServerPacket for SSetJigsawBlock { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + pos: BlockPos::from_i64(bytebuf.get_i64_be()?), + name: bytebuf.get_str_bounded(32767)?.into_string(), + target: bytebuf.get_str_bounded(32767)?.into_string(), + pool: bytebuf.get_str_bounded(32767)?.into_string(), + final_state: bytebuf.get_str_bounded(32767)?.into_string(), + joint: bytebuf.get_str_bounded(32767)?.into_string(), + selection_priority: bytebuf.get_var_int()?, + placement_priority: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/set_test_block.rs b/pumpkin-protocol/src/java/server/play/set_test_block.rs index b46c01854..846e50ac3 100644 --- a/pumpkin-protocol/src/java/server/play/set_test_block.rs +++ b/pumpkin-protocol/src/java/server/play/set_test_block.rs @@ -2,7 +2,13 @@ use pumpkin_data::packet::serverbound::PLAY_SET_TEST_BLOCK; use pumpkin_macros::java_packet; use pumpkin_util::math::position::BlockPos; -#[derive(serde::Deserialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_SET_TEST_BLOCK)] pub struct SSetTestBlock { pub position: BlockPos, @@ -10,10 +16,32 @@ pub struct SSetTestBlock { pub message: String, } -#[derive(serde::Deserialize, Clone, Copy, Debug)] +impl ServerPacket for SSetTestBlock { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + position: BlockPos::from_i64(bytebuf.get_i64_be()?), + mode: TestBlockMode::read(&mut bytebuf)?, + message: bytebuf.get_str()?.into_string(), + }) + } +} + +#[derive(Clone, Copy, Debug)] pub enum TestBlockMode { Start, Log, Fail, Accept, } + +impl TestBlockMode { + fn read(bytebuf: &mut impl Read) -> Result { + match bytebuf.get_var_int()?.0 { + 0 => Ok(Self::Start), + 1 => Ok(Self::Log), + 2 => Ok(Self::Fail), + 3 => Ok(Self::Accept), + _ => Err(ReadingError::Message("Invalid TestBlockMode".to_string())), + } + } +} diff --git a/pumpkin-protocol/src/java/server/play/swing_arm.rs b/pumpkin-protocol/src/java/server/play/swing_arm.rs index 813ffb3ee..d3a03e589 100644 --- a/pumpkin-protocol/src/java/server/play/swing_arm.rs +++ b/pumpkin-protocol/src/java/server/play/swing_arm.rs @@ -1,11 +1,23 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_SWING; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_SWING)] pub struct SSwingArm { pub hand: VarInt, } + +impl ServerPacket for SSwingArm { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + hand: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs b/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs index 1e11f3c80..1b074b395 100644 --- a/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs +++ b/pumpkin-protocol/src/java/server/play/teleport_to_entity.rs @@ -1,10 +1,24 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_TELEPORT_TO_ENTITY; use pumpkin_macros::java_packet; -use serde::Deserialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; -#[derive(Deserialize)] #[java_packet(PLAY_TELEPORT_TO_ENTITY)] pub struct STeleportToEntity { - #[serde(with = "uuid::serde::compact")] pub target: uuid::Uuid, } + +impl ServerPacket for STeleportToEntity { + fn read( + mut bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self { + target: bytebuf.get_uuid()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs b/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs index bc279a7c2..0897c2e79 100644 --- a/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs +++ b/pumpkin-protocol/src/java/server/play/test_instance_block_action.rs @@ -4,7 +4,13 @@ use pumpkin_util::math::position::BlockPos; use crate::VarInt; -#[derive(serde::Deserialize)] +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; + #[java_packet(PLAY_TEST_INSTANCE_BLOCK_ACTION)] pub struct STestInstanceBlockAction { pub pos: BlockPos, @@ -12,7 +18,17 @@ pub struct STestInstanceBlockAction { pub data: TestInstanceBlockData, } -#[derive(serde::Deserialize, Clone, Copy, Debug)] +impl ServerPacket for STestInstanceBlockAction { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + pos: BlockPos::from_i64(bytebuf.get_i64_be()?), + action: TestInstanceBlockAction::read(&mut bytebuf)?, + data: TestInstanceBlockData::read(&mut bytebuf)?, + }) + } +} + +#[derive(Clone, Copy, Debug)] pub enum TestInstanceBlockAction { Init, Query, @@ -23,14 +39,39 @@ pub enum TestInstanceBlockAction { Run, } -#[derive(serde::Deserialize)] +impl TestInstanceBlockAction { + fn read(bytebuf: &mut impl Read) -> Result { + match bytebuf.get_var_int()?.0 { + 0 => Ok(Self::Init), + 1 => Ok(Self::Query), + 2 => Ok(Self::Set), + 3 => Ok(Self::Reset), + 4 => Ok(Self::Save), + 5 => Ok(Self::Export), + 6 => Ok(Self::Run), + _ => Err(ReadingError::Message( + "Invalid TestInstanceBlockAction".to_string(), + )), + } + } +} + pub struct VarIntVector3 { pub x: VarInt, pub y: VarInt, pub z: VarInt, } -#[derive(serde::Deserialize)] +impl VarIntVector3 { + fn read(bytebuf: &mut impl Read) -> Result { + Ok(Self { + x: bytebuf.get_var_int()?, + y: bytebuf.get_var_int()?, + z: bytebuf.get_var_int()?, + }) + } +} + pub struct TestInstanceBlockData { pub test: Option, pub size: VarIntVector3, @@ -40,7 +81,43 @@ pub struct TestInstanceBlockData { pub error_message: Option, } -#[derive(serde::Deserialize, Clone, Copy, Debug)] +impl TestInstanceBlockData { + fn read(bytebuf: &mut impl Read) -> Result { + let test = bytebuf.get_option(|b| b.get_str().map(String::from))?; + let size = VarIntVector3::read(bytebuf)?; + let rotation = match bytebuf.get_var_int()?.0 { + 0 => pumpkin_data::block_rotation::Rotation::None, + 1 => pumpkin_data::block_rotation::Rotation::Clockwise90, + 2 => pumpkin_data::block_rotation::Rotation::Rotate180, + 3 => pumpkin_data::block_rotation::Rotation::CounterClockwise90, + _ => return Err(ReadingError::Message("Invalid Rotation".to_string())), + }; + let ignore_entities = bytebuf.get_bool()?; + let status = match bytebuf.get_var_int()?.0 { + 0 => TestInstanceBlockStatus::Cleared, + 1 => TestInstanceBlockStatus::Running, + 2 => TestInstanceBlockStatus::Success, + 3 => TestInstanceBlockStatus::Failed, + _ => { + return Err(ReadingError::Message( + "Invalid TestInstanceBlockStatus".to_string(), + )); + } + }; + let error_message = bytebuf.get_option(|b| b.get_str().map(String::from))?; + + Ok(Self { + test, + size, + rotation, + ignore_entities, + status, + error_message, + }) + } +} + +#[derive(Clone, Copy, Debug)] pub enum TestInstanceBlockStatus { Cleared, Running, diff --git a/pumpkin-protocol/src/java/server/play/use_item.rs b/pumpkin-protocol/src/java/server/play/use_item.rs index 38c8a936e..17f725960 100644 --- a/pumpkin-protocol/src/java/server/play/use_item.rs +++ b/pumpkin-protocol/src/java/server/play/use_item.rs @@ -1,10 +1,14 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_USE_ITEM; use pumpkin_macros::java_packet; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_USE_ITEM)] pub struct SUseItem { // 0 for main hand, 1 for off hand @@ -13,3 +17,17 @@ pub struct SUseItem { pub yaw: f32, pub pitch: f32, } + +impl ServerPacket for SUseItem { + fn read( + mut bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self { + hand: bytebuf.get_var_int()?, + sequence: bytebuf.get_var_int()?, + yaw: bytebuf.get_f32_be()?, + pitch: bytebuf.get_f32_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/play/use_item_on.rs b/pumpkin-protocol/src/java/server/play/use_item_on.rs index 9b23f9b79..95f0b6ecd 100644 --- a/pumpkin-protocol/src/java/server/play/use_item_on.rs +++ b/pumpkin-protocol/src/java/server/play/use_item_on.rs @@ -1,11 +1,15 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::PLAY_USE_ITEM_ON; use pumpkin_macros::java_packet; use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; -use serde::{Deserialize, Serialize}; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; use crate::VarInt; -#[derive(Deserialize, Serialize)] #[java_packet(PLAY_USE_ITEM_ON)] pub struct SUseItemOn { pub hand: VarInt, @@ -16,3 +20,21 @@ pub struct SUseItemOn { pub is_against_world_border: bool, pub sequence: VarInt, } + +impl ServerPacket for SUseItemOn { + fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result { + Ok(Self { + hand: bytebuf.get_var_int()?, + position: BlockPos::from_i64(bytebuf.get_i64_be()?), + face: bytebuf.get_var_int()?, + cursor_pos: Vector3::new( + bytebuf.get_f32_be()?, + bytebuf.get_f32_be()?, + bytebuf.get_f32_be()?, + ), + inside_block: bytebuf.get_bool()?, + is_against_world_border: bytebuf.get_bool()?, + sequence: bytebuf.get_var_int()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/status/ping_request.rs b/pumpkin-protocol/src/java/server/status/ping_request.rs index d2e4e822a..6ac29ccdf 100644 --- a/pumpkin-protocol/src/java/server/status/ping_request.rs +++ b/pumpkin-protocol/src/java/server/status/ping_request.rs @@ -1,13 +1,28 @@ +use crate::{ + ServerPacket, + ser::{NetworkReadExt, ReadingError}, +}; use pumpkin_data::packet::serverbound::STATUS_PING_REQUEST; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; /// Sent by the client to measure the round-trip time (latency) to the server. /// /// This is the second part of the Server List Ping (SLP) process /// The server should respond with `CPingResponse`. -#[derive(serde::Deserialize, Serialize)] #[java_packet(STATUS_PING_REQUEST)] pub struct SStatusPingRequest { pub payload: i64, } + +impl ServerPacket for SStatusPingRequest { + fn read( + mut bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self { + payload: bytebuf.get_i64_be()?, + }) + } +} diff --git a/pumpkin-protocol/src/java/server/status/status_request.rs b/pumpkin-protocol/src/java/server/status/status_request.rs index 27cc479cf..e3bc8b54e 100644 --- a/pumpkin-protocol/src/java/server/status/status_request.rs +++ b/pumpkin-protocol/src/java/server/status/status_request.rs @@ -1,11 +1,21 @@ +use crate::{ServerPacket, ser::ReadingError}; use pumpkin_data::packet::serverbound::STATUS_STATUS_REQUEST; use pumpkin_macros::java_packet; -use serde::Serialize; +use pumpkin_util::version::JavaMinecraftVersion; +use std::io::Read; /// Sent by the client to request the server's current status information. /// /// This is the first packet sent during the "Status" state. /// The server should respond with `CStatusResponse`. -#[derive(Serialize)] #[java_packet(STATUS_STATUS_REQUEST)] pub struct SStatusRequest; + +impl ServerPacket for SStatusRequest { + fn read( + _bytebuf: impl Read, + _protocol_version: &JavaMinecraftVersion, + ) -> Result { + Ok(Self) + } +} diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 9966e8cd9..89bc0d729 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -1,6 +1,5 @@ use std::{ io::{Error, Read, Write}, - marker::PhantomData, pin::Pin, task::{Context, Poll}, }; @@ -15,10 +14,7 @@ use pumpkin_util::{ version::JavaMinecraftVersion, }; use ser::{ReadingError, WritingError}; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{DeserializeSeed, SeqAccess, Visitor}, -}; + use thiserror::Error; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; @@ -76,110 +72,41 @@ impl TryFrom for ConnectionState { } } -struct IdOrVisitor(PhantomData); -impl<'de, T: Deserialize<'de>> Visitor<'de> for IdOrVisitor { - type Value = IdOr; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("A VarInt followed by a value if the VarInt is 0") - } - - fn visit_seq>(self, mut seq: A) -> Result { - enum IdOrStateDeserializer { - Init, - Id(u16), - Value(T), - } - - impl<'de, T: Deserialize<'de>> DeserializeSeed<'de> for &mut IdOrStateDeserializer { - type Value = (); - - fn deserialize>( - self, - deserializer: D, - ) -> Result { - match self { - IdOrStateDeserializer::Init => { - // Get the VarInt - let id = VarInt::deserialize(deserializer)?; - *self = IdOrStateDeserializer::::Id(id.0.try_into().map_err(|_| { - serde::de::Error::custom(format!( - "{} cannot be mapped to a registry id", - id.0 - )) - })?); - } - IdOrStateDeserializer::Id(id) => { - debug_assert_eq!(*id, 0); - // Get the data - let value = T::deserialize(deserializer)?; - *self = IdOrStateDeserializer::Value(value); - } - IdOrStateDeserializer::Value(_) => { - return Err(serde::de::Error::custom("Unreachable state reached")); - } - } - - Ok(()) - } - } - - let mut state = IdOrStateDeserializer::::Init; - - let _ = seq.next_element_seed(&mut state)?; - - match state { - IdOrStateDeserializer::Id(id) => { - if id > 0 { - return Ok(IdOr::Id(id - 1)); - } - } - _ => return Err(serde::de::Error::custom("Unreachable state reached")), - } - - let _ = seq.next_element_seed(&mut state)?; - - match state { - IdOrStateDeserializer::Value(val) => Ok(IdOr::Value(val)), - _ => Err(serde::de::Error::custom("Unreachable state reached")), - } - } -} - #[derive(PartialEq, Eq, Clone)] pub enum IdOr { Id(u16), Value(T), } -impl<'de, T: Deserialize<'de>> Deserialize<'de> for IdOr { - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_seq(IdOrVisitor(PhantomData)) +impl IdOr { + pub fn read( + read: &mut R, + read_value: impl FnOnce(&mut R) -> Result, + ) -> Result { + let id = read.get_var_int()?.0; + if id == 0 { + Ok(Self::Value(read_value(read)?)) + } else { + Ok(Self::Id((id - 1) as u16)) + } } -} -#[expect(clippy::trait_duplication_in_bounds)] -impl Serialize for IdOr { - fn serialize(&self, serializer: S) -> Result { + pub fn write( + &self, + write: &mut W, + write_value: impl FnOnce(&mut W, &T) -> Result<(), ser::WritingError>, + ) -> Result<(), ser::WritingError> { match self { - Self::Id(id) => VarInt::from(*id + 1).serialize(serializer), + Self::Id(id) => write.write_var_int(&((*id as i32) + 1).into()), Self::Value(value) => { - #[derive(Serialize)] - struct NetworkRepr { - zero_id: VarInt, - value: T, - } - NetworkRepr { - zero_id: 0.into(), - value, - } - .serialize(serializer) + write.write_var_int(&0.into())?; + write_value(write, value) } } } } -#[derive(Serialize, Deserialize, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct SoundEvent { pub sound_name: ResourceLocation, pub range: Option, @@ -375,7 +302,8 @@ impl From for PacketDecodeError { } } -#[derive(Serialize, Clone)] +#[derive(Clone, serde::Serialize)] +#[serde(rename_all = "camelCase")] pub struct StatusResponse { /// The version on which the server is running. (Optional) pub version: Option, @@ -388,7 +316,7 @@ pub struct StatusResponse { /// Whether players are forced to use secure chat. pub enforce_secure_chat: bool, } -#[derive(Serialize, Clone)] +#[derive(Clone, serde::Serialize)] pub struct Version { /// The name of the version (e.g. 1.21.4) pub name: String, @@ -396,7 +324,7 @@ pub struct Version { pub protocol: u32, } -#[derive(Serialize, Clone)] +#[derive(Clone, serde::Serialize)] pub struct Players { /// The maximum player count that the server allows. pub max: u32, @@ -407,7 +335,7 @@ pub struct Players { pub sample: Vec, } -#[derive(Serialize, Clone)] +#[derive(Clone, serde::Serialize)] pub struct Sample { /// The player's name. pub name: String, @@ -415,24 +343,46 @@ pub struct Sample { pub id: String, } -// basically game profile -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct Property { pub name: Box, - // base 64 pub value: Box, - // base 64 pub signature: Option>, } -#[derive(Serialize)] +impl Property { + pub fn read(read: &mut impl ser::NetworkReadExt) -> Result { + Ok(Self { + name: read.get_str()?, + value: read.get_str()?, + signature: read.get_option(ser::NetworkReadExt::get_str)?, + }) + } + + pub fn write(&self, write: &mut impl ser::NetworkWriteExt) -> Result<(), ser::WritingError> { + write.write_string(&self.name)?; + write.write_string(&self.value)?; + write.write_option(&self.signature, |w, v| w.write_string(v))?; + Ok(()) + } +} + pub struct KnownPack<'a> { pub namespace: &'a str, pub id: &'a str, pub version: &'a str, } -#[derive(Serialize, Clone)] +impl KnownPack<'_> { + pub fn write(&self, write: &mut impl ser::NetworkWriteExt) -> Result<(), ser::WritingError> { + write.write_string(self.namespace)?; + write.write_string(self.id)?; + write.write_string(self.version)?; + Ok(()) + } +} + +#[derive(Clone)] pub enum NumberFormat { /// Show nothing. Blank, @@ -442,6 +392,24 @@ pub enum NumberFormat { Fixed(TextComponent), } +impl NumberFormat { + pub fn write(&self, write: &mut impl ser::NetworkWriteExt) -> Result<(), ser::WritingError> { + match self { + Self::Blank => write.write_var_int(&0.into()), + Self::Styled(_style) => { + write.write_var_int(&1.into())?; + // TODO: Style write + Ok(()) + } + Self::Fixed(_text) => { + write.write_var_int(&2.into())?; + // TODO: TextComponent write + Ok(()) + } + } + } +} + /// For the first 8 values set means relative value while unset means absolute #[derive(Debug, PartialEq, Eq, Hash)] pub enum PositionFlag { @@ -482,16 +450,6 @@ pub enum Label { TextComponent(Box), } -impl Serialize for Label { - fn serialize(&self, serializer: S) -> Result { - match self { - Self::BuiltIn(link_type) => link_type.serialize(serializer), - Self::TextComponent(component) => component.serialize(serializer), - } - } -} - -#[derive(Serialize)] pub struct Link<'a> { pub is_built_in: bool, pub label: Label, @@ -510,6 +468,21 @@ impl<'a> Link<'a> { url, } } + + pub fn write(&self, write: &mut impl ser::NetworkWriteExt) -> Result<(), ser::WritingError> { + match &self.label { + Label::BuiltIn(link_type) => { + write.write_bool(true)?; + write.write_var_int(&(*link_type as i32).into())?; + } + Label::TextComponent(text_component) => { + write.write_bool(false)?; + write.write_slice(&text_component.encode())?; + } + } + write.write_string(self.url)?; + Ok(()) + } } #[derive(Clone, Copy)] @@ -526,49 +499,3 @@ pub enum LinkType { News = 8, Announcements = 9, } - -impl Serialize for LinkType { - fn serialize(&self, serializer: S) -> Result { - VarInt(*self as i32).serialize(serializer) - } -} - -#[cfg(test)] -mod test { - use serde::{Deserialize, Serialize}; - - use crate::{ - IdOr, SoundEvent, - ser::{deserializer::Deserializer, serializer::Serializer}, - }; - - #[test] - fn serde_id_or_id() -> Result<(), Box> { - let mut buf = Vec::new(); - - let id = IdOr::::Id(0); - id.serialize(&mut Serializer::new(&mut buf))?; - - let deser_id = IdOr::::deserialize(&mut Deserializer::new(buf.as_slice()))?; - - assert!(id == deser_id); - Ok(()) - } - - #[test] - fn serde_id_or_value() -> Result<(), Box> { - let mut buf = Vec::new(); - let event = SoundEvent { - sound_name: "test".to_string(), - range: Some(1.0), - }; - - let id = IdOr::::Value(event); - id.serialize(&mut Serializer::new(&mut buf))?; - - let deser_id = IdOr::::deserialize(&mut Deserializer::new(buf.as_slice()))?; - - assert!(id == deser_id); - Ok(()) - } -} diff --git a/pumpkin-protocol/src/packet.rs b/pumpkin-protocol/src/packet.rs index 42c9d6f5b..f9e78c8fe 100644 --- a/pumpkin-protocol/src/packet.rs +++ b/pumpkin-protocol/src/packet.rs @@ -1,12 +1,10 @@ use std::io::{Error, Read, Write}; use pumpkin_util::version::JavaMinecraftVersion; -use serde::{Serialize, de::DeserializeOwned}; use crate::{ - BClientPacket, BServerPacket, ClientPacket, ReadingError, ServerPacket, WritingError, + BClientPacket, BServerPacket, codec::var_int::VarIntType, - ser::{deserializer, serializer}, serial::{PacketRead, PacketWrite}, }; @@ -19,24 +17,6 @@ pub trait MultiVersionJavaPacket { fn to_id(version: JavaMinecraftVersion) -> i32; } -impl ClientPacket for P { - fn write_packet_data( - &self, - write: impl Write, - _version: &JavaMinecraftVersion, - ) -> Result<(), WritingError> { - let mut serializer = serializer::Serializer::new(write); - self.serialize(&mut serializer) - } -} - -impl ServerPacket for P { - fn read(read: impl Read, _version: &JavaMinecraftVersion) -> Result { - let mut deserializer = deserializer::Deserializer::new(read); - P::deserialize(&mut deserializer) - } -} - impl BClientPacket for P { fn write_packet(&self, mut writer: impl Write) -> Result<(), Error> { self.write(&mut writer) diff --git a/pumpkin-protocol/src/ser/deserializer.rs b/pumpkin-protocol/src/ser/deserializer.rs deleted file mode 100644 index 542ecd5ff..000000000 --- a/pumpkin-protocol/src/ser/deserializer.rs +++ /dev/null @@ -1,327 +0,0 @@ -use std::fmt::Display; - -use crate::ser::NetworkReadExt; -use serde::de::{EnumAccess, IntoDeserializer, VariantAccess, Visitor}; - -use super::{Read, ReadingError}; -use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess}; - -pub struct Deserializer { - inner: R, -} - -impl de::Error for ReadingError { - fn custom(msg: T) -> Self { - Self::Message(msg.to_string()) - } -} - -impl Deserializer { - pub const fn new(read: R) -> Self { - Self { inner: read } - } -} - -impl<'de, R: Read> de::Deserializer<'de> for &mut Deserializer { - type Error = ReadingError; - - fn deserialize_any>(self, _visitor: V) -> Result { - Err(ReadingError::Message( - "This is impossible to do, since you cannot infer the data structure from the packet" - .into(), - )) - } - - fn deserialize_bool>(self, visitor: V) -> Result { - visitor.visit_bool(self.inner.get_bool()?) - } - - fn deserialize_i8>(self, visitor: V) -> Result { - visitor.visit_i8(self.inner.get_i8()?) - } - - fn deserialize_i16>(self, visitor: V) -> Result { - visitor.visit_i16(self.inner.get_i16_be()?) - } - - fn deserialize_i32>(self, visitor: V) -> Result { - visitor.visit_i32(self.inner.get_i32_be()?) - } - - fn deserialize_i64>(self, visitor: V) -> Result { - visitor.visit_i64(self.inner.get_i64_be()?) - } - - fn deserialize_u8>(self, visitor: V) -> Result { - visitor.visit_u8(self.inner.get_u8()?) - } - - fn deserialize_u16>(self, visitor: V) -> Result { - visitor.visit_u16(self.inner.get_u16_be()?) - } - - fn deserialize_u32>(self, visitor: V) -> Result { - visitor.visit_u32(self.inner.get_u32_be()?) - } - - fn deserialize_u64>(self, visitor: V) -> Result { - visitor.visit_u64(self.inner.get_u64_be()?) - } - - fn deserialize_f32>(self, visitor: V) -> Result { - visitor.visit_f32(self.inner.get_f32_be()?) - } - - fn deserialize_f64>(self, visitor: V) -> Result { - visitor.visit_f64(self.inner.get_f64_be()?) - } - - fn deserialize_i128>(self, visitor: V) -> Result { - visitor.visit_i128(self.inner.get_i128_be()?) - } - - fn deserialize_u128>(self, visitor: V) -> Result { - visitor.visit_u128(self.inner.get_u128_be()?) - } - - fn deserialize_char>(self, visitor: V) -> Result { - let u32_val = self.inner.get_u32_be()?; - char::from_u32(u32_val).map_or_else( - || { - Err(ReadingError::Message(format!( - "Invalid char value: {u32_val}" - ))) - }, - |c| visitor.visit_char(c), - ) - } - - fn deserialize_str>(self, visitor: V) -> Result { - self.deserialize_string(visitor) - } - - fn deserialize_string>(self, visitor: V) -> Result { - visitor.visit_str(&self.inner.get_str()?) - } - - fn deserialize_bytes>(self, _visitor: V) -> Result { - Err(ReadingError::Message( - "deserialize_bytes not implemented".into(), - )) - } - - fn deserialize_byte_buf>(self, _visitor: V) -> Result { - Err(ReadingError::Message( - "deserialize_byte_buf not implemented".into(), - )) - } - - fn deserialize_option>(self, visitor: V) -> Result { - if self.inner.get_bool()? { - visitor.visit_some(self) - } else { - visitor.visit_none() - } - } - - fn deserialize_unit>(self, visitor: V) -> Result { - visitor.visit_unit() - } - - fn deserialize_unit_struct>( - self, - _name: &'static str, - visitor: V, - ) -> Result { - visitor.visit_unit() - } - - fn deserialize_newtype_struct>( - self, - _name: &'static str, - _visitor: V, - ) -> Result { - Err(ReadingError::Message( - "deserialize_newtype_struct not implemented".into(), - )) - } - - fn deserialize_seq>(self, visitor: V) -> Result { - struct Access<'a, R: Read> { - deserializer: &'a mut Deserializer, - } - - impl<'de, R: Read> SeqAccess<'de> for Access<'_, R> { - type Error = ReadingError; - - fn next_element_seed>( - &mut self, - seed: T, - ) -> Result, Self::Error> { - let value = DeserializeSeed::deserialize(seed, &mut *self.deserializer)?; - Ok(Some(value)) - } - } - - visitor.visit_seq(Access { deserializer: self }) - } - - fn deserialize_tuple>( - self, - len: usize, - visitor: V, - ) -> Result { - struct Access<'a, R: Read> { - deserializer: &'a mut Deserializer, - len: usize, - } - - impl<'de, R: Read> SeqAccess<'de> for Access<'_, R> { - type Error = ReadingError; - - fn next_element_seed>( - &mut self, - seed: T, - ) -> Result, Self::Error> { - if self.len > 0 { - self.len -= 1; - let value = DeserializeSeed::deserialize(seed, &mut *self.deserializer)?; - Ok(Some(value)) - } else { - Ok(None) - } - } - } - - visitor.visit_seq(Access { - deserializer: self, - len, - }) - } - - fn deserialize_tuple_struct>( - self, - _name: &'static str, - len: usize, - visitor: V, - ) -> Result { - self.deserialize_tuple(len, visitor) - } - - fn deserialize_map>(self, visitor: V) -> Result { - struct Access<'a, R: Read> { - deserializer: &'a mut Deserializer, - len: usize, - } - impl<'de, R: Read> MapAccess<'de> for Access<'_, R> { - type Error = ReadingError; - - fn next_key_seed>( - &mut self, - seed: K, - ) -> Result, Self::Error> { - if self.len == 0 { - return Ok(None); - } - seed.deserialize(&mut *self.deserializer).map(Some) - } - - fn next_value_seed>( - &mut self, - seed: Val, - ) -> Result { - self.len -= 1; - seed.deserialize(&mut *self.deserializer) - } - } - let len = self.inner.get_var_int()?.0 as usize; - - visitor.visit_map(Access { - deserializer: self, - len, - }) - } - - fn deserialize_struct>( - self, - _name: &'static str, - fields: &'static [&'static str], - visitor: V, - ) -> Result { - self.deserialize_tuple(fields.len(), visitor) - } - - fn deserialize_enum>( - self, - _name: &'static str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result { - visitor.visit_enum(self) - } - - fn deserialize_identifier>(self, _visitor: V) -> Result { - Err(ReadingError::Message( - "deserialize_identifier not implemented".into(), - )) - } - - fn deserialize_ignored_any>( - self, - _visitor: V, - ) -> Result { - Err(ReadingError::Message( - "deserialize_ignored_any not implemented".into(), - )) - } -} - -impl<'de, R: Read> EnumAccess<'de> for &mut Deserializer { - type Error = ReadingError; - type Variant = Self; - - fn variant_seed>( - self, - seed: V, - ) -> Result<(V::Value, Self::Variant), Self::Error> { - let variant_index = self.inner.get_var_int()?.0; - let variant_index_u32: u32 = variant_index.try_into().map_err(|_| { - ReadingError::Message(format!( - "Invalid variant index {variant_index} for enum, cannot convert to u32" - )) - })?; - let val = seed.deserialize(variant_index_u32.into_deserializer())?; - Ok((val, self)) - } -} - -impl<'de, R: Read> VariantAccess<'de> for &mut Deserializer { - type Error = ReadingError; - - fn unit_variant(self) -> Result<(), Self::Error> { - Ok(()) - } - - fn newtype_variant_seed>( - self, - seed: T, - ) -> Result { - seed.deserialize(self) - } - - fn tuple_variant>( - self, - len: usize, - visitor: V, - ) -> Result { - de::Deserializer::deserialize_tuple(self, len, visitor) - } - - fn struct_variant>( - self, - fields: &'static [&'static str], - visitor: V, - ) -> Result { - de::Deserializer::deserialize_struct(self, "", fields, visitor) - } -} diff --git a/pumpkin-protocol/src/ser/mod.rs b/pumpkin-protocol/src/ser/mod.rs index 16fd57036..5f9ae12d1 100644 --- a/pumpkin-protocol/src/ser/mod.rs +++ b/pumpkin-protocol/src/ser/mod.rs @@ -1,5 +1,4 @@ use core::str; -use serde::Serialize; use std::io::{Read, Write}; use crate::{ @@ -9,11 +8,9 @@ use crate::{ }, }; -pub mod deserializer; use pumpkin_nbt::{serializer::NbtWriteHelperJava, tag::NbtTag}; use pumpkin_util::math::position::BlockPos; use thiserror::Error; -pub mod serializer; #[derive(Debug, Error)] pub enum ReadingError { @@ -27,16 +24,28 @@ pub enum ReadingError { Message(String), } +impl serde::de::Error for ReadingError { + fn custom(msg: T) -> Self { + Self::Message(msg.to_string()) + } +} + #[derive(Debug, Error)] pub enum WritingError { #[error("IO error: {0}")] - IoError(std::io::Error), + IoError(#[from] std::io::Error), #[error("Serde failure: {0}")] Serde(String), #[error("Failed to serialize packet: {0}")] Message(String), } +impl serde::ser::Error for WritingError { + fn custom(msg: T) -> Self { + Self::Serde(msg.to_string()) + } +} + pub trait NetworkReadExt { fn get_i8(&mut self) -> Result; fn get_u8(&mut self) -> Result; @@ -51,6 +60,32 @@ pub trait NetworkReadExt { fn get_f64_be(&mut self) -> Result; fn get_i128_be(&mut self) -> Result; fn get_u128_be(&mut self) -> Result; + + fn get_i16(&mut self) -> Result { + self.get_i16_be() + } + fn get_u16(&mut self) -> Result { + self.get_u16_be() + } + fn get_i32(&mut self) -> Result { + self.get_i32_be() + } + fn get_u32(&mut self) -> Result { + self.get_u32_be() + } + fn get_i64(&mut self) -> Result { + self.get_i64_be() + } + fn get_u64(&mut self) -> Result { + self.get_u64_be() + } + fn get_f32(&mut self) -> Result { + self.get_f32_be() + } + fn get_f64(&mut self) -> Result { + self.get_f64_be() + } + fn read_boxed_slice(&mut self, count: usize) -> Result, ReadingError>; fn read_remaining_to_boxed_slice(&mut self, bound: usize) -> Result, ReadingError>; @@ -242,6 +277,41 @@ pub trait NetworkWriteExt { fn write_f64_be(&mut self, data: f64) -> Result<(), WritingError>; fn write_slice(&mut self, data: &[u8]) -> Result<(), WritingError>; + fn write_i16(&mut self, data: i16) -> Result<(), WritingError> { + self.write_i16_be(data) + } + fn write_u16(&mut self, data: u16) -> Result<(), WritingError> { + self.write_u16_be(data) + } + fn write_i32(&mut self, data: i32) -> Result<(), WritingError> { + self.write_i32_be(data) + } + fn write_u32(&mut self, data: u32) -> Result<(), WritingError> { + self.write_u32_be(data) + } + fn write_i64(&mut self, data: i64) -> Result<(), WritingError> { + self.write_i64_be(data) + } + fn write_u64(&mut self, data: u64) -> Result<(), WritingError> { + self.write_u64_be(data) + } + fn write_f32(&mut self, data: f32) -> Result<(), WritingError> { + self.write_f32_be(data) + } + fn write_f64(&mut self, data: f64) -> Result<(), WritingError> { + self.write_f64_be(data) + } + + fn put_var_int(&mut self, data: &VarInt) -> Result<(), WritingError> { + self.write_var_int(data) + } + fn put_i32(&mut self, data: i32) -> Result<(), WritingError> { + self.write_i32(data) + } + fn put_bool(&mut self, data: bool) -> Result<(), WritingError> { + self.write_bool(data) + } + fn write_bool(&mut self, data: bool) -> Result<(), WritingError> { if data { self.write_u8(1) @@ -293,10 +363,6 @@ pub trait NetworkWriteExt { Ok(()) } - fn write_serialize(&mut self, data: &G) -> Result<(), WritingError> - where - Self: Sized; - fn write_nbt(&mut self, data: NbtTag) -> Result<(), WritingError>; } @@ -415,11 +481,6 @@ impl NetworkWriteExt for W { Ok(()) } - fn write_serialize(&mut self, data: &G) -> Result<(), WritingError> { - let mut serializer = serializer::Serializer::new(self); - data.serialize(&mut serializer) - } - fn write_nbt(&mut self, data: NbtTag) -> Result<(), WritingError> { let mut write_adaptor = NbtWriteHelperJava::new(self); data.serialize(&mut write_adaptor) @@ -428,319 +489,3 @@ impl NetworkWriteExt for W { Ok(()) } } - -#[cfg(test)] -mod test { - use std::io::Cursor; - - use serde::{Deserialize, Serialize}; - - use crate::{ - VarInt, - ser::{deserializer, serializer}, - }; - - #[test] - fn i32_reserialize() -> Result<(), Box> { - #[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug)] - struct Foo { - bar: i32, - } - let foo = Foo { bar: 69 }; - let mut bytes = Vec::new(); - let mut serializer = serializer::Serializer::new(&mut bytes); - foo.serialize(&mut serializer)?; - - let cursor = Cursor::new(bytes); - let deserialized: Foo = Foo::deserialize(&mut deserializer::Deserializer::new(cursor))?; - - assert_eq!(foo, deserialized); - Ok(()) - } - - #[test] - fn varint_reserialize() -> Result<(), Box> { - #[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug)] - struct Foo { - bar: VarInt, - } - let foo = Foo { bar: 69.into() }; - let mut bytes = Vec::new(); - let mut serializer = serializer::Serializer::new(&mut bytes); - foo.serialize(&mut serializer)?; - - let cursor = Cursor::new(bytes); - let deserialized: Foo = Foo::deserialize(&mut deserializer::Deserializer::new(cursor))?; - - assert_eq!(foo, deserialized); - Ok(()) - } - - #[test] - fn char_reserialize() -> Result<(), Box> { - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - struct CharStruct { - c: char, - } - - // Test with normal char - let original = CharStruct { c: 'A' }; - let mut bytes = Vec::new(); - let mut ser = serializer::Serializer::new(&mut bytes); - original.serialize(&mut ser)?; - assert_eq!(bytes, vec![0, 0, 0, 0x41]); - - let de_cursor = Cursor::new(bytes); - let deserialized: CharStruct = - CharStruct::deserialize(&mut deserializer::Deserializer::new(de_cursor))?; - assert_eq!(original, deserialized); - - // Test with complex char - let original_complex = CharStruct { c: 'Ω' }; // Greek Omega, U+03A9 - let mut bytes_complex = Vec::new(); - let mut ser_complex = serializer::Serializer::new(&mut bytes_complex); - original_complex.serialize(&mut ser_complex)?; - assert_eq!(bytes_complex, vec![0, 0, 0x03, 0xA9]); - - let de_cursor_complex = Cursor::new(bytes_complex); - let deserialized_complex: CharStruct = - CharStruct::deserialize(&mut deserializer::Deserializer::new(de_cursor_complex))?; - assert_eq!(original_complex, deserialized_complex); - - // Test with an emoji - let original_emoji = CharStruct { c: '\u{1F383}' }; // Pumpkin emoji, U+1F383 - let mut bytes_emoji = Vec::new(); - let mut ser_emoji = serializer::Serializer::new(&mut bytes_emoji); - original_emoji.serialize(&mut ser_emoji)?; - assert_eq!(bytes_emoji, vec![0, 0x01, 0xF3, 0x83]); - - let de_cursor_emoji = Cursor::new(bytes_emoji); - let deserialized_emoji: CharStruct = - CharStruct::deserialize(&mut deserializer::Deserializer::new(de_cursor_emoji))?; - assert_eq!(original_emoji, deserialized_emoji); - Ok(()) - } - - #[test] - fn i128_reserialize() -> Result<(), Box> { - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - struct I128Struct { - val: i128, - } - - let original = I128Struct { - val: 12345678901234567890123456789012345678, - }; - let mut bytes = Vec::new(); - let mut ser = serializer::Serializer::new(&mut bytes); - original.serialize(&mut ser)?; - assert_eq!(bytes, original.val.to_be_bytes()); - - let de_cursor = Cursor::new(bytes); - let deserialized: I128Struct = - I128Struct::deserialize(&mut deserializer::Deserializer::new(de_cursor))?; - assert_eq!(original, deserialized); - - let original_neg = I128Struct { - val: -12345678901234567890123456789012345678, - }; - let mut bytes_neg = Vec::new(); - let mut ser_neg = serializer::Serializer::new(&mut bytes_neg); - original_neg.serialize(&mut ser_neg)?; - assert_eq!(bytes_neg, original_neg.val.to_be_bytes()); - - let de_cursor_neg = Cursor::new(bytes_neg); - let deserialized_neg: I128Struct = - I128Struct::deserialize(&mut deserializer::Deserializer::new(de_cursor_neg))?; - assert_eq!(original_neg, deserialized_neg); - Ok(()) - } - - #[test] - fn u128_reserialize() -> Result<(), Box> { - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - struct U128Struct { - val: u128, - } - - let original = U128Struct { - val: 123456789012345678901234567890123456789, - }; - let mut bytes = Vec::new(); - let mut ser = serializer::Serializer::new(&mut bytes); - original.serialize(&mut ser)?; - assert_eq!(bytes, original.val.to_be_bytes()); - - let de_cursor = Cursor::new(bytes); - let deserialized: U128Struct = - U128Struct::deserialize(&mut deserializer::Deserializer::new(de_cursor))?; - assert_eq!(original, deserialized); - Ok(()) - } - - #[test] - fn unit_reserialize() -> Result<(), Box> { - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - struct UnitStruct; - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - struct StructWithUnit { - a: i32, - b: UnitStruct, - c: i32, - } - let original = UnitStruct; - let mut bytes = Vec::new(); - let mut ser = serializer::Serializer::new(&mut bytes); - original.serialize(&mut ser)?; - assert!(bytes.is_empty()); - - let de_cursor = Cursor::new(bytes); - let deserialized: UnitStruct = - UnitStruct::deserialize(&mut deserializer::Deserializer::new(de_cursor))?; - assert_eq!(original, deserialized); - - let original_with_unit = StructWithUnit { - a: 1, - b: UnitStruct, - c: 2, - }; - let mut bytes_with_unit = Vec::new(); - let mut ser_with_unit = serializer::Serializer::new(&mut bytes_with_unit); - original_with_unit.serialize(&mut ser_with_unit)?; - - // Check that only a and c were serialized - let mut expected_bytes = Vec::new(); - expected_bytes.extend_from_slice(&1i32.to_be_bytes()); - expected_bytes.extend_from_slice(&2i32.to_be_bytes()); - assert_eq!(bytes_with_unit, expected_bytes); - - let de_cursor_with_unit = Cursor::new(bytes_with_unit); - let deserialized_with_unit: StructWithUnit = - StructWithUnit::deserialize(&mut deserializer::Deserializer::new(de_cursor_with_unit))?; - assert_eq!(original_with_unit, deserialized_with_unit); - Ok(()) - } - - #[test] - fn enum_reserialize() -> Result<(), Box> { - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - enum MyEnum { - A, - B(i32), - C { x: i32, y: String }, - } - - let original_a = MyEnum::A; - let mut bytes_a = Vec::new(); - let mut ser_a = serializer::Serializer::new(&mut bytes_a); - original_a.serialize(&mut ser_a)?; - // VarInt for index 0 - assert_eq!(bytes_a, vec![0x00]); - let de_cursor_a = Cursor::new(bytes_a); - let deserialized_a: MyEnum = - MyEnum::deserialize(&mut deserializer::Deserializer::new(de_cursor_a))?; - assert_eq!(original_a, deserialized_a); - - let original_b = MyEnum::B(123); - let mut bytes_b = Vec::new(); - let mut ser_b = serializer::Serializer::new(&mut bytes_b); - original_b.serialize(&mut ser_b)?; - // VarInt for index 1, then i32 for 123 - let mut expected_bytes_b = vec![0x01]; - expected_bytes_b.extend_from_slice(&123i32.to_be_bytes()); - assert_eq!(bytes_b, expected_bytes_b); - let de_cursor_b = Cursor::new(bytes_b); - let deserialized_b: MyEnum = - MyEnum::deserialize(&mut deserializer::Deserializer::new(de_cursor_b))?; - assert_eq!(original_b, deserialized_b); - - let original_c = MyEnum::C { - x: 456, - y: "hello".to_string(), - }; - let mut bytes_c = Vec::new(); - let mut ser_c = serializer::Serializer::new(&mut bytes_c); - original_c.serialize(&mut ser_c)?; - // VarInt for index 2, then i32 for 456, then string "hello" - let mut expected_bytes_c = vec![0x02]; - expected_bytes_c.extend_from_slice(&456i32.to_be_bytes()); - expected_bytes_c.push(0x05); // VarInt for string length 5 - expected_bytes_c.extend_from_slice(b"hello"); - assert_eq!(bytes_c, expected_bytes_c); - let de_cursor_c = Cursor::new(bytes_c); - let deserialized_c: MyEnum = - MyEnum::deserialize(&mut deserializer::Deserializer::new(de_cursor_c))?; - assert_eq!(original_c, deserialized_c); - Ok(()) - } - - #[test] - fn tuple_struct_reserialize() -> Result<(), Box> { - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - struct MyTupleStruct(i32, String); - - let original = MyTupleStruct(789, "world".to_string()); - let mut bytes = Vec::new(); - let mut ser = serializer::Serializer::new(&mut bytes); - original.serialize(&mut ser)?; - - let mut expected_bytes = Vec::new(); - expected_bytes.extend_from_slice(&789i32.to_be_bytes()); - expected_bytes.push(0x05); // VarInt for string length 5 - expected_bytes.extend_from_slice(b"world"); - assert_eq!(bytes, expected_bytes); - - let de_cursor = Cursor::new(bytes); - let deserialized: MyTupleStruct = - MyTupleStruct::deserialize(&mut deserializer::Deserializer::new(de_cursor))?; - assert_eq!(original, deserialized); - Ok(()) - } - - #[test] - fn map_reserialize() -> Result<(), Box> { - use std::collections::HashMap; - - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] - struct MyMapStruct { - map: HashMap, - } - - let mut map = HashMap::new(); - map.insert("one".to_string(), 1); - map.insert("two".to_string(), 2); - - let original = MyMapStruct { map }; - - let mut bytes = Vec::new(); - let mut serializer = serializer::Serializer::new(&mut bytes); - original.serialize(&mut serializer)?; - - // Expected bytes: VarInt for map length (2), then key1, value1, key2, value2 - // Order of elements in HashMap is not guaranteed, so we check deserialized content - - let de_cursor = Cursor::new(bytes.clone()); // Clone bytes for potential debug - let deserialized: MyMapStruct = - MyMapStruct::deserialize(&mut deserializer::Deserializer::new(de_cursor))?; - - assert_eq!(original.map.len(), deserialized.map.len()); - for (k, v) in original.map { - assert_eq!(deserialized.map.get(&k), Some(&v)); - } - - // Test with an empty map - let empty_map_original = MyMapStruct { - map: HashMap::new(), - }; - let mut empty_map_bytes = Vec::new(); - let mut empty_map_ser = serializer::Serializer::new(&mut empty_map_bytes); - empty_map_original.serialize(&mut empty_map_ser)?; - assert_eq!(empty_map_bytes, vec![0x00]); // VarInt for length 0 - - let empty_map_de_cursor = Cursor::new(empty_map_bytes); - let empty_map_deserialized: MyMapStruct = - MyMapStruct::deserialize(&mut deserializer::Deserializer::new(empty_map_de_cursor))?; - assert_eq!(empty_map_original, empty_map_deserialized); - Ok(()) - } -} diff --git a/pumpkin-protocol/src/ser/serializer.rs b/pumpkin-protocol/src/ser/serializer.rs deleted file mode 100644 index 8e5e5d57c..000000000 --- a/pumpkin-protocol/src/ser/serializer.rs +++ /dev/null @@ -1,362 +0,0 @@ -use std::fmt::Display; - -use pumpkin_nbt::serializer::NbtWriteHelperJava; -use serde::{ - Serialize, - ser::{self}, -}; - -use super::{NetworkWriteExt, Write, WritingError}; - -pub struct Serializer { - pub write: W, -} - -impl Serializer { - pub const fn new(w: W) -> Self { - Self { write: w } - } -} - -impl ser::Error for WritingError { - fn custom(msg: T) -> Self { - Self::Message(msg.to_string()) - } -} - -// General notes on the serializer: -// -// Primitives are written as-is -// Strings automatically prepend a VarInt -// Enums are written as a VarInt of the index -// Structs are ignored -// Iterables' values are written in order, but NO information (e.g. size) about the -// iterable itself is written (list sizes should be a separate field) -impl ser::Serializer for &mut Serializer { - type Ok = (); - type Error = WritingError; - - type SerializeSeq = Self; - type SerializeTuple = Self; - type SerializeTupleStruct = Self; - type SerializeTupleVariant = Self; - type SerializeMap = Self; - type SerializeStruct = Self; - type SerializeStructVariant = Self; - - fn serialize_bool(self, v: bool) -> Result { - self.write.write_bool(v) - } - fn serialize_bytes(self, v: &[u8]) -> Result { - self.write.write_slice(v) - } - fn serialize_char(self, v: char) -> Result { - self.write.write_u32_be(v as u32) - } - fn serialize_f32(self, v: f32) -> Result { - self.write.write_f32_be(v) - } - fn serialize_f64(self, v: f64) -> Result { - self.write.write_f64_be(v) - } - fn serialize_i128(self, v: i128) -> Result { - self.write - .write_all(&v.to_be_bytes()) - .map_err(WritingError::IoError) - } - fn serialize_i16(self, v: i16) -> Result { - self.write.write_i16_be(v) - } - fn serialize_i32(self, v: i32) -> Result { - self.write.write_i32_be(v) - } - fn serialize_i64(self, v: i64) -> Result { - self.write.write_i64_be(v) - } - fn serialize_i8(self, v: i8) -> Result { - self.write.write_i8(v) - } - fn serialize_map(self, len: Option) -> Result { - let Some(len) = len else { - return Err(WritingError::Serde("Maps must have a known length".into())); - }; - - self.write.write_var_int(&len.try_into().map_err(|_| { - WritingError::Message(format!("{len} isn't representable as a VarInt")) - })?)?; - - Ok(self) - } - fn serialize_newtype_struct( - self, - name: &'static str, - value: &T, - ) -> Result { - // TODO: This is super sketchy... is there a way to do it better? Can we choose what - // serializer to use on a struct somehow from within the struct? - if name == "TextComponent" || name == "DialogNBT" { - let mut nbt_serializer = pumpkin_nbt::serializer::Serializer::new( - NbtWriteHelperJava::new(&mut self.write), - None, - ); - value.serialize(&mut nbt_serializer).map_err(|err| { - WritingError::Serde(format!("Failed to serialize {name} NBT: {err}")) - }) - } else { - value.serialize(self) - } - } - fn serialize_newtype_variant( - self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - value: &T, - ) -> Result { - self.write - .write_var_int(&variant_index.try_into().map_err(|_| { - WritingError::Message(format!("{variant_index} isn't representable as a VarInt")) - })?)?; - value.serialize(self) - } - fn serialize_none(self) -> Result { - self.write.write_bool(false) - } - fn serialize_seq(self, len: Option) -> Result { - let Some(len) = len else { - return Err(WritingError::Serde( - "Sequences must have a known length".into(), - )); - }; - - self.write.write_var_int(&len.try_into().map_err(|_| { - WritingError::Message(format!("{len} isn't representable as a VarInt")) - })?)?; - - Ok(self) - } - fn serialize_some(self, value: &T) -> Result { - self.write.write_bool(true)?; - value.serialize(self) - } - fn serialize_str(self, v: &str) -> Result { - self.write.write_string(v) - } - fn serialize_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - Ok(self) - } - fn serialize_struct_variant( - self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - // Serialize ENUM index as varint - self.write - .write_var_int(&variant_index.try_into().map_err(|_| { - WritingError::Message(format!("{variant_index} isn't representable as a VarInt")) - })?)?; - Ok(self) - } - fn serialize_tuple(self, _len: usize) -> Result { - Ok(self) - } - fn serialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - Ok(self) - } - fn serialize_tuple_variant( - self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - // Serialize ENUM index as varint - self.write - .write_var_int(&variant_index.try_into().map_err(|_| { - WritingError::Message(format!("{variant_index} isn't representable as a VarInt")) - })?)?; - Ok(self) - } - fn serialize_u128(self, v: u128) -> Result { - self.write - .write_all(&v.to_be_bytes()) - .map_err(WritingError::IoError) - } - fn serialize_u16(self, v: u16) -> Result { - self.write.write_u16_be(v) - } - fn serialize_u32(self, v: u32) -> Result { - self.write.write_u32_be(v) - } - fn serialize_u64(self, v: u64) -> Result { - self.write.write_u64_be(v) - } - fn serialize_u8(self, v: u8) -> Result { - self.write.write_u8(v) - } - fn serialize_unit(self) -> Result { - Ok(()) - } - fn serialize_unit_struct(self, _name: &'static str) -> Result { - Ok(()) - } - fn serialize_unit_variant( - self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - ) -> Result { - // For ENUMs, only write enum index as varint - self.write - .write_var_int(&variant_index.try_into().map_err(|_| { - WritingError::Message(format!("{variant_index} isn't representable as a VarInt")) - })?) - } - fn is_human_readable(&self) -> bool { - false - } -} - -impl ser::SerializeSeq for &mut Serializer { - // Must match the `Ok` type of the serializer. - type Ok = (); - // Must match the `Error` type of the serializer. - type Error = WritingError; - - // Serialize a single element of the sequence. - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> { - value.serialize(&mut **self) - } - - // Close the sequence. - fn end(self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl ser::SerializeTuple for &mut Serializer { - type Ok = (); - type Error = WritingError; - - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> { - value.serialize(&mut **self) - } - - fn end(self) -> Result<(), Self::Error> { - Ok(()) - } -} - -// Same thing but for tuple structs. -impl ser::SerializeTupleStruct for &mut Serializer { - type Ok = (); - type Error = WritingError; - - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> { - value.serialize(&mut **self) - } - - fn end(self) -> Result<(), Self::Error> { - Ok(()) - } -} - -// Tuple variants are a little different. Refer back to the -// `serialize_tuple_variant` method above: -// -// self.write += "{"; -// variant.serialize(&mut *self)?; -// self.write += ":["; -// -// So the `end` method in this impl is responsible for closing both the `]` and -// the `}`. -impl ser::SerializeTupleVariant for &mut Serializer { - type Ok = (); - type Error = WritingError; - - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> { - value.serialize(&mut **self) - } - - fn end(self) -> Result<(), Self::Error> { - Ok(()) - } -} - -// Some `Serialize` types are not able to hold a key and value in memory at the -// same time, so `SerializeMap` implementations are required to support -// `serialize_key` and `serialize_value` individually. -// -// There is a third optional method on the `SerializeMap` trait. The -// `serialize_entry` method allows serializers to optimize for the case where -// key and value are both available simultaneously. In JSON it doesn't make a -// difference, so the default behavior for `serialize_entry` is fine. -impl ser::SerializeMap for &mut Serializer { - type Ok = (); - type Error = WritingError; - - fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> { - key.serialize(&mut **self) - } - - fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> { - value.serialize(&mut **self) - } - - fn end(self) -> Result<(), Self::Error> { - Ok(()) - } -} - -// Structs are like maps in which the keys are constrained to be compile-time -// constant strings. -impl ser::SerializeStruct for &mut Serializer { - type Ok = (); - type Error = WritingError; - - fn serialize_field( - &mut self, - _key: &'static str, - value: &T, - ) -> Result<(), Self::Error> { - value.serialize(&mut **self) - } - - fn end(self) -> Result<(), Self::Error> { - Ok(()) - } - - fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { - let _ = key; - Ok(()) - } -} - -// Similar to `SerializeTupleVariant`, here the `end` method is responsible for -// closing both of the curly braces opened by `serialize_struct_variant`. -impl ser::SerializeStructVariant for &mut Serializer { - type Ok = (); - type Error = WritingError; - - fn serialize_field( - &mut self, - _key: &'static str, - value: &T, - ) -> Result<(), Self::Error> { - value.serialize(&mut **self) - } - - fn end(self) -> Result<(), Self::Error> { - Ok(()) - } -} diff --git a/pumpkin-world/src/chunk/format/mod.rs b/pumpkin-world/src/chunk/format/mod.rs index c76ca16c8..2a823764c 100644 --- a/pumpkin-world/src/chunk/format/mod.rs +++ b/pumpkin-world/src/chunk/format/mod.rs @@ -1,6 +1,7 @@ use std::{ path::PathBuf, pin::Pin, + str::FromStr, sync::{ RwLock, atomic::{AtomicBool, Ordering}, @@ -10,6 +11,7 @@ use std::{ use bytes::Bytes; use pumpkin_data::{Block, BlockStateId, chunk::ChunkStatus, fluid::Fluid}; use pumpkin_nbt::{compound::NbtCompound, nbt_long_array}; +use pumpkin_util::resource_location::{FromResourceLocation, ResourceLocation, ToResourceLocation}; use rustc_hash::FxHashMap; use tokio::sync::Mutex; @@ -21,7 +23,7 @@ use crate::{ }, generation::section_coords, level::LevelFolder, - tick::{ScheduledTick, scheduler::ChunkTickScheduler}, + tick::{ScheduledTick, TickPriority, scheduler::ChunkTickScheduler}, }; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector2::Vector2; @@ -73,7 +75,85 @@ impl Dirtiable for ChunkData { } } +fn extract_u16_array(tag: &pumpkin_nbt::tag::NbtTag) -> Option> { + match tag { + pumpkin_nbt::tag::NbtTag::IntArray(arr) => Some( + arr.iter() + .map(|&x| BlockStateId::new_or_air(x as u16)) + .collect(), + ), + pumpkin_nbt::tag::NbtTag::ByteArray(arr) => Some( + arr.iter() + .map(|&x| BlockStateId::new_or_air(x as u16)) + .collect(), + ), + pumpkin_nbt::tag::NbtTag::LongArray(arr) => Some( + arr.iter() + .map(|&x| BlockStateId::new_or_air(x as u16)) + .collect(), + ), + pumpkin_nbt::tag::NbtTag::List(list) => { + let ids: Box<[BlockStateId]> = list + .iter() + .map(|t| { + let val = match t { + pumpkin_nbt::tag::NbtTag::Int(x) => *x as u16, + pumpkin_nbt::tag::NbtTag::Short(x) => *x as u16, + pumpkin_nbt::tag::NbtTag::Byte(x) => *x as u16, + pumpkin_nbt::tag::NbtTag::Long(x) => *x as u16, + _ => 0, + }; + BlockStateId::new_or_air(val) + }) + .collect(); + Some(ids) + } + _ => None, + } +} + +fn extract_u8_array(tag: &pumpkin_nbt::tag::NbtTag) -> Option> { + match tag { + pumpkin_nbt::tag::NbtTag::ByteArray(arr) => Some(arr.iter().map(|&x| x as u8).collect()), + pumpkin_nbt::tag::NbtTag::IntArray(arr) => Some(arr.iter().map(|&x| x as u8).collect()), + pumpkin_nbt::tag::NbtTag::List(list) => { + let bytes: Box<[u8]> = list + .iter() + .map(|t| match t { + pumpkin_nbt::tag::NbtTag::Byte(x) => *x as u8, + pumpkin_nbt::tag::NbtTag::Int(x) => *x as u8, + pumpkin_nbt::tag::NbtTag::Short(x) => *x as u8, + _ => 0, + }) + .collect(); + Some(bytes) + } + _ => None, + } +} + +fn parse_scheduled_tick(nbt: &pumpkin_nbt::compound::NbtCompound) -> Option> +where + T: FromResourceLocation, +{ + let x = nbt.get_int("x")?; + let y = nbt.get_int("y")?; + let z = nbt.get_int("z")?; + let delay = nbt.get_int("t")? as u8; + let priority = TickPriority::try_from(nbt.get_int("p")?).ok()?; + let res_loc_str = nbt.get_string("i")?; + let res_loc = ResourceLocation::from_str(res_loc_str).ok()?; + let value = T::from_resource_location(&res_loc)?; + Some(ScheduledTick { + delay, + priority, + position: BlockPos::new(x, y, z), + value, + }) +} + impl ChunkData { + #[allow(clippy::too_many_lines)] pub fn internal_from_bytes( chunk_data: &[u8], position: Vector2, @@ -82,26 +162,47 @@ impl ChunkData { && chunk_data[0] == 0x0a && chunk_data[1] == 0x00 && chunk_data[2] == 0x00; - let chunk_data = if is_named { - pumpkin_nbt::from_bytes::(std::io::Cursor::new(chunk_data)) + + let mut cursor = std::io::Cursor::new(chunk_data); + let mut reader = pumpkin_nbt::deserializer::NbtReadHelperJava::new(&mut cursor); + let nbt = if is_named { + pumpkin_nbt::Nbt::read(&mut reader) } else { - pumpkin_nbt::from_bytes_unnamed::(std::io::Cursor::new(chunk_data)) + pumpkin_nbt::Nbt::read_unnamed(&mut reader) } .map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?; - if chunk_data.x_pos != position.x || chunk_data.z_pos != position.y { + let root_tag = nbt.root_tag; + + let x_pos = root_tag.get_int("xPos").ok_or_else(|| { + ChunkParsingError::ErrorDeserializingChunk("Missing xPos".to_string()) + })?; + let z_pos = root_tag.get_int("zPos").ok_or_else(|| { + ChunkParsingError::ErrorDeserializingChunk("Missing zPos".to_string()) + })?; + + if x_pos != position.x || z_pos != position.y { return Err(ChunkParsingError::ErrorDeserializingChunk(format!( "Expected data for chunk {},{} but got it for {},{}!", - position.x, position.y, chunk_data.x_pos, chunk_data.z_pos, + position.x, position.y, x_pos, z_pos, ))); } - let min_y_section = chunk_data.min_y_section; - let max_y_section = chunk_data - .sections - .iter() - .map(|s| s.y) - .max() - .unwrap_or(min_y_section as i8); + + let min_y_section = root_tag.get_int("yPos").ok_or_else(|| { + ChunkParsingError::ErrorDeserializingChunk("Missing yPos".to_string()) + })?; + + let mut max_y_section = min_y_section as i8; + if let Some(sections_list) = root_tag.get_list("sections") { + for section_tag in sections_list { + if let pumpkin_nbt::tag::NbtTag::Compound(section_compound) = section_tag { + let y = section_compound.get_byte("Y").unwrap_or(0); + if y > max_y_section { + max_y_section = y; + } + } + } + } let section_count = (max_y_section as i32 - min_y_section + 1).max(0) as usize; let mut block_lights = vec![LightContainer::Empty(0); section_count]; @@ -109,29 +210,61 @@ impl ChunkData { let mut block_palettes = vec![BlockPalette::default(); section_count]; let mut biome_palettes = vec![BiomePalette::default(); section_count]; - for section in chunk_data.sections { - let index = (section.y as i32 - min_y_section) as usize; - if index >= section_count { - continue; + if let Some(sections_list) = root_tag.get_list("sections") { + for section_tag in sections_list { + if let pumpkin_nbt::tag::NbtTag::Compound(section_compound) = section_tag { + let y = section_compound.get_byte("Y").unwrap_or(0); + let index = (y as i32 - min_y_section) as usize; + if index >= section_count { + continue; + } + + let block_light = section_compound + .get("BlockLight") + .and_then(|tag| tag.extract_byte_array()) + .map(|arr| arr.iter().map(|&x| x as u8).collect::>()); + + let sky_light = section_compound + .get("SkyLight") + .and_then(|tag| tag.extract_byte_array()) + .map(|arr| arr.iter().map(|&x| x as u8).collect::>()); + + block_lights[index] = + block_light.map_or(LightContainer::Empty(0), LightContainer::Full); + sky_lights[index] = + sky_light.map_or(LightContainer::Empty(0), LightContainer::Full); + + if let Some(bs_compound) = section_compound.get_compound("block_states") { + let data = bs_compound + .get_long_array("data") + .map(|arr| arr.to_vec().into_boxed_slice()); + let palette = bs_compound + .get("palette") + .and_then(extract_u16_array) + .unwrap_or_else(|| vec![BlockStateId::AIR].into_boxed_slice()); + + block_palettes[index] = + BlockPalette::from_disk_nbt(ChunkSectionBlockStates { data, palette }); + } else { + block_palettes[index] = BlockPalette::default(); + } + + if let Some(b_compound) = section_compound.get_compound("biomes") { + let data = b_compound + .get_long_array("data") + .map(|arr| arr.to_vec().into_boxed_slice()); + let palette = b_compound + .get("palette") + .and_then(extract_u8_array) + .unwrap_or_else(|| vec![0].into_boxed_slice()); + + biome_palettes[index] = + BiomePalette::from_disk_nbt(ChunkSectionBiomes { data, palette }); + } else { + biome_palettes[index] = BiomePalette::default(); + } + } } - - // When loading light data, missing data should default to 0 (no light) - block_lights[index] = section - .block_light - .map_or(LightContainer::Empty(0), LightContainer::Full); - sky_lights[index] = section - .sky_light - .map_or(LightContainer::Empty(0), LightContainer::Full); - - // Convert NBT to Palettes - block_palettes[index] = section - .block_states - .map(BlockPalette::from_disk_nbt) - .unwrap_or_default(); - biome_palettes[index] = section - .biomes - .map(BiomePalette::from_disk_nbt) - .unwrap_or_default(); } // Assemble the LightEngine @@ -141,7 +274,7 @@ impl ChunkData { }; // Assemble the ChunkSections - let min_y = section_coords::section_to_block(chunk_data.min_y_section); + let min_y = section_coords::section_to_block(min_y_section); let (random_tick_sections, randomly_ticking_mask) = ChunkSections::build_random_tick_sections_cache(&block_palettes); let section = ChunkSections { @@ -152,35 +285,99 @@ impl ChunkData { biome_sections: RwLock::new(biome_palettes.into_boxed_slice()), min_y, }; + + let heightmaps = root_tag.get_compound("Heightmaps").map_or( + ChunkHeightmaps { + world_surface: None, + motion_blocking: None, + motion_blocking_no_leaves: None, + }, + |h_compound| ChunkHeightmaps { + world_surface: h_compound + .get_long_array("WORLD_SURFACE") + .map(|a| a.to_vec().into_boxed_slice()), + motion_blocking: h_compound + .get_long_array("MOTION_BLOCKING") + .map(|a| a.to_vec().into_boxed_slice()), + motion_blocking_no_leaves: h_compound + .get_long_array("MOTION_BLOCKING_NO_LEAVES") + .map(|a| a.to_vec().into_boxed_slice()), + }, + ); + let mut block_ticks = Vec::new(); + if let Some(list) = root_tag.get_list("block_ticks") { + for tag in list { + if let pumpkin_nbt::tag::NbtTag::Compound(compound) = tag + && let Some(tick) = parse_scheduled_tick::<&'static Block>(compound) + { + block_ticks.push(tick); + } + } + } + + let mut fluid_ticks = Vec::new(); + if let Some(list) = root_tag.get_list("fluid_ticks") { + for tag in list { + if let pumpkin_nbt::tag::NbtTag::Compound(compound) = tag + && let Some(tick) = parse_scheduled_tick::<&'static Fluid>(compound) + { + fluid_ticks.push(tick); + } + } + } + + let mut block_entities = FxHashMap::default(); + if let Some(list) = root_tag.get_list("block_entities") { + for tag in list { + if let pumpkin_nbt::tag::NbtTag::Compound(nbt) = tag + && let Some(x) = nbt.get_int("x") + && let Some(y) = nbt.get_int("y") + && let Some(z) = nbt.get_int("z") + { + block_entities.insert(BlockPos::new(x, y, z), nbt.clone()); + } + } + } + + let light_correct = root_tag.get_bool("isLightOn").unwrap_or(false); + + let status_str = root_tag.get_string("Status").unwrap_or("minecraft:empty"); + let status = match status_str { + "minecraft:structure_starts" => ChunkStatus::StructureStarts, + "minecraft:structure_references" => ChunkStatus::StructureReferences, + "minecraft:biomes" => ChunkStatus::Biomes, + "minecraft:noise" => ChunkStatus::Noise, + "minecraft:surface" => ChunkStatus::Surface, + "minecraft:carvers" => ChunkStatus::Carvers, + "minecraft:features" => ChunkStatus::Features, + "minecraft:initialize_light" => ChunkStatus::InitializeLight, + "minecraft:light" => ChunkStatus::Light, + "minecraft:spawn" => ChunkStatus::Spawn, + "minecraft:full" => ChunkStatus::Full, + _ => ChunkStatus::Empty, + }; + Ok(Self { section, - heightmap: std::sync::Mutex::new(chunk_data.heightmaps), + heightmap: std::sync::Mutex::new(heightmaps), x: position.x, z: position.y, // This chunk is read from disk, so it has not been modified dirty: AtomicBool::new(false), - block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks), - fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks), - pending_block_entities: { - let mut block_entities = FxHashMap::default(); - for nbt in chunk_data.block_entities { - if let Some(x) = nbt.get_int("x") - && let Some(y) = nbt.get_int("y") - && let Some(z) = nbt.get_int("z") - { - block_entities.insert(BlockPos::new(x, y, z), nbt); - } - } - std::sync::Mutex::new(block_entities) - }, + block_ticks: ChunkTickScheduler::from_iter(block_ticks), + fluid_ticks: ChunkTickScheduler::from_iter(fluid_ticks), + pending_block_entities: std::sync::Mutex::new(block_entities), light_engine: std::sync::Mutex::new(light_engine), - light_populated: AtomicBool::new(chunk_data.light_correct), - status: chunk_data.status, + light_populated: AtomicBool::new(light_correct), + status, blending_data: None, }) } + #[allow(clippy::too_many_lines)] fn internal_to_bytes(&self) -> Result { + use pumpkin_nbt::tag::NbtTag; + fn extract_light_ref(light: Option<&LightContainer>) -> Option<&[u8]> { match light { Some(LightContainer::Full(data)) => Some(data.as_ref()), @@ -204,32 +401,126 @@ impl ChunkData { let min_section_y = (self.section.min_y >> 4) as i8; - let sections = (0..self.section.count) - .map(|i| ChunkSectionNbtRef { - y: i as i8 + min_section_y, - block_states: Some(block_lock[i].to_disk_nbt()), - biomes: Some(biome_lock[i].to_disk_nbt()), - block_light: extract_light_ref(light_lock.block_light.get(i)), - sky_light: extract_light_ref(light_lock.sky_light.get(i)), - }) - .collect::>(); + let mut root_compound = NbtCompound::new(); + root_compound.put_int("DataVersion", WORLD_DATA_VERSION); + root_compound.put_int("xPos", self.x); + root_compound.put_int("zPos", self.z); + root_compound.put_int("yPos", section_coords::block_to_section(self.section.min_y)); - let nbt_ref = ChunkNbtRef { - data_version: WORLD_DATA_VERSION, - x_pos: self.x, - z_pos: self.z, - min_y_section: section_coords::block_to_section(self.section.min_y), - status: &self.status, - heightmaps: &heightmap_lock, - sections, - block_ticks: &self.block_ticks.to_vec(), - fluid_ticks: &self.fluid_ticks.to_vec(), - block_entities: &block_entities_nbt, - light_correct: is_light_correct, + let status_str = match self.status { + ChunkStatus::Empty => "minecraft:empty", + ChunkStatus::StructureStarts => "minecraft:structure_starts", + ChunkStatus::StructureReferences => "minecraft:structure_references", + ChunkStatus::Biomes => "minecraft:biomes", + ChunkStatus::Noise => "minecraft:noise", + ChunkStatus::Surface => "minecraft:surface", + ChunkStatus::Carvers => "minecraft:carvers", + ChunkStatus::Features => "minecraft:features", + ChunkStatus::InitializeLight => "minecraft:initialize_light", + ChunkStatus::Light => "minecraft:light", + ChunkStatus::Spawn => "minecraft:spawn", + ChunkStatus::Full => "minecraft:full", }; + root_compound.put_string("Status", status_str.to_string()); + + let mut heightmaps_compound = NbtCompound::new(); + if let Some(ref arr) = heightmap_lock.world_surface { + heightmaps_compound.put("WORLD_SURFACE", NbtTag::LongArray(arr.to_vec())); + } + if let Some(ref arr) = heightmap_lock.motion_blocking { + heightmaps_compound.put("MOTION_BLOCKING", NbtTag::LongArray(arr.to_vec())); + } + if let Some(ref arr) = heightmap_lock.motion_blocking_no_leaves { + heightmaps_compound.put("MOTION_BLOCKING_NO_LEAVES", NbtTag::LongArray(arr.to_vec())); + } + root_compound.put_compound("Heightmaps", heightmaps_compound); + + let mut sections_list = Vec::new(); + for i in 0..self.section.count { + let mut section_comp = NbtCompound::new(); + let y_val = i as i8 + min_section_y; + section_comp.put_byte("Y", y_val); + + // block_states + let block_states_nbt = block_lock[i].to_disk_nbt(); + let mut bs_comp = NbtCompound::new(); + if let Some(ref data_arr) = block_states_nbt.data { + bs_comp.put("data", NbtTag::LongArray(data_arr.to_vec())); + } + let palette_tags: Vec = block_states_nbt + .palette + .iter() + .map(|id| NbtTag::Int(BlockStateId::as_u16(*id) as i32)) + .collect(); + bs_comp.put_list("palette", palette_tags); + section_comp.put_compound("block_states", bs_comp); + + // biomes + let biomes_nbt = biome_lock[i].to_disk_nbt(); + let mut b_comp = NbtCompound::new(); + if let Some(ref data_arr) = biomes_nbt.data { + b_comp.put("data", NbtTag::LongArray(data_arr.to_vec())); + } + let biome_palette_tags: Vec = biomes_nbt + .palette + .iter() + .map(|&val| NbtTag::Byte(val as i8)) + .collect(); + b_comp.put_list("palette", biome_palette_tags); + section_comp.put_compound("biomes", b_comp); + + // block_light + if let Some(light_data) = extract_light_ref(light_lock.block_light.get(i)) { + let bytes: Box<[i8]> = light_data.iter().map(|&x| x as i8).collect(); + section_comp.put("BlockLight", NbtTag::ByteArray(bytes)); + } + + // sky_light + if let Some(light_data) = extract_light_ref(light_lock.sky_light.get(i)) { + let bytes: Box<[i8]> = light_data.iter().map(|&x| x as i8).collect(); + section_comp.put("SkyLight", NbtTag::ByteArray(bytes)); + } + + sections_list.push(NbtTag::Compound(section_comp)); + } + root_compound.put_list("sections", sections_list); + + let mut block_ticks_list = Vec::new(); + for tick in self.block_ticks.to_vec() { + let mut tick_comp = NbtCompound::new(); + tick_comp.put_int("x", tick.position.0.x); + tick_comp.put_int("y", tick.position.0.y); + tick_comp.put_int("z", tick.position.0.z); + tick_comp.put_int("t", tick.delay as i32); + tick_comp.put_int("p", tick.priority as i32); + tick_comp.put_string("i", tick.value.to_resource_location()); + block_ticks_list.push(NbtTag::Compound(tick_comp)); + } + root_compound.put_list("block_ticks", block_ticks_list); + + let mut fluid_ticks_list = Vec::new(); + for tick in self.fluid_ticks.to_vec() { + let mut tick_comp = NbtCompound::new(); + tick_comp.put_int("x", tick.position.0.x); + tick_comp.put_int("y", tick.position.0.y); + tick_comp.put_int("z", tick.position.0.z); + tick_comp.put_int("t", tick.delay as i32); + tick_comp.put_int("p", tick.priority as i32); + tick_comp.put_string("i", tick.value.to_resource_location()); + fluid_ticks_list.push(NbtTag::Compound(tick_comp)); + } + root_compound.put_list("fluid_ticks", fluid_ticks_list); + + let mut block_entities_list = Vec::new(); + for entity_comp in block_entities_nbt { + block_entities_list.push(NbtTag::Compound(entity_comp)); + } + root_compound.put_list("block_entities", block_entities_list); + + root_compound.put_bool("isLightOn", is_light_correct); let mut result = Vec::new(); - pumpkin_nbt::to_bytes(&nbt_ref, &mut result) + pumpkin_nbt::serializer::to_bytes(&root_compound, &mut result) .map_err(ChunkSerializingError::ErrorSerializingChunk)?; Ok(result.into()) @@ -324,34 +615,6 @@ impl ChunkEntityData { } } -#[derive(Serialize, Deserialize)] -struct ChunkSectionNBT { - #[serde(skip_serializing_if = "Option::is_none")] - block_states: Option, - #[serde(skip_serializing_if = "Option::is_none")] - biomes: Option, - #[serde(rename = "BlockLight", skip_serializing_if = "Option::is_none")] - block_light: Option>, - #[serde(rename = "SkyLight", skip_serializing_if = "Option::is_none")] - sky_light: Option>, - #[serde(rename = "Y")] - y: i8, -} - -#[derive(Serialize)] -struct ChunkSectionNbtRef<'a> { - #[serde(skip_serializing_if = "Option::is_none")] - block_states: Option, - #[serde(skip_serializing_if = "Option::is_none")] - biomes: Option, - #[serde(rename = "BlockLight", skip_serializing_if = "Option::is_none")] - block_light: Option<&'a [u8]>, - #[serde(rename = "SkyLight", skip_serializing_if = "Option::is_none")] - sky_light: Option<&'a [u8]>, - #[serde(rename = "Y")] - y: i8, -} - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ChunkSectionBiomes { #[serde( @@ -477,55 +740,6 @@ impl Default for LightContainer { } } -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "PascalCase")] -struct ChunkNbt { - data_version: i32, - #[serde(rename = "xPos")] - x_pos: i32, - #[serde(rename = "zPos")] - z_pos: i32, - #[serde(rename = "yPos")] - min_y_section: i32, - status: ChunkStatus, - #[serde(rename = "sections")] - sections: Vec, - #[serde(default)] - heightmaps: ChunkHeightmaps, - #[serde(rename = "block_ticks", default)] - block_ticks: Vec>, - #[serde(rename = "fluid_ticks", default)] - fluid_ticks: Vec>, - #[serde(rename = "block_entities", default)] - block_entities: Vec, - #[serde(rename = "isLightOn", default)] - light_correct: bool, -} - -#[derive(Serialize)] -#[serde(rename_all = "PascalCase")] -struct ChunkNbtRef<'a> { - data_version: i32, - #[serde(rename = "xPos")] - x_pos: i32, - #[serde(rename = "zPos")] - z_pos: i32, - #[serde(rename = "yPos")] - min_y_section: i32, - status: &'a ChunkStatus, - #[serde(rename = "sections")] - sections: Vec>, - heightmaps: &'a ChunkHeightmaps, - #[serde(rename = "block_ticks")] - block_ticks: &'a [ScheduledTick<&'static Block>], - #[serde(rename = "fluid_ticks")] - fluid_ticks: &'a [ScheduledTick<&'static Fluid>], - #[serde(rename = "block_entities")] - block_entities: &'a [NbtCompound], - #[serde(rename = "isLightOn", default)] - light_correct: bool, -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "PascalCase")] struct EntityNbt { diff --git a/pumpkin-world/src/generation/structure/template/processor.rs b/pumpkin-world/src/generation/structure/template/processor.rs index 042672482..9caef29f2 100644 --- a/pumpkin-world/src/generation/structure/template/processor.rs +++ b/pumpkin-world/src/generation/structure/template/processor.rs @@ -13,26 +13,52 @@ pub enum StructureProcessor { BlockRot { integrity: f32, blocks: BlockTag }, Rules(Vec), ProtectedBlocks(BlockTag), + Capped { limit: i32, delegate: Box }, } #[derive(Clone)] pub struct ProcessorRule { - input_block: BlockId, + input: RuleInput, probability: f32, output_state: &'static BlockState, } +#[derive(Clone, Copy)] +pub enum RuleInput { + Block(BlockId), + Tag(BlockTag), +} + +impl RuleInput { + fn matches(self, block_id: BlockId) -> bool { + match self { + Self::Block(id) => id == block_id, + Self::Tag(tag) => tag.contains(block_id), + } + } +} + #[derive(Clone, Copy)] pub enum BlockTag { AncientCityReplaceable, FeaturesCannotReplace, + TrailRuinsReplaceable, + Doors, } impl BlockTag { fn from_name(name: &str) -> Option { match name { - "#minecraft:ancient_city_replaceable" => Some(Self::AncientCityReplaceable), - "#minecraft:features_cannot_replace" => Some(Self::FeaturesCannotReplace), + "#minecraft:ancient_city_replaceable" | "minecraft:ancient_city_replaceable" => { + Some(Self::AncientCityReplaceable) + } + "#minecraft:features_cannot_replace" | "minecraft:features_cannot_replace" => { + Some(Self::FeaturesCannotReplace) + } + "#minecraft:trail_ruins_replaceable" | "minecraft:trail_ruins_replaceable" => { + Some(Self::TrailRuinsReplaceable) + } + "#minecraft:doors" | "minecraft:doors" => Some(Self::Doors), _ => None, } } @@ -41,6 +67,8 @@ impl BlockTag { block_id.has_tag(match self { Self::AncientCityReplaceable => tag::Block::MINECRAFT_ANCIENT_CITY_REPLACEABLE, Self::FeaturesCannotReplace => tag::Block::MINECRAFT_FEATURES_CANNOT_REPLACE, + Self::TrailRuinsReplaceable => tag::Block::MINECRAFT_TRAIL_RUINS_REPLACEABLE, + Self::Doors => tag::Block::MINECRAFT_DOORS, }) } } @@ -67,7 +95,7 @@ impl StructureProcessor { rules .iter() .find(|rule| { - input_block == rule.input_block && random.next_f32() < rule.probability + rule.input.matches(input_block) && random.next_f32() < rule.probability }) .map_or(Some(state), |rule| Some(rule.output_state)) } @@ -75,6 +103,7 @@ impl StructureProcessor { let existing = chunk.get_block_state(&pos).to_block_id(); (!blocks.contains(existing)).then_some(state) } + Self::Capped { limit: _, delegate } => delegate.process(chunk, pos, state), } } } @@ -96,6 +125,8 @@ enum RawProcessor { Rule { rules: Vec }, #[serde(rename = "minecraft:protected_blocks")] ProtectedBlocks { value: String }, + #[serde(rename = "minecraft:capped")] + Capped { limit: i32, delegate: Box }, } #[derive(Deserialize)] @@ -106,8 +137,9 @@ struct RawRule { #[derive(Deserialize)] struct RawInputPredicate { - block: String, - probability: f32, + block: Option, + tag: Option, + probability: Option, } #[derive(Deserialize)] @@ -116,6 +148,55 @@ struct RawOutputState { name: String, } +fn convert_raw_processor(raw: RawProcessor) -> Option { + match raw { + RawProcessor::BlockRot { + integrity, + rottable_blocks, + } => BlockTag::from_name(&rottable_blocks) + .map(|blocks| StructureProcessor::BlockRot { integrity, blocks }), + RawProcessor::ProtectedBlocks { value } => { + BlockTag::from_name(&value).map(StructureProcessor::ProtectedBlocks) + } + RawProcessor::Rule { rules } => Some(StructureProcessor::Rules( + rules + .into_iter() + .filter_map(|rule| { + let output_name = rule + .output_state + .name + .strip_prefix("minecraft:") + .unwrap_or(&rule.output_state.name); + let output_block = Block::from_name(output_name)?; + + let input = if let Some(ref block_name) = rule.input_predicate.block { + let input_name = + block_name.strip_prefix("minecraft:").unwrap_or(block_name); + let input_block = Block::from_name(input_name)?; + RuleInput::Block(input_block.id) + } else { + let tag_name = rule.input_predicate.tag.as_ref()?; + let tag = BlockTag::from_name(tag_name)?; + RuleInput::Tag(tag) + }; + + Some(ProcessorRule { + input, + probability: rule.input_predicate.probability.unwrap_or(1.0), + output_state: output_block.default_state, + }) + }) + .collect(), + )), + RawProcessor::Capped { limit, delegate } => { + convert_raw_processor(*delegate).map(|proc| StructureProcessor::Capped { + limit, + delegate: Box::new(proc), + }) + } + } +} + #[must_use] pub fn load_processor_list(name: &str) -> Arc<[StructureProcessor]> { static CACHE: LazyLock>> = @@ -140,40 +221,7 @@ pub fn load_processor_list(name: &str) -> Arc<[StructureProcessor]> { let processors = raw .processors .into_iter() - .filter_map(|processor| match processor { - RawProcessor::BlockRot { - integrity, - rottable_blocks, - } => BlockTag::from_name(&rottable_blocks) - .map(|blocks| StructureProcessor::BlockRot { integrity, blocks }), - RawProcessor::ProtectedBlocks { value } => { - BlockTag::from_name(&value).map(StructureProcessor::ProtectedBlocks) - } - RawProcessor::Rule { rules } => Some(StructureProcessor::Rules( - rules - .into_iter() - .filter_map(|rule| { - let input_name = rule - .input_predicate - .block - .strip_prefix("minecraft:") - .unwrap_or(&rule.input_predicate.block); - let output_name = rule - .output_state - .name - .strip_prefix("minecraft:") - .unwrap_or(&rule.output_state.name); - let input_block = Block::from_name(input_name)?; - let output_block = Block::from_name(output_name)?; - Some(ProcessorRule { - input_block: input_block.id, - probability: rule.input_predicate.probability, - output_state: output_block.default_state, - }) - }) - .collect(), - )), - }) + .filter_map(convert_raw_processor) .collect::>(); CACHE.insert(name.to_owned(), Arc::clone(&processors)); processors @@ -198,4 +246,18 @@ mod tests { 3 ); } + + #[test] + fn parses_street_processor_lists() { + assert_eq!(load_processor_list("minecraft:street_plains").len(), 1); + assert_eq!(load_processor_list("minecraft:street_savanna").len(), 1); + } + + #[test] + fn parses_trail_ruins_processor_lists() { + assert_eq!( + load_processor_list("minecraft:trail_ruins_houses_archaeology").len(), + 3 + ); + } } diff --git a/pumpkin/src/entity/area_effect_cloud.rs b/pumpkin/src/entity/area_effect_cloud.rs index c943cadf9..423826ee2 100644 --- a/pumpkin/src/entity/area_effect_cloud.rs +++ b/pumpkin/src/entity/area_effect_cloud.rs @@ -13,6 +13,22 @@ type EffectEntry = (&'static StatusEffect, i32, u8, bool, bool, bool); use pumpkin_data::item_stack::ItemStack; use tokio::sync::Mutex; +struct ParticleMeta<'a> { + particle_id: pumpkin_protocol::codec::var_int::VarInt, + data: &'a [u8], +} + +impl pumpkin_protocol::java::client::play::MetadataSerializer for ParticleMeta<'_> { + fn write_metadata( + &self, + writer: &mut impl std::io::Write, + ) -> Result<(), pumpkin_protocol::ser::WritingError> { + use pumpkin_protocol::ser::NetworkWriteExt; + writer.write_var_int(&self.particle_id)?; + writer.write_slice(self.data) + } +} + /// The effect cloud entity that is spawned where a lingering potion lands. pub struct AreaEffectCloudEntity { pub entity: Entity, @@ -97,23 +113,7 @@ impl EntityBase for AreaEffectCloudEntity { } fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { - // Serialize bytes to the packet without a length prefix. - // This matches how the Minecraft protocol encodes particle data in EntityEffect. - fn serialize_bytes_no_prefix(data: &[u8], serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_bytes(data) - } - Box::pin(async move { - #[derive(serde::Serialize)] - struct ParticleMeta<'a> { - particle_id: pumpkin_protocol::codec::var_int::VarInt, - #[serde(serialize_with = "serialize_bytes_no_prefix")] - data: &'a [u8], - } - // Send initial radius and particle (color) so clients render correctly let radius = *self.radius.lock().await; diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 76312080a..6f38fcc4a 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -54,7 +54,7 @@ use pumpkin_protocol::{ codec::var_ulong::VarULong, java::client::play::{ CEntityPositionSync, CEntityVelocity, CHeadRot, CPlayerPosition, CSetEntityMetadata, - CSetPassengers, CSpawnEntity, CUpdateEntityRot, Metadata, + CSetPassengers, CSpawnEntity, CUpdateEntityRot, Metadata, MetadataSerializer, }, }; use pumpkin_util::math::vector3::Axis; @@ -68,7 +68,6 @@ use pumpkin_util::math::{ }; use pumpkin_util::text::TextComponent; use pumpkin_util::text::hover::HoverEvent; -use serde::Serialize; use std::collections::{BTreeMap, HashSet}; use std::pin::Pin; use std::sync::{ @@ -2788,7 +2787,7 @@ impl Entity { .play_sound(sound, SoundCategory::Neutral, &self.pos.load()); } - pub fn send_meta_data( + pub fn send_meta_data( &self, meta: &[Metadata], bedrock_meta: Option<&EntityMetadata>, diff --git a/pumpkin/src/entity/passive/villager/data.rs b/pumpkin/src/entity/passive/villager/data.rs index 70270a563..0946dad0f 100644 --- a/pumpkin/src/entity/passive/villager/data.rs +++ b/pumpkin/src/entity/passive/villager/data.rs @@ -26,13 +26,25 @@ pub enum GossipType { Trading = 4, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct VillagerData { pub r#type: VarInt, pub profession: VarInt, pub level: VarInt, } +impl pumpkin_protocol::java::client::play::MetadataSerializer for VillagerData { + fn write_metadata( + &self, + writer: &mut impl std::io::Write, + ) -> Result<(), pumpkin_protocol::ser::WritingError> { + use pumpkin_protocol::ser::NetworkWriteExt; + writer.write_var_int(&self.r#type)?; + writer.write_var_int(&self.profession)?; + writer.write_var_int(&self.level) + } +} + impl VillagerData { #[must_use] pub const fn new(r#type: VillagerType, profession: VillagerProfession, level: i32) -> Self { diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/item_stack.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/item_stack.rs index ef4a802d8..990f2f3c7 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/item_stack.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/item_stack.rs @@ -11,19 +11,13 @@ use std::sync::Arc; use tokio::sync::Mutex; use wasmtime::component::Resource; -use crate::plugin::loader::wasm::wasm_host::wit::v0_1::player::{ - WitSeqAccess, text_component_from_resource, -}; +use crate::plugin::loader::wasm::wasm_host::wit::v0_1::player::text_component_from_resource; use pumpkin_data::Enchantment; use pumpkin_data::data_component::DataComponent; use pumpkin_data::data_component_impl::{CustomNameImpl, EnchantmentsImpl}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; use pumpkin_protocol::codec::data_component::{deserialize, serialize}; -use pumpkin_protocol::ser::deserializer::Deserializer; -use pumpkin_protocol::ser::serializer::Serializer; -use serde::ser::SerializeStruct; -use serde::ser::Serializer as _; use std::borrow::Cow; pub(crate) fn to_wit_data_component(id: DataComponent) -> WitDataComponent { @@ -426,10 +420,7 @@ impl HostItemStack for PluginHostState { for (id, data) in &stack.patch { if let Some(data) = data { let mut buf = Vec::new(); - let mut serializer = Serializer::new(&mut buf); - let mut struct_ser = serializer.serialize_struct("", 0).unwrap(); - serialize(*id, data.as_ref(), &mut struct_ser).unwrap(); - struct_ser.end().unwrap(); + serialize(*id, data.as_ref(), &mut buf).unwrap(); components.push(WitDataComponentValue { component: to_wit_data_component(*id), @@ -449,12 +440,9 @@ impl HostItemStack for PluginHostState { let stack = self.get_item_stack(&res)?; let mut stack = stack.lock().await; let id = from_wit_data_component(component); - let mut deserializer = Deserializer::new(value.as_slice()); - let mut seq = WitSeqAccess { - deserializer: &mut deserializer, - }; + let mut cursor = std::io::Cursor::new(value); - if let Ok(component_impl) = deserialize(id, &mut seq) { + if let Ok(component_impl) = deserialize(id, &mut cursor) { if let Some((_, data)) = stack.patch.iter_mut().find(|(pid, _)| *pid == id) { *data = Some(component_impl); } else { diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs index 4840f9859..7199555d9 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs @@ -47,26 +47,6 @@ use pumpkin_protocol::bedrock::client::set_actor_data::{ use pumpkin_protocol::codec::var_ulong::VarULong; use pumpkin_util::version::{BedrockMinecraftVersion, JavaMinecraftVersion}; -use pumpkin_protocol::ser::ReadingError; -use pumpkin_protocol::ser::deserializer::Deserializer; -use serde::de::SeqAccess; - -pub(crate) struct WitSeqAccess<'a> { - pub(crate) deserializer: &'a mut Deserializer<&'a [u8]>, -} - -impl<'de> SeqAccess<'de> for WitSeqAccess<'_> { - type Error = ReadingError; - - fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> - where - T: serde::de::DeserializeSeed<'de>, - { - let val = seed.deserialize(&mut *self.deserializer)?; - Ok(Some(val)) - } -} - const fn to_wasm_java_version( version: JavaMinecraftVersion, ) -> pumpkin::plugin::player::JavaMinecraftVersion {