From 8d04394fbac856a343ebdf26d1a204cf0b74e87b Mon Sep 17 00:00:00 2001 From: Mrln Date: Sun, 11 Jun 2023 15:43:47 +0200 Subject: [PATCH] Update to main --- .github/pull_request_template.md | 17 - .github/workflows/ci.yml | 119 +- CONTRIBUTING.md | 147 +- Cargo.toml | 17 +- README.md | 5 +- crates/README.md | 1 + crates/valence/Cargo.toml | 4 +- crates/valence/benches/decode_array.rs | 2 +- crates/valence/benches/packet.rs | 54 +- crates/valence/benches/var_int.rs | 4 +- crates/valence/benches/var_long.rs | 4 +- crates/valence/examples/advancement.rs | 285 + crates/valence/examples/block_entities.rs | 11 +- crates/valence/examples/building.rs | 16 +- crates/valence/examples/chest.rs | 4 +- crates/valence/examples/combat.rs | 4 +- crates/valence/examples/conway.rs | 2 +- crates/valence/examples/death.rs | 4 +- crates/valence/examples/entity_hitbox.rs | 146 + crates/valence/examples/parkour.rs | 14 +- crates/valence/examples/resource_pack.rs | 9 +- crates/valence/examples/schem_saving.rs | 97 +- crates/valence/src/lib.rs | 23 +- crates/valence/src/tests.rs | 134 +- crates/valence/src/tests/client.rs | 17 +- crates/valence/src/tests/example.rs | 18 +- crates/valence/src/tests/inventory.rs | 217 +- crates/valence/src/tests/weather.rs | 48 +- crates/valence_advancement/Cargo.toml | 13 + crates/valence_advancement/README.md | 7 + crates/valence_advancement/src/event.rs | 30 + crates/valence_advancement/src/lib.rs | 481 ++ .../src/packet.rs} | 49 +- crates/valence_anvil/src/lib.rs | 4 +- crates/valence_block/Cargo.toml | 1 + crates/valence_block/build.rs | 107 +- crates/valence_block/src/lib.rs | 4 +- crates/valence_client/Cargo.toml | 3 +- crates/valence_client/src/action.rs | 31 +- crates/valence_client/src/chat.rs | 51 + crates/valence_client/src/command.rs | 43 +- crates/valence_client/src/custom_payload.rs | 58 + crates/valence_client/src/event_loop.rs | 20 +- crates/valence_client/src/hand_swing.rs | 49 + crates/valence_client/src/interact_block.rs | 73 + crates/valence_client/src/interact_entity.rs | 29 +- crates/valence_client/src/interact_item.rs | 50 + crates/valence_client/src/keepalive.rs | 14 +- crates/valence_client/src/lib.rs | 174 +- crates/valence_client/src/misc.rs | 159 - crates/valence_client/src/movement.rs | 70 +- crates/valence_client/src/op_level.rs | 30 + crates/valence_client/src/packet.rs | 468 ++ crates/valence_client/src/resource_pack.rs | 89 + crates/valence_client/src/settings.rs | 62 +- crates/valence_client/src/status.rs | 50 + crates/valence_client/src/teleport.rs | 35 +- crates/valence_client/src/title.rs | 94 + crates/valence_client/src/weather.rs | 4 +- crates/valence_core/Cargo.toml | 2 +- crates/valence_core/build/item.rs | 22 +- crates/valence_core/build/packet_id.rs | 9 +- crates/valence_core/build/sound.rs | 10 +- crates/valence_core/build/translation_key.rs | 2 +- crates/valence_core/src/aabb.rs | 30 + crates/valence_core/src/block_pos.rs | 2 +- crates/valence_core/src/chunk_pos.rs | 2 +- crates/valence_core/src/difficulty.rs | 2 +- crates/valence_core/src/direction.rs | 2 +- crates/valence_core/src/game_mode.rs | 2 +- crates/valence_core/src/hand.rs | 2 +- crates/valence_core/src/ident.rs | 8 +- crates/valence_core/src/item.rs | 4 +- crates/valence_core/src/lib.rs | 10 +- crates/valence_core/src/packet.rs | 654 -- crates/valence_core/src/packet/c2s.rs | 205 - .../src/packet/c2s/handshake/handshake.rs | 18 - .../src/packet/c2s/login/login_hello.rs | 9 - .../src/packet/c2s/login/login_key.rs | 7 - .../packet/c2s/login/login_query_response.rs | 9 - .../src/packet/c2s/play/advancement_tab.rs | 10 - .../src/packet/c2s/play/boat_paddle.rs | 7 - .../src/packet/c2s/play/book_update.rs | 9 - .../src/packet/c2s/play/button_click.rs | 7 - .../src/packet/c2s/play/chat_message.rs | 15 - .../src/packet/c2s/play/click_slot.rs | 33 - .../src/packet/c2s/play/client_command.rs | 22 - .../src/packet/c2s/play/client_settings.rs | 43 - .../src/packet/c2s/play/client_status.rs | 7 - .../packet/c2s/play/close_handled_screen.rs | 6 - .../src/packet/c2s/play/command_execution.rs | 21 - .../src/packet/c2s/play/craft_request.rs | 11 - .../c2s/play/creative_inventory_action.rs | 8 - .../src/packet/c2s/play/custom_payload.rs | 11 - .../src/packet/c2s/play/hand_swing.rs | 7 - .../src/packet/c2s/play/jigsaw_generating.rs | 10 - .../src/packet/c2s/play/keep_alive.rs | 6 - .../packet/c2s/play/message_acknowledgment.rs | 7 - .../packet/c2s/play/pick_from_inventory.rs | 7 - .../src/packet/c2s/play/play_pong.rs | 6 - .../src/packet/c2s/play/player_action.rs | 23 - .../src/packet/c2s/play/player_input.rs | 19 - .../packet/c2s/play/player_interact_block.rs | 17 - .../packet/c2s/play/player_interact_entity.rs | 19 - .../packet/c2s/play/player_interact_item.rs | 9 - .../src/packet/c2s/play/player_move.rs | 29 - .../src/packet/c2s/play/player_session.rs | 12 - .../src/packet/c2s/play/query_block_nbt.rs | 9 - .../src/packet/c2s/play/query_entity_nbt.rs | 8 - .../src/packet/c2s/play/recipe_book_data.rs | 9 - .../c2s/play/recipe_category_options.rs | 16 - .../src/packet/c2s/play/rename_item.rs | 6 - .../c2s/play/request_command_completions.rs | 8 - .../packet/c2s/play/resource_pack_status.rs | 9 - .../packet/c2s/play/select_merchant_trade.rs | 7 - .../src/packet/c2s/play/spectator_teleport.rs | 8 - .../src/packet/c2s/play/teleport_confirm.rs | 7 - .../src/packet/c2s/play/update_beacon.rs | 8 - .../packet/c2s/play/update_command_block.rs | 29 - .../c2s/play/update_command_block_minecart.rs | 9 - .../src/packet/c2s/play/update_difficulty.rs | 7 - .../packet/c2s/play/update_difficulty_lock.rs | 6 - .../src/packet/c2s/play/update_jigsaw.rs | 15 - .../c2s/play/update_player_abilities.rs | 9 - .../packet/c2s/play/update_selected_slot.rs | 6 - .../src/packet/c2s/play/update_sign.rs | 8 - .../packet/c2s/play/update_structure_block.rs | 62 - .../src/packet/c2s/play/vehicle_move.rs | 10 - .../src/packet/c2s/status/query_ping.rs | 6 - .../src/packet/c2s/status/query_request.rs | 4 - .../src/packet/message_signature.rs | 40 - crates/valence_core/src/packet/s2c.rs | 379 - .../src/packet/s2c/login/login_compression.rs | 7 - .../src/packet/s2c/login/login_disconnect.rs | 9 - .../src/packet/s2c/login/login_hello.rs | 8 - .../packet/s2c/login/login_query_request.rs | 13 - .../src/packet/s2c/login/login_success.rs | 13 - .../s2c/play/block_breaking_progress.rs | 10 - .../packet/s2c/play/block_entity_update.rs | 14 - .../src/packet/s2c/play/block_event.rs | 11 - .../src/packet/s2c/play/block_update.rs | 9 - .../src/packet/s2c/play/boss_bar.rs | 57 - .../src/packet/s2c/play/bundle_splitter.rs | 4 - .../src/packet/s2c/play/chat_message.rs | 100 - .../src/packet/s2c/play/chat_suggestions.rs | 16 - .../src/packet/s2c/play/chunk_biome_data.rs | 16 - .../src/packet/s2c/play/chunk_data.rs | 31 - .../src/packet/s2c/play/chunk_delta_update.rs | 11 - .../packet/s2c/play/chunk_load_distance.rs | 7 - .../s2c/play/chunk_render_distance_center.rs | 8 - .../src/packet/s2c/play/clear_title.rs | 6 - .../src/packet/s2c/play/close_screen.rs | 7 - .../packet/s2c/play/command_suggestions.rs | 19 - .../src/packet/s2c/play/command_tree.rs | 441 - .../src/packet/s2c/play/cooldown_update.rs | 8 - .../packet/s2c/play/craft_failed_response.rs | 10 - .../src/packet/s2c/play/custom_payload.rs | 11 - .../src/packet/s2c/play/damage_tilt.rs | 10 - .../src/packet/s2c/play/death_message.rs | 13 - .../src/packet/s2c/play/difficulty.rs | 8 - .../src/packet/s2c/play/disconnect.rs | 9 - .../src/packet/s2c/play/end_combat.rs | 9 - .../src/packet/s2c/play/enter_combat.rs | 5 - .../src/packet/s2c/play/entities_destroy.rs | 9 - .../src/packet/s2c/play/entity_animation.rs | 8 - .../src/packet/s2c/play/entity_attach.rs | 7 - .../src/packet/s2c/play/entity_attributes.rs | 27 - .../src/packet/s2c/play/entity_damage.rs | 25 - .../s2c/play/entity_equipment_update.rs | 60 - .../src/packet/s2c/play/entity_move.rs | 27 - .../packet/s2c/play/entity_passengers_set.rs | 9 - .../src/packet/s2c/play/entity_position.rs | 14 - .../packet/s2c/play/entity_set_head_yaw.rs | 9 - .../src/packet/s2c/play/entity_spawn.rs | 19 - .../src/packet/s2c/play/entity_status.rs | 7 - .../packet/s2c/play/entity_status_effect.rs | 25 - .../packet/s2c/play/entity_tracker_update.rs | 9 - .../packet/s2c/play/entity_velocity_update.rs | 8 - .../packet/s2c/play/experience_bar_update.rs | 9 - .../packet/s2c/play/experience_orb_spawn.rs | 11 - .../src/packet/s2c/play/explosion.rs | 11 - .../src/packet/s2c/play/features.rs | 9 - .../src/packet/s2c/play/game_join.rs | 31 - .../src/packet/s2c/play/game_message.rs | 11 - .../src/packet/s2c/play/game_state_change.rs | 23 - .../src/packet/s2c/play/health_update.rs | 9 - .../src/packet/s2c/play/inventory.rs | 13 - .../packet/s2c/play/item_pickup_animation.rs | 9 - .../src/packet/s2c/play/keep_alive.rs | 6 - .../src/packet/s2c/play/light_update.rs | 16 - .../src/packet/s2c/play/look_at.rs | 23 - .../src/packet/s2c/play/map_update.rs | 113 - .../src/packet/s2c/play/nbt_query_response.rs | 10 - .../src/packet/s2c/play/open_horse_screen.rs | 9 - .../src/packet/s2c/play/open_screen.rs | 40 - .../src/packet/s2c/play/open_written_book.rs | 7 - .../src/packet/s2c/play/overlay_message.rs | 9 - .../src/packet/s2c/play/play_ping.rs | 6 - .../src/packet/s2c/play/play_sound.rs | 60 - .../packet/s2c/play/play_sound_from_entity.rs | 13 - .../src/packet/s2c/play/player_abilities.rs | 21 - .../packet/s2c/play/player_action_response.rs | 7 - .../src/packet/s2c/play/player_list_header.rs | 10 - .../packet/s2c/play/player_position_look.rs | 26 - .../src/packet/s2c/play/player_remove.rs | 10 - .../src/packet/s2c/play/player_respawn.rs | 19 - .../src/packet/s2c/play/player_spawn.rs | 15 - .../packet/s2c/play/player_spawn_position.rs | 8 - .../s2c/play/profileless_chat_message.rs | 13 - .../s2c/play/remove_entity_status_effect.rs | 8 - .../src/packet/s2c/play/remove_message.rs | 7 - .../src/packet/s2c/play/resource_pack_send.rs | 12 - .../src/packet/s2c/play/scoreboard_display.rs | 73 - .../s2c/play/scoreboard_objective_update.rs | 27 - .../s2c/play/scoreboard_player_update.rs | 19 - .../play/screen_handler_property_update.rs | 8 - .../s2c/play/screen_handler_slot_update.rs | 13 - .../packet/s2c/play/select_advancement_tab.rs | 9 - .../src/packet/s2c/play/server_metadata.rs | 11 - .../src/packet/s2c/play/set_camera_entity.rs | 7 - .../src/packet/s2c/play/set_trade_offers.rs | 27 - .../src/packet/s2c/play/sign_editor_open.rs | 7 - .../packet/s2c/play/simulation_distance.rs | 7 - .../src/packet/s2c/play/statistics.rs | 14 - .../src/packet/s2c/play/stop_sound.rs | 51 - .../src/packet/s2c/play/subtitle.rs | 9 - .../packet/s2c/play/synchronize_recipes.rs | 414 - .../src/packet/s2c/play/synchronize_tags.rs | 22 - .../valence_core/src/packet/s2c/play/team.rs | 232 - .../valence_core/src/packet/s2c/play/title.rs | 9 - .../src/packet/s2c/play/title_fade.rs | 11 - .../src/packet/s2c/play/unload_chunk.rs | 7 - .../src/packet/s2c/play/unlock_recipes.rs | 94 - .../packet/s2c/play/update_selected_slot.rs | 6 - .../src/packet/s2c/play/vehicle_move.rs | 10 - .../s2c/play/world_border_center_changed.rs | 7 - .../s2c/play/world_border_initialize.rs | 15 - .../s2c/play/world_border_interpolate_size.rs | 9 - .../s2c/play/world_border_size_changed.rs | 6 - .../world_border_warning_blocks_changed.rs | 7 - .../play/world_border_warning_time_changed.rs | 7 - .../src/packet/s2c/play/world_event.rs | 10 - .../src/packet/s2c/play/world_time_update.rs | 11 - .../src/packet/s2c/status/query_pong.rs | 6 - .../src/packet/s2c/status/query_response.rs | 6 - .../src/{packet/s2c/play => }/particle.rs | 25 +- crates/valence_core/src/property.rs | 2 +- crates/valence_core/src/protocol.rs | 351 + .../src/{packet => protocol}/array.rs | 4 +- .../src/{packet => protocol}/byte_angle.rs | 2 +- crates/valence_core/src/protocol/c2s.rs | 156 + .../src/{packet => protocol}/decode.rs | 111 +- .../src/{packet => protocol}/encode.rs | 77 +- .../src/{packet => protocol}/global_pos.rs | 2 +- .../src/{packet => protocol}/impls.rs | 19 +- crates/valence_core/src/protocol/packet.rs | 1374 ++++ .../src/{packet => protocol}/raw.rs | 25 +- crates/valence_core/src/protocol/s2c.rs | 338 + .../src/{packet => protocol}/var_int.rs | 5 +- .../src/{packet => protocol}/var_long.rs | 2 +- crates/valence_core/src/sound.rs | 45 - crates/valence_core/src/text.rs | 2 +- crates/valence_core_macros/src/lib.rs | 40 +- crates/valence_core_macros/src/packet.rs | 106 +- crates/valence_entity/Cargo.toml | 15 +- crates/valence_entity/build.rs | 37 +- crates/valence_entity/src/hitbox.rs | 804 +- crates/valence_entity/src/lib.rs | 14 +- crates/valence_entity/src/packet.rs | 290 + crates/valence_instance/src/chunk.rs | 15 +- crates/valence_instance/src/lib.rs | 91 +- crates/valence_instance/src/packet.rs | 194 + .../src/paletted_container.rs | 4 +- crates/valence_inventory/src/lib.rs | 85 +- crates/valence_inventory/src/packet.rs | 758 ++ crates/valence_inventory/src/validate.rs | 61 +- crates/valence_nbt/Cargo.toml | 13 +- crates/valence_nbt/README.md | 44 +- crates/valence_nbt/src/binary.rs | 48 + .../{to_binary_writer.rs => binary/decode.rs} | 141 +- .../encode.rs} | 47 +- crates/valence_nbt/src/{ => binary}/error.rs | 4 +- .../src/{ => binary}/modified_utf8.rs | 0 crates/valence_nbt/src/{ => binary}/tests.rs | 29 +- crates/valence_nbt/src/compound.rs | 147 +- crates/valence_nbt/src/lib.rs | 19 +- crates/valence_nbt/src/serde.rs | 160 + crates/valence_nbt/src/serde/de.rs | 356 + crates/valence_nbt/src/serde/ser.rs | 625 ++ crates/valence_network/src/connect.rs | 79 +- crates/valence_network/src/lib.rs | 1 + crates/valence_network/src/packet.rs | 105 + crates/valence_network/src/packet_io.rs | 52 +- crates/valence_player_list/Cargo.toml | 4 +- crates/valence_player_list/src/lib.rs | 38 +- .../src/packet.rs} | 39 +- crates/valence_registry/Cargo.toml | 4 +- crates/valence_registry/src/lib.rs | 11 +- crates/valence_registry/src/tags.rs | 84 + crates/valence_schem/src/lib.rs | 12 +- extracted/blocks.json | 160 +- extracted/tags.json | 7235 +++++++++++++++++ .../main/java/rs/valence/extractor/Main.java | 20 +- .../valence/extractor/extractors/Blocks.java | 4 +- .../rs/valence/extractor/extractors/Tags.java | 90 + tools/packet_inspector/Cargo.toml | 3 +- tools/packet_inspector/src/context.rs | 52 +- tools/packet_inspector/src/main.rs | 38 +- tools/packet_inspector/src/packet_groups.rs | 249 + tools/packet_inspector/src/state.rs | 6 +- tools/playground/Cargo.toml | 9 +- tools/playground/src/main.rs | 11 +- 312 files changed, 16889 insertions(+), 6894 deletions(-) create mode 100644 crates/valence/examples/advancement.rs create mode 100644 crates/valence/examples/entity_hitbox.rs create mode 100644 crates/valence_advancement/Cargo.toml create mode 100644 crates/valence_advancement/README.md create mode 100644 crates/valence_advancement/src/event.rs create mode 100644 crates/valence_advancement/src/lib.rs rename crates/{valence_core/src/packet/s2c/play/advancement_update.rs => valence_advancement/src/packet.rs} (65%) create mode 100644 crates/valence_client/src/chat.rs create mode 100644 crates/valence_client/src/custom_payload.rs create mode 100644 crates/valence_client/src/hand_swing.rs create mode 100644 crates/valence_client/src/interact_block.rs create mode 100644 crates/valence_client/src/interact_item.rs delete mode 100644 crates/valence_client/src/misc.rs create mode 100644 crates/valence_client/src/op_level.rs create mode 100644 crates/valence_client/src/packet.rs create mode 100644 crates/valence_client/src/resource_pack.rs create mode 100644 crates/valence_client/src/status.rs create mode 100644 crates/valence_client/src/title.rs delete mode 100644 crates/valence_core/src/packet.rs delete mode 100644 crates/valence_core/src/packet/c2s.rs delete mode 100644 crates/valence_core/src/packet/c2s/handshake/handshake.rs delete mode 100644 crates/valence_core/src/packet/c2s/login/login_hello.rs delete mode 100644 crates/valence_core/src/packet/c2s/login/login_key.rs delete mode 100644 crates/valence_core/src/packet/c2s/login/login_query_response.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/advancement_tab.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/boat_paddle.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/book_update.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/button_click.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/chat_message.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/click_slot.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/client_command.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/client_settings.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/client_status.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/close_handled_screen.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/command_execution.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/craft_request.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/creative_inventory_action.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/custom_payload.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/hand_swing.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/jigsaw_generating.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/keep_alive.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/message_acknowledgment.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/pick_from_inventory.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/play_pong.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/player_action.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/player_input.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/player_interact_block.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/player_interact_entity.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/player_interact_item.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/player_move.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/player_session.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/query_block_nbt.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/query_entity_nbt.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/recipe_book_data.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/recipe_category_options.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/rename_item.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/request_command_completions.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/resource_pack_status.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/select_merchant_trade.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/spectator_teleport.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/teleport_confirm.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_beacon.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_command_block.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_command_block_minecart.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_difficulty.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_difficulty_lock.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_jigsaw.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_player_abilities.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_selected_slot.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_sign.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/update_structure_block.rs delete mode 100644 crates/valence_core/src/packet/c2s/play/vehicle_move.rs delete mode 100644 crates/valence_core/src/packet/c2s/status/query_ping.rs delete mode 100644 crates/valence_core/src/packet/c2s/status/query_request.rs delete mode 100644 crates/valence_core/src/packet/message_signature.rs delete mode 100644 crates/valence_core/src/packet/s2c.rs delete mode 100644 crates/valence_core/src/packet/s2c/login/login_compression.rs delete mode 100644 crates/valence_core/src/packet/s2c/login/login_disconnect.rs delete mode 100644 crates/valence_core/src/packet/s2c/login/login_hello.rs delete mode 100644 crates/valence_core/src/packet/s2c/login/login_query_request.rs delete mode 100644 crates/valence_core/src/packet/s2c/login/login_success.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/block_breaking_progress.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/block_entity_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/block_event.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/block_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/boss_bar.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/bundle_splitter.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/chat_message.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/chat_suggestions.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/chunk_biome_data.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/chunk_data.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/chunk_delta_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/chunk_load_distance.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/chunk_render_distance_center.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/clear_title.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/close_screen.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/command_suggestions.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/command_tree.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/cooldown_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/craft_failed_response.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/custom_payload.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/damage_tilt.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/death_message.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/difficulty.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/disconnect.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/end_combat.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/enter_combat.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entities_destroy.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_animation.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_attach.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_attributes.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_damage.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_equipment_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_move.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_passengers_set.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_position.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_set_head_yaw.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_spawn.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_status.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_status_effect.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_tracker_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/entity_velocity_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/experience_bar_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/experience_orb_spawn.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/explosion.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/features.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/game_join.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/game_message.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/game_state_change.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/health_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/inventory.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/item_pickup_animation.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/keep_alive.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/light_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/look_at.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/map_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/nbt_query_response.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/open_horse_screen.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/open_screen.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/open_written_book.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/overlay_message.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/play_ping.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/play_sound.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/play_sound_from_entity.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_abilities.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_action_response.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_list_header.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_position_look.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_remove.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_respawn.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_spawn.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/player_spawn_position.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/profileless_chat_message.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/remove_entity_status_effect.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/remove_message.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/resource_pack_send.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/scoreboard_display.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/scoreboard_objective_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/scoreboard_player_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/screen_handler_property_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/screen_handler_slot_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/select_advancement_tab.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/server_metadata.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/set_camera_entity.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/set_trade_offers.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/sign_editor_open.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/simulation_distance.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/statistics.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/stop_sound.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/subtitle.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/synchronize_recipes.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/synchronize_tags.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/team.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/title.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/title_fade.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/unload_chunk.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/unlock_recipes.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/update_selected_slot.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/vehicle_move.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_border_center_changed.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_border_initialize.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_border_interpolate_size.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_border_size_changed.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_border_warning_blocks_changed.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_border_warning_time_changed.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_event.rs delete mode 100644 crates/valence_core/src/packet/s2c/play/world_time_update.rs delete mode 100644 crates/valence_core/src/packet/s2c/status/query_pong.rs delete mode 100644 crates/valence_core/src/packet/s2c/status/query_response.rs rename crates/valence_core/src/{packet/s2c/play => }/particle.rs (98%) create mode 100644 crates/valence_core/src/protocol.rs rename crates/valence_core/src/{packet => protocol}/array.rs (93%) rename crates/valence_core/src/{packet => protocol}/byte_angle.rs (95%) create mode 100644 crates/valence_core/src/protocol/c2s.rs rename crates/valence_core/src/{packet => protocol}/decode.rs (60%) rename crates/valence_core/src/{packet => protocol}/encode.rs (83%) rename crates/valence_core/src/{packet => protocol}/global_pos.rs (85%) rename crates/valence_core/src/{packet => protocol}/impls.rs (97%) create mode 100644 crates/valence_core/src/protocol/packet.rs rename crates/valence_core/src/{packet => protocol}/raw.rs (58%) create mode 100644 crates/valence_core/src/protocol/s2c.rs rename crates/valence_core/src/{packet => protocol}/var_int.rs (97%) rename crates/valence_core/src/{packet => protocol}/var_long.rs (99%) delete mode 100644 crates/valence_core/src/sound.rs create mode 100644 crates/valence_entity/src/packet.rs create mode 100644 crates/valence_instance/src/packet.rs create mode 100644 crates/valence_inventory/src/packet.rs create mode 100644 crates/valence_nbt/src/binary.rs rename crates/valence_nbt/src/{to_binary_writer.rs => binary/decode.rs} (67%) rename crates/valence_nbt/src/{from_binary_slice.rs => binary/encode.rs} (89%) rename crates/valence_nbt/src/{ => binary}/error.rs (91%) rename crates/valence_nbt/src/{ => binary}/modified_utf8.rs (100%) rename crates/valence_nbt/src/{ => binary}/tests.rs (79%) create mode 100644 crates/valence_nbt/src/serde.rs create mode 100644 crates/valence_nbt/src/serde/de.rs create mode 100644 crates/valence_nbt/src/serde/ser.rs create mode 100644 crates/valence_network/src/packet.rs rename crates/{valence_core/src/packet/s2c/play/player_list.rs => valence_player_list/src/packet.rs} (77%) create mode 100644 crates/valence_registry/src/tags.rs create mode 100644 extracted/tags.json create mode 100644 extractor/src/main/java/rs/valence/extractor/extractors/Tags.java create mode 100644 tools/packet_inspector/src/packet_groups.rs diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f6c18d6f0..4917846ae 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,20 +1,3 @@ ## Description Describe the changes you've made. Link to any issues this PR fixes or addresses. - -## Test Plan - -Explain the steps necessary to test your changes. If you used a playground, include the code in the details below. - -Steps: -1. - -
- -Playground - -```rust -PASTE YOUR PLAYGROUND CODE HERE -``` - -
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66e0b7d40..9b1e7888b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,53 +1,110 @@ name: Rust on: - push: - branches: ["main"] pull_request: - branches: ["main"] + push: # Run CI on the main branch after every merge. This is important to fill the GitHub Actions cache in a way that pull requests can see it + branches: + - main env: CARGO_TERM_COLOR: always jobs: - build: + valence-fmt-clippy: strategy: - fail-fast: false + fail-fast: true matrix: - os: [ubuntu-latest, windows-latest, macos-11] + platform: [windows-latest, macos-latest, ubuntu-latest] + style: [default] + rust: + - stable + include: + - style: default + flags: "" - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy, rustfmt - - run: cp tools/playground/src/playground.template.rs tools/playground/src/playground.rs - - name: Set up cargo cache - uses: actions/cache@v3 - continue-on-error: false + + - name: Setup Rust toolchain and cache + uses: actions-rust-lang/setup-rust-toolchain@v1.4.4 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-target-${{ hashFiles('**/Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-cargo-target-${{ hashFiles('**/Cargo.toml') }} - ${{ runner.os }}-cargo-target + toolchain: "stable" + components: "clippy, rustfmt" + + - name: "Copy playground" + run: cp tools/playground/src/playground.template.rs tools/playground/src/playground.rs + - name: Install Dependencies (Linux) run: sudo apt-get update && sudo apt-get install -y libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libclang-dev libgtk-3-dev - if: matrix.os == 'ubuntu-latest' - - name: Validate formatting + if: matrix.platform == 'ubuntu-latest' + + - name: cargo fmt run: cargo fmt --all -- --check - - name: Validate documentation - run: cargo doc --workspace --no-deps --all-features --document-private-items - - name: Run clippy lints - run: cargo clippy --workspace --no-deps --all-features --all-targets -- -D warnings + + - name: Clippy + run: cargo clippy --workspace ${{ matrix.flags }}--no-deps --all-features --all-targets -- -D warnings + + valence-tests: + strategy: + fail-fast: true + matrix: + platform: [windows-latest, macos-latest, ubuntu-latest] + style: [default] + rust: + - stable + include: + - style: default + flags: "" + + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v3 + + - name: Setup Rust toolchain and cache + uses: actions-rust-lang/setup-rust-toolchain@v1.4.4 + + - name: "Copy playground" + run: cp tools/playground/src/playground.template.rs tools/playground/src/playground.rs + + - name: Install Dependencies (Linux) + run: sudo apt-get update && sudo apt-get install -y libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libclang-dev libgtk-3-dev + if: matrix.platform == 'ubuntu-latest' + - name: Run tests - run: cargo test --workspace --all-features --all-targets + run: cargo test --workspace ${{ matrix.flags }}--all-features --all-targets + - name: Run valence_nbt tests without preserve_order feature run: cargo test -p valence_nbt --all-targets + + valence-docs: + strategy: + fail-fast: true + matrix: + platform: [windows-latest, macos-latest, ubuntu-latest] + style: [default] + rust: + - stable + include: + - style: default + flags: "" + + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v3 + + - name: Setup Rust toolchain and cache + uses: actions-rust-lang/setup-rust-toolchain@v1.4.4 + + - name: "Copy playground" + run: cp tools/playground/src/playground.template.rs tools/playground/src/playground.rs + + - name: Install Dependencies (Linux) + run: sudo apt-get update && sudo apt-get install -y libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libclang-dev libgtk-3-dev + if: matrix.platform == 'ubuntu-latest' + + - name: Validate documentation + run: cargo doc --workspace ${{ matrix.flags }}--no-deps --all-features --document-private-items diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66a8d3839..877044894 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -Valence has a public Discord server [here](https://discord.gg/8Fqqy9XrYb). Check it out if you have additional questions +Valence has a public Discord server [here](https://discord.gg/8Fqqy9XrYb) and GitHub discussions [here](https://github.com/valence-rs/valence/discussions). Check those out if you have additional questions or comments. # What version of Rust should I use? @@ -7,11 +7,11 @@ To _use_ Valence, only the most recent stable version of Rust is required. Howev unstable `rustfmt` settings are enabled in the project. To run `rustfmt` with the nightly toolchain, use the `cargo +nightly fmt` command. -# What issues can I work on? +# What issues should I work on? Issues labelled [good first issue](https://github.com/valence-rs/valence/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) -are a good place to start. This label is reserved for issues that shouldn't require too much specialized domain +are a good place to start. This label is reserved for issues that are relatively uncontroversial and shouldn't require too much specialized domain knowledge to complete. New contributors are not required to start with these issues. If you plan to work on something that's not an open issue, consider making one first so that it can be discussed. This @@ -43,7 +43,7 @@ If an error does occur, it must be fixed before the pull request can be merged. Here are some rules you should follow for your code. Generally the goal here is to be consistent with existing code, the standard library, and the Rust ecosystem as a whole. Nonconforming code is not necessarily a blocker for accepting your -contribution. It's just nice to have. +contribution, but conformance is advised. These guidelines are intended to complement the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/naming.html). @@ -83,7 +83,15 @@ struct Bar { ## Getters and Setters -Getters and setters should be named like this: +Getters should not start with a `get_` prefix. + + + + + + + + + + +
GoodBad
```rust impl Foo { @@ -91,8 +99,8 @@ impl Foo { fn set_bar(&mut self, bar: Bar) { ... } } ``` - -And **not** like this: + ```rust impl Foo { @@ -100,6 +108,9 @@ impl Foo { fn set_bar(&mut self, bar: Bar) { ... } } ``` +
See [`SocketAddr`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html) for an example of a standard library type that uses this convention. @@ -111,10 +122,99 @@ this. If a `bar` field exists and no invariants need to be maintained by the getters and setters, it is usually better to make the `bar` field public. -## Naming Quantities +## Bevy `Event`s + +Types intended to be used as events in [`EventReader`] and [`EventWriter`] should end in the `Event` suffix. +This is helpful for readers trying to distinguish events from other types in the program. + + + + + + + + + + +
GoodBad
+ +```rust +struct CollisionEvent { ... } + +fn handle_collisions(mut events: EventReader) { ... } +``` + + +```rust +struct Collision { ... } + +fn handle_collisions(mut events: EventReader) { ... } +``` +
+ +[`EventReader`]: https://docs.rs/bevy_ecs/latest/bevy_ecs/event/struct.EventReader.html +[`EventWriter`]: https://docs.rs/bevy_ecs/latest/bevy_ecs/event/struct.EventWriter.html + +## Specifying Dependencies -Quantities of something should be named `foo_count` where `foo` is the thing you're quantifying. It would be incorrect -to name this variable `num_foos`. +When adding a new dependency to a crate, make sure you specify the full semver version. + + + + + + + + + + +
GoodBad
+ +```toml +[dependencies] +serde_json = "1.0.96" +``` + + +```toml +[dependencies] +serde_json = "1" +``` +
+ +## Writing Unit Tests +When writing unit tests, unwrap errors instead of returning them. +Panicking displays the line and column of the error, which is useful for debugging. +This information is lost when the error is returned. + + + + + + + + + + +
GoodBad
+ +```rust +#[test] +fn my_test() { + some_fallible_func().unwrap(); +} +``` + + +```rust +#[test] +fn my_test() -> anyhow::Result<()> { + some_fallible_func()?; + // ... + Ok(()) +} +``` +
## Documentation @@ -127,4 +227,29 @@ where appropriate. Unit tests help your contributions last! They ensure that your code works as expected and that it continues to work in the future. -You can find examples of unit tests in the `unit_test/example.rs` module. +whole-server unit tests can be found in [`crates/valence/src/tests/`](crates/valence/src/tests). + +## Naming Quantities + +Variables intended to hold quantities should be written with the `_count` suffix instead of the `num_` prefix. + + + + + + + + + + +
GoodBad
+ +```rust +let block_count = ...; +``` + + +```rust +let num_blocks = ...; +``` +
diff --git a/Cargo.toml b/Cargo.toml index f7bc9759b..7f53eca24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = ["crates/*", "tools/*"] -exclude = ["rust-mc-bot"] +exclude = ["rust-mc-bot", "tools/stresser", "tools/packet_inspector"] resolver = "2" [workspace.package] @@ -11,7 +11,7 @@ documentation = "https://docs.rs/valence/" license = "MIT" [workspace.dependencies] -aes = "0.7.5" +aes = "0.8.2" anyhow = "1.0.70" approx = "0.5.1" arrayvec = "0.7.2" @@ -19,15 +19,14 @@ async-trait = "0.1.60" atty = "0.2.14" base64 = "0.21.0" bevy_app = { version = "0.10.1", default-features = false } -bevy_ecs = { version = "0.10.1", default-features = false, features = [ - "trace", -] } +bevy_ecs = { version = "0.10.1", default-features = false, features = ["trace"] } +bevy_hierarchy = { version = "0.10.1", default-features = false } bevy_mod_debugdump = "0.7.0" bitfield-struct = "0.3.1" byteorder = "1.4.3" bytes = "1.2.1" cesu8 = "1.1.0" -cfb8 = "0.7.1" +cfb8 = "0.8.1" clap = { version = "4.0.30", features = ["derive"] } criterion = "0.4.0" directories = "5.0.0" @@ -48,6 +47,7 @@ num-integer = "0.1.45" owo-colors = "3.5.0" parking_lot = "0.12.1" paste = "1.0.11" +pretty_assertions = "1.3.0" proc-macro2 = "1.0.56" quote = "1.0.26" rand = "0.8.5" @@ -62,7 +62,7 @@ serde = "1.0.160" serde_json = "1.0.96" sha1 = "0.10.5" sha2 = "0.10.6" -syn = "1.0.109" # TODO: update this. +syn = "2.0.15" syntect = { version = "5.0.0", default-features = false } tempfile = "3.3.0" thiserror = "1.0.40" @@ -73,6 +73,7 @@ tracing = "0.1.37" tracing-subscriber = "0.3.16" url = { version = "2.2.2", features = ["serde"] } uuid = "1.3.1" +valence_advancement.path = "crates/valence_advancement" valence_anvil.path = "crates/valence_anvil" valence_schem.path = "crates/valence_schem" valence_biome.path = "crates/valence_biome" @@ -85,7 +86,7 @@ valence_dimension.path = "crates/valence_dimension" valence_entity.path = "crates/valence_entity" valence_instance.path = "crates/valence_instance" valence_inventory.path = "crates/valence_inventory" -valence_nbt = { path = "crates/valence_nbt", features = ["uuid"] } +valence_nbt = { path = "crates/valence_nbt" } valence_network.path = "crates/valence_network" valence_player_list.path = "crates/valence_player_list" valence_registry.path = "crates/valence_registry" diff --git a/README.md b/README.md index 7d3ac4c5e..1f3e3e3b4 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,9 @@ View the latest documentation by running `cargo d --open` in your project. # Contributing Contributions are welcome! Please -see [CONTRIBUTING.md](https://github.com/valence-rs/valence/blob/main/CONTRIBUTING.md). You can also -join [the Discord](https://discord.gg/8Fqqy9XrYb) to discuss the project and ask questions. +see [CONTRIBUTING.md](https://github.com/valence-rs/valence/blob/main/CONTRIBUTING.md). You can +join [Discord](https://discord.gg/8Fqqy9XrYb) or [GitHub Discussions](https://github.com/valence-rs/valence/discussions) +to discuss the project and ask questions. # License diff --git a/crates/README.md b/crates/README.md index a5456a357..f17011ef7 100644 --- a/crates/README.md +++ b/crates/README.md @@ -21,4 +21,5 @@ graph TD inventory --> client anvil --> instance entity --> block + advancement --> client ``` diff --git a/crates/valence/Cargo.toml b/crates/valence/Cargo.toml index 86161ca4b..4135d81ba 100644 --- a/crates/valence/Cargo.toml +++ b/crates/valence/Cargo.toml @@ -11,11 +11,12 @@ keywords = ["minecraft", "gamedev", "server", "ecs"] categories = ["game-engines"] [features] -default = ["network", "player_list", "inventory", "anvil", "schem"] +default = ["network", "player_list", "inventory", "anvil", "advancement", "schem"] network = ["dep:valence_network"] player_list = ["dep:valence_player_list"] inventory = ["dep:valence_inventory"] anvil = ["dep:valence_anvil"] +advancement = ["dep:valence_advancement"] schem = ["dep:valence_schem"] [dependencies] @@ -36,6 +37,7 @@ valence_network = { workspace = true, optional = true } valence_player_list = { workspace = true, optional = true } valence_inventory = { workspace = true, optional = true } valence_anvil = { workspace = true, optional = true } +valence_advancement = { workspace = true, optional = true } valence_schem = { workspace = true, optional = true } [dev-dependencies] diff --git a/crates/valence/benches/decode_array.rs b/crates/valence/benches/decode_array.rs index 1f82ae68c..335db0c7e 100644 --- a/crates/valence/benches/decode_array.rs +++ b/crates/valence/benches/decode_array.rs @@ -1,7 +1,7 @@ use std::hint::black_box; use criterion::Criterion; -use valence::packet::{Decode, Encode}; +use valence::protocol::{Decode, Encode}; pub fn decode_array(c: &mut Criterion) { let floats = [123.0, 456.0, 789.0]; diff --git a/crates/valence/benches/packet.rs b/crates/valence/benches/packet.rs index 476f8eb83..30cdc6d7e 100644 --- a/crates/valence/benches/packet.rs +++ b/crates/valence/benches/packet.rs @@ -3,14 +3,16 @@ use std::hint::black_box; use criterion::Criterion; use valence::nbt::{compound, List}; -use valence::packet::array::LengthPrefixedArray; -use valence::packet::byte_angle::ByteAngle; -use valence::packet::decode::{decode_packet, PacketDecoder}; -use valence::packet::encode::{encode_packet, encode_packet_compressed, PacketEncoder}; -use valence::packet::s2c::play::{ChunkDataS2c, EntitySpawnS2c, PlayerListHeaderS2c}; -use valence::packet::var_int::VarInt; use valence::prelude::*; +use valence::protocol::array::LengthPrefixedArray; +use valence::protocol::byte_angle::ByteAngle; +use valence::protocol::decode::PacketDecoder; +use valence::protocol::encode::{encode_packet, encode_packet_compressed, PacketEncoder}; +use valence::protocol::var_int::VarInt; use valence::text::TextFormat; +use valence_entity::packet::EntitySpawnS2c; +use valence_instance::packet::ChunkDataS2c; +use valence_player_list::packet::PlayerListHeaderS2c; pub fn packet(c: &mut Criterion) { let mut encoder = PacketEncoder::new(); @@ -135,7 +137,12 @@ pub fn packet(c: &mut Criterion) { let decoder = black_box(&mut decoder); decoder.queue_slice(&packet_buf); - decode_packet::(&decoder.try_next_packet().unwrap().unwrap()).unwrap(); + decoder + .try_next_packet() + .unwrap() + .unwrap() + .decode::() + .unwrap(); black_box(decoder); }); @@ -149,7 +156,11 @@ pub fn packet(c: &mut Criterion) { let decoder = black_box(&mut decoder); decoder.queue_slice(&packet_buf); - decode_packet::(&decoder.try_next_packet().unwrap().unwrap()) + decoder + .try_next_packet() + .unwrap() + .unwrap() + .decode::() .unwrap(); black_box(decoder); @@ -164,7 +175,12 @@ pub fn packet(c: &mut Criterion) { let decoder = black_box(&mut decoder); decoder.queue_slice(&packet_buf); - decode_packet::(&decoder.try_next_packet().unwrap().unwrap()).unwrap(); + decoder + .try_next_packet() + .unwrap() + .unwrap() + .decode::() + .unwrap(); black_box(decoder); }); @@ -182,7 +198,12 @@ pub fn packet(c: &mut Criterion) { let decoder = black_box(&mut decoder); decoder.queue_slice(&packet_buf); - decode_packet::(&decoder.try_next_packet().unwrap().unwrap()).unwrap(); + decoder + .try_next_packet() + .unwrap() + .unwrap() + .decode::() + .unwrap(); black_box(decoder); }); @@ -202,7 +223,11 @@ pub fn packet(c: &mut Criterion) { let decoder = black_box(&mut decoder); decoder.queue_slice(&packet_buf); - decode_packet::(&decoder.try_next_packet().unwrap().unwrap()) + decoder + .try_next_packet() + .unwrap() + .unwrap() + .decode::() .unwrap(); black_box(decoder); @@ -217,7 +242,12 @@ pub fn packet(c: &mut Criterion) { let decoder = black_box(&mut decoder); decoder.queue_slice(&packet_buf); - decode_packet::(&decoder.try_next_packet().unwrap().unwrap()).unwrap(); + decoder + .try_next_packet() + .unwrap() + .unwrap() + .decode::() + .unwrap(); black_box(decoder); }); diff --git a/crates/valence/benches/var_int.rs b/crates/valence/benches/var_int.rs index f08d422c6..d7744d5b5 100644 --- a/crates/valence/benches/var_int.rs +++ b/crates/valence/benches/var_int.rs @@ -2,8 +2,8 @@ use std::hint::black_box; use criterion::Criterion; use rand::Rng; -use valence::packet::var_int::VarInt; -use valence::packet::{Decode, Encode}; +use valence::protocol::var_int::VarInt; +use valence::protocol::{Decode, Encode}; pub fn var_int(c: &mut Criterion) { let mut rng = rand::thread_rng(); diff --git a/crates/valence/benches/var_long.rs b/crates/valence/benches/var_long.rs index d8fe0fb8a..83787898e 100644 --- a/crates/valence/benches/var_long.rs +++ b/crates/valence/benches/var_long.rs @@ -2,8 +2,8 @@ use std::hint::black_box; use criterion::Criterion; use rand::Rng; -use valence::packet::var_long::VarLong; -use valence::packet::{Decode, Encode}; +use valence::protocol::var_long::VarLong; +use valence::protocol::{Decode, Encode}; pub fn var_long(c: &mut Criterion) { let mut rng = rand::thread_rng(); diff --git a/crates/valence/examples/advancement.rs b/crates/valence/examples/advancement.rs new file mode 100644 index 000000000..e5f6f7ac9 --- /dev/null +++ b/crates/valence/examples/advancement.rs @@ -0,0 +1,285 @@ +use std::collections::HashMap; + +use valence::prelude::*; +use valence_advancement::bevy_hierarchy::{BuildChildren, Children, Parent}; +use valence_advancement::ForceTabUpdate; +use valence_client::SpawnClientsSet; + +#[derive(Component)] +struct RootCriteria; + +#[derive(Component)] +struct Root2Criteria; + +#[derive(Component)] +struct RootAdvancement; + +#[derive(Component)] +struct RootCriteriaDone(bool); + +#[derive(Component)] +struct TabChangeCount(u8); + +#[derive(Resource, Default)] +struct ClientSave(HashMap); + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .init_resource::() + .add_startup_system(setup) + .add_systems(( + init_clients, + init_advancements, + sneak, + tab_change, + load_clients + .after(SpawnClientsSet) + .in_base_set(CoreSet::PreUpdate), + )) + .run(); +} + +fn setup( + mut commands: Commands, + server: Res, + dimensions: Query<&DimensionType>, + biomes: Query<&Biome>, +) { + let mut instance = Instance::new(ident!("overworld"), &dimensions, &biomes, &server); + + for z in -5..5 { + for x in -5..5 { + instance.insert_chunk([x, z], Chunk::default()); + } + } + + for z in -25..25 { + for x in -25..25 { + instance.set_block([x, 64, z], BlockState::GRASS_BLOCK); + } + } + + commands.spawn(instance); + + let root_criteria = commands + .spawn(( + AdvancementCriteria::new(ident!("custom:root_criteria").into()), + RootCriteria, + )) + .id(); + + let root_advancement = commands + .spawn(( + AdvancementBundle { + advancement: Advancement::new(ident!("custom:root").into()), + requirements: AdvancementRequirements(vec![vec![root_criteria]]), + cached_bytes: Default::default(), + }, + AdvancementDisplay { + title: "Root".into(), + description: "Toggles when you sneak".into(), + icon: Some(ItemStack::new(ItemKind::Stone, 1, None)), + frame_type: AdvancementFrameType::Task, + show_toast: true, + hidden: false, + background_texture: Some(ident!("textures/block/stone.png").into()), + x_coord: 0.0, + y_coord: 0.0, + }, + RootAdvancement, + )) + .add_child(root_criteria) + .id(); + + commands + .spawn(( + AdvancementBundle { + advancement: Advancement::new(ident!("custom:first").into()), + requirements: AdvancementRequirements::default(), + cached_bytes: Default::default(), + }, + AdvancementDisplay { + title: "First".into(), + description: "First advancement".into(), + icon: Some(ItemStack::new(ItemKind::OakWood, 1, None)), + frame_type: AdvancementFrameType::Task, + show_toast: false, + hidden: false, + background_texture: None, + x_coord: 1.0, + y_coord: -0.5, + }, + )) + .set_parent(root_advancement); + + commands + .spawn(( + AdvancementBundle { + advancement: Advancement::new(ident!("custom:second").into()), + requirements: AdvancementRequirements::default(), + cached_bytes: Default::default(), + }, + AdvancementDisplay { + title: "Second".into(), + description: "Second advancement".into(), + icon: Some(ItemStack::new(ItemKind::AcaciaWood, 1, None)), + frame_type: AdvancementFrameType::Task, + show_toast: false, + hidden: false, + background_texture: None, + x_coord: 1.0, + y_coord: 0.5, + }, + )) + .set_parent(root_advancement); + + let root2_criteria = commands + .spawn(( + AdvancementCriteria::new(ident!("custom:root2_criteria").into()), + Root2Criteria, + )) + .id(); + + commands + .spawn(( + AdvancementBundle { + advancement: Advancement::new(ident!("custom:root2").into()), + requirements: AdvancementRequirements(vec![vec![root2_criteria]]), + cached_bytes: Default::default(), + }, + AdvancementDisplay { + title: "Root2".into(), + description: "Go to this tab 5 times to earn this advancement".into(), + icon: Some(ItemStack::new(ItemKind::IronSword, 1, None)), + frame_type: AdvancementFrameType::Challenge, + show_toast: false, + hidden: false, + background_texture: Some(Ident::new("textures/block/andesite.png").unwrap()), + x_coord: 0.0, + y_coord: 0.0, + }, + )) + .add_child(root2_criteria); +} + +fn init_clients( + mut clients: Query<(&mut Location, &mut Position, &mut GameMode), Added>, + instances: Query>, +) { + for (mut loc, mut pos, mut game_mode) in &mut clients { + loc.0 = instances.single(); + pos.set([0.5, 65.0, 0.5]); + *game_mode = GameMode::Creative; + } +} + +fn load_clients( + mut commands: Commands, + clients: Query<(Entity, &UniqueId), Added>, + mut client_save: ResMut, +) { + for (client, uuid) in clients.iter() { + let (root_criteria_done, tab_change_count) = + client_save.0.entry(uuid.0).or_insert((false, 0)); + + commands.entity(client).insert(( + RootCriteriaDone(*root_criteria_done), + TabChangeCount(*tab_change_count), + )); + } +} + +fn init_advancements( + mut clients: Query< + ( + &mut AdvancementClientUpdate, + &RootCriteriaDone, + &TabChangeCount, + ), + Added, + >, + root_advancement_query: Query, With)>, + children_query: Query<&Children>, + advancement_check_query: Query<(), With>, + root2_criteria: Query>, + root_criteria: Query>, +) { + let root_c = root_criteria.single(); + let root2_c = root2_criteria.single(); + for (mut advancement_client_update, root_criteria, tab_change) in clients.iter_mut() { + for root_advancement in root_advancement_query.iter() { + advancement_client_update.send_advancements( + root_advancement, + &children_query, + &advancement_check_query, + ); + if root_criteria.0 { + advancement_client_update.criteria_done(root_c); + } + if tab_change.0 > 5 { + advancement_client_update.criteria_done(root2_c); + } + } + } +} + +fn sneak( + mut sneaking: EventReader, + mut client: Query<(&mut AdvancementClientUpdate, &mut RootCriteriaDone)>, + root_criteria: Query>, + client_uuid: Query<&UniqueId>, + mut client_save: ResMut, +) { + let root_criteria = root_criteria.single(); + for sneaking in sneaking.iter() { + if sneaking.state == SneakState::Stop { + continue; + } + let Ok((mut advancement_client_update, mut root_criteria_done)) = client.get_mut(sneaking.client) else { continue; }; + root_criteria_done.0 = !root_criteria_done.0; + match root_criteria_done.0 { + true => advancement_client_update.criteria_done(root_criteria), + false => advancement_client_update.criteria_undone(root_criteria), + } + client_save + .0 + .get_mut(&client_uuid.get(sneaking.client).unwrap().0) + .unwrap() + .0 = root_criteria_done.0; + } +} + +fn tab_change( + mut tab_change: EventReader, + mut client: Query<(&mut AdvancementClientUpdate, &mut TabChangeCount)>, + root2_criteria: Query>, + root: Query>, + client_uuid: Query<&UniqueId>, + mut client_save: ResMut, +) { + let root2_criteria = root2_criteria.single(); + let root = root.single(); + for tab_change in tab_change.iter() { + let Ok((mut advancement_client_update, mut tab_change_count)) = client.get_mut(tab_change.client) else { continue; }; + if let Some(ref opened) = tab_change.opened_tab { + if opened.as_str() == "custom:root2" { + tab_change_count.0 += 1; + } else { + continue; + } + } else { + continue; + } + if tab_change_count.0 == 5 { + advancement_client_update.criteria_done(root2_criteria); + } else if tab_change_count.0 >= 10 { + advancement_client_update.force_tab_update = ForceTabUpdate::Spec(root); + } + client_save + .0 + .get_mut(&client_uuid.get(tab_change.client).unwrap().0) + .unwrap() + .1 = tab_change_count.0; + } +} diff --git a/crates/valence/examples/block_entities.rs b/crates/valence/examples/block_entities.rs index b94a89d75..d8d300888 100644 --- a/crates/valence/examples/block_entities.rs +++ b/crates/valence/examples/block_entities.rs @@ -1,8 +1,9 @@ #![allow(clippy::type_complexity)] -use valence::client::misc::{ChatMessage, InteractBlock}; use valence::nbt::{compound, List}; use valence::prelude::*; +use valence_client::chat::ChatMessageEvent; +use valence_client::interact_block::InteractBlockEvent; const FLOOR_Y: i32 = 64; const SIGN_POS: [i32; 3] = [3, FLOOR_Y + 1, 2]; @@ -74,12 +75,12 @@ fn init_clients( fn event_handler( clients: Query<(&Username, &Properties, &UniqueId)>, - mut messages: EventReader, - mut block_interacts: EventReader, + mut messages: EventReader, + mut block_interacts: EventReader, mut instances: Query<&mut Instance>, ) { let mut instance = instances.single_mut(); - for ChatMessage { + for ChatMessageEvent { client, message, .. } in messages.iter() { @@ -93,7 +94,7 @@ fn event_handler( nbt.insert("Text3", format!("~{}", username).italic()); } - for InteractBlock { + for InteractBlockEvent { client, position, hand, diff --git a/crates/valence/examples/building.rs b/crates/valence/examples/building.rs index 02d57153c..c9af37e5e 100644 --- a/crates/valence/examples/building.rs +++ b/crates/valence/examples/building.rs @@ -1,8 +1,8 @@ #![allow(clippy::type_complexity)] -use valence::client::misc::InteractBlock; -use valence::inventory::ClientInventoryState; +use valence::inventory::HeldItem; use valence::prelude::*; +use valence_client::interact_block::InteractBlockEvent; const SPAWN_Y: i32 = 64; @@ -77,7 +77,7 @@ fn toggle_gamemode_on_sneak(mut clients: Query<&mut GameMode>, mut events: Event fn digging_creative_mode( clients: Query<&GameMode>, mut instances: Query<&mut Instance>, - mut events: EventReader, + mut events: EventReader, ) { let mut instance = instances.single_mut(); @@ -94,7 +94,7 @@ fn digging_creative_mode( fn digging_survival_mode( clients: Query<&GameMode>, mut instances: Query<&mut Instance>, - mut events: EventReader, + mut events: EventReader, ) { let mut instance = instances.single_mut(); @@ -109,14 +109,14 @@ fn digging_survival_mode( } fn place_blocks( - mut clients: Query<(&mut Inventory, &GameMode, &ClientInventoryState)>, + mut clients: Query<(&mut Inventory, &GameMode, &HeldItem)>, mut instances: Query<&mut Instance>, - mut events: EventReader, + mut events: EventReader, ) { let mut instance = instances.single_mut(); for event in events.iter() { - let Ok((mut inventory, game_mode, inv_state)) = clients.get_mut(event.client) else { + let Ok((mut inventory, game_mode, held)) = clients.get_mut(event.client) else { continue; }; if event.hand != Hand::Main { @@ -124,7 +124,7 @@ fn place_blocks( } // get the held item - let slot_id = inv_state.held_item_slot(); + let slot_id = held.slot(); let Some(stack) = inventory.slot(slot_id) else { // no item in the slot continue; diff --git a/crates/valence/examples/chest.rs b/crates/valence/examples/chest.rs index 7e1d7bf64..511aa7a74 100644 --- a/crates/valence/examples/chest.rs +++ b/crates/valence/examples/chest.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] -use valence::client::misc::InteractBlock; use valence::prelude::*; +use valence_client::interact_block::InteractBlockEvent; const SPAWN_Y: i32 = 64; const CHEST_POS: [i32; 3] = [0, SPAWN_Y + 1, 3]; @@ -78,7 +78,7 @@ fn toggle_gamemode_on_sneak(mut clients: Query<&mut GameMode>, mut events: Event fn open_chest( mut commands: Commands, inventories: Query, Without)>, - mut events: EventReader, + mut events: EventReader, ) { for event in events.iter() { if event.position != CHEST_POS.into() { diff --git a/crates/valence/examples/combat.rs b/crates/valence/examples/combat.rs index 6fd208ffa..b554b61b5 100644 --- a/crates/valence/examples/combat.rs +++ b/crates/valence/examples/combat.rs @@ -96,7 +96,7 @@ fn handle_combat_events( server: Res, mut clients: Query, mut sprinting: EventReader, - mut interact_entity: EventReader, + mut interact_entity: EventReader, ) { for &Sprinting { client, state } in sprinting.iter() { if let Ok(mut client) = clients.get_mut(client) { @@ -104,7 +104,7 @@ fn handle_combat_events( } } - for &InteractEntity { + for &InteractEntityEvent { client: attacker_client, entity: victim_client, .. diff --git a/crates/valence/examples/conway.rs b/crates/valence/examples/conway.rs index a460253bc..e7e31b45b 100644 --- a/crates/valence/examples/conway.rs +++ b/crates/valence/examples/conway.rs @@ -152,7 +152,7 @@ impl LifeBoard { } } -fn toggle_cell_on_dig(mut events: EventReader, mut board: ResMut) { +fn toggle_cell_on_dig(mut events: EventReader, mut board: ResMut) { for event in events.iter() { if event.state == DiggingState::Start { let (x, z) = (event.position.x, event.position.z); diff --git a/crates/valence/examples/death.rs b/crates/valence/examples/death.rs index 923248016..41573a51f 100644 --- a/crates/valence/examples/death.rs +++ b/crates/valence/examples/death.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] -use valence::client::misc::Respawn; use valence::prelude::*; +use valence_client::status::RequestRespawnEvent; const SPAWN_Y: i32 = 64; @@ -76,7 +76,7 @@ fn squat_and_die(mut clients: Query<&mut Client>, mut events: EventReader, - mut events: EventReader, + mut events: EventReader, instances: Query>, ) { for event in events.iter() { diff --git a/crates/valence/examples/entity_hitbox.rs b/crates/valence/examples/entity_hitbox.rs new file mode 100644 index 000000000..d86797179 --- /dev/null +++ b/crates/valence/examples/entity_hitbox.rs @@ -0,0 +1,146 @@ +use std::collections::HashMap; + +use bevy_app::App; +use bevy_ecs::prelude::Entity; +use rand::Rng; +use valence::prelude::*; +use valence_entity::entity::NameVisible; +use valence_entity::hoglin::HoglinEntityBundle; +use valence_entity::pig::PigEntityBundle; +use valence_entity::sheep::SheepEntityBundle; +use valence_entity::warden::WardenEntityBundle; +use valence_entity::zombie::ZombieEntityBundle; +use valence_entity::zombie_horse::ZombieHorseEntityBundle; +use valence_entity::{entity, Pose}; + +pub fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_startup_system(setup) + .add_system(init_clients) + .add_systems((spawn_entity, intersections)) + .run(); +} + +fn setup( + mut commands: Commands, + server: Res, + dimensions: Query<&DimensionType>, + biomes: Query<&Biome>, +) { + let mut instance = Instance::new(ident!("overworld"), &dimensions, &biomes, &server); + + for z in -5..5 { + for x in -5..5 { + instance.insert_chunk([x, z], Chunk::default()); + } + } + + for z in -25..25 { + for x in -25..25 { + instance.set_block([x, 64, z], BlockState::GRASS_BLOCK); + } + } + + commands.spawn(instance); +} + +fn init_clients( + mut clients: Query<(&mut Location, &mut Position, &mut GameMode, &mut Client), Added>, + instances: Query>, +) { + for (mut loc, mut pos, mut game_mode, mut client) in &mut clients { + loc.0 = instances.single(); + pos.set([0.5, 65.0, 0.5]); + *game_mode = GameMode::Creative; + client.send_message("To spawn an entity, press shift. F3 + B to activate hitboxes"); + } +} + +fn spawn_entity( + mut commands: Commands, + mut sneaking: EventReader, + client_query: Query<(&Position, &Location)>, +) { + for sneaking in sneaking.iter() { + if sneaking.state == SneakState::Start { + continue; + } + + let (position, location) = client_query.get(sneaking.client).unwrap(); + + let position = *position; + let location = *location; + + match rand::thread_rng().gen_range(0..7) { + 0 => commands.spawn(SheepEntityBundle { + position, + location, + entity_name_visible: NameVisible(true), + ..Default::default() + }), + 1 => commands.spawn(PigEntityBundle { + position, + location, + entity_name_visible: NameVisible(true), + ..Default::default() + }), + 2 => commands.spawn(ZombieEntityBundle { + position, + location, + entity_name_visible: NameVisible(true), + ..Default::default() + }), + 3 => commands.spawn(ZombieHorseEntityBundle { + position, + location, + entity_name_visible: NameVisible(true), + ..Default::default() + }), + 4 => commands.spawn(WardenEntityBundle { + position, + location, + entity_name_visible: NameVisible(true), + entity_pose: entity::Pose(Pose::Digging), + ..Default::default() + }), + 5 => commands.spawn(WardenEntityBundle { + position, + location, + entity_name_visible: NameVisible(true), + ..Default::default() + }), + 6 => commands.spawn(HoglinEntityBundle { + position, + location, + entity_name_visible: NameVisible(true), + + ..Default::default() + }), + _ => unreachable!(), + }; + } +} + +fn intersections(query: Query<(Entity, &Hitbox)>, mut name_query: Query<&mut entity::CustomName>) { + // This code only to show how hitboxes can be used + let mut intersections = HashMap::new(); + + for [(entity1, hitbox1), (entity2, hitbox2)] in query.iter_combinations() { + let aabb1 = hitbox1.get(); + let aabb2 = hitbox2.get(); + + let _ = *intersections.entry(entity1).or_insert(0); + let _ = *intersections.entry(entity2).or_insert(0); + + if aabb1.intersects(aabb2) { + *intersections.get_mut(&entity1).unwrap() += 1; + *intersections.get_mut(&entity2).unwrap() += 1; + } + } + + for (entity, value) in intersections { + let Ok(mut name) = name_query.get_mut(entity) else { continue; }; + name.0 = Some(format!("{value}").into()); + } +} diff --git a/crates/valence/examples/parkour.rs b/crates/valence/examples/parkour.rs index a94fb8e1b..4b7211623 100644 --- a/crates/valence/examples/parkour.rs +++ b/crates/valence/examples/parkour.rs @@ -5,9 +5,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; use rand::seq::SliceRandom; use rand::Rng; -use valence::packet::s2c::play::TitleFadeS2c; use valence::prelude::*; -use valence::sound::{Sound, SoundCategory}; +use valence::protocol::packet::sound::{Sound, SoundCategory}; const START_POS: BlockPos = BlockPos::new(0, 100, 0); const VIEW_DIST: u8 = 10; @@ -179,15 +178,8 @@ fn manage_blocks(mut clients: Query<(&mut Client, &Position, &mut GameState, &mu pitch, ); - client.set_title( - "", - state.score.to_string().color(Color::LIGHT_PURPLE).bold(), - TitleFadeS2c { - fade_in: 0, - stay: 7, - fade_out: 4, - }, - ); + client.set_title(""); + client.set_subtitle(state.score.to_string().color(Color::LIGHT_PURPLE).bold()); } } } diff --git a/crates/valence/examples/resource_pack.rs b/crates/valence/examples/resource_pack.rs index d3e069776..97058a04a 100644 --- a/crates/valence/examples/resource_pack.rs +++ b/crates/valence/examples/resource_pack.rs @@ -1,10 +1,9 @@ #![allow(clippy::type_complexity)] -use valence::client::misc::{ResourcePackStatus, ResourcePackStatusChange}; use valence::entity::player::PlayerEntityBundle; use valence::entity::sheep::SheepEntityBundle; -use valence::packet::c2s::play::player_interact_entity::EntityInteraction; use valence::prelude::*; +use valence_client::resource_pack::{ResourcePackStatus, ResourcePackStatusEvent}; const SPAWN_Y: i32 = 64; @@ -69,7 +68,7 @@ fn init_clients( } } -fn prompt_on_punch(mut clients: Query<&mut Client>, mut events: EventReader) { +fn prompt_on_punch(mut clients: Query<&mut Client>, mut events: EventReader) { for event in events.iter() { if let Ok(mut client) = clients.get_mut(event.client) { if event.interact == EntityInteraction::Attack { @@ -86,7 +85,7 @@ fn prompt_on_punch(mut clients: Query<&mut Client>, mut events: EventReader, - mut events: EventReader, + mut events: EventReader, ) { for event in events.iter() { if let Ok(mut client) = clients.get_mut(event.client) { @@ -100,7 +99,7 @@ fn on_resource_pack_status( ResourcePackStatus::FailedDownload => { client.send_message("Resource pack failed to download.".color(Color::RED)); } - ResourcePackStatus::Loaded => { + ResourcePackStatus::SuccessfullyLoaded => { client .send_message("Resource pack successfully downloaded.".color(Color::BLUE)); } diff --git a/crates/valence/examples/schem_saving.rs b/crates/valence/examples/schem_saving.rs index 54f201114..f8b8ce1f9 100644 --- a/crates/valence/examples/schem_saving.rs +++ b/crates/valence/examples/schem_saving.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; use valence::prelude::*; -use valence_client::misc::InteractBlock; -use valence_inventory::ClientInventoryState; +use valence_client::interact_block::InteractBlockEvent; +use valence_inventory::HeldItem; use valence_nbt::compound; use valence_schem::Schematic; @@ -42,25 +42,20 @@ struct Origin(BlockPos); struct Clipboard(Schematic); fn first_pos( - mut clients: Query<( - &mut Client, - &Inventory, - Option<&mut FirstPos>, - &ClientInventoryState, - )>, - mut block_breaks: EventReader, + mut clients: Query<(&mut Client, &Inventory, Option<&mut FirstPos>, &HeldItem)>, + mut block_breaks: EventReader, mut commands: Commands, ) { - for Digging { + for DiggingEvent { client: entity, position, .. } in block_breaks.iter() { - let Ok((mut client, inv, pos, inv_state)) = clients.get_mut(*entity) else { + let Ok((mut client, inv, pos, held_item)) = clients.get_mut(*entity) else { continue; }; - let slot = inv.slot(inv_state.held_item_slot()); + let slot = inv.slot(held_item.slot()); if matches!(slot, Some(ItemStack {item, ..}) if *item == ItemKind::WoodenAxe) { let changed = !matches!(pos.map(|pos| pos.0), Some(pos) if pos == *position); if changed { @@ -75,16 +70,11 @@ fn first_pos( } fn second_pos( - mut clients: Query<( - &mut Client, - &Inventory, - Option<&mut SecondPos>, - &ClientInventoryState, - )>, - mut interacts: EventReader, + mut clients: Query<(&mut Client, &Inventory, Option<&mut SecondPos>, &HeldItem)>, + mut interacts: EventReader, mut commands: Commands, ) { - for InteractBlock { + for InteractBlockEvent { client: entity, hand, position, @@ -92,10 +82,10 @@ fn second_pos( } in interacts.iter() { if *hand == Hand::Main { - let Ok((mut client, inv, pos, inv_state)) = clients.get_mut(*entity) else { + let Ok((mut client, inv, pos, held_item)) = clients.get_mut(*entity) else { continue; }; - let slot = inv.slot(inv_state.held_item_slot()); + let slot = inv.slot(held_item.slot()); if matches!(slot, Some(ItemStack {item, ..}) if *item == ItemKind::WoodenAxe) { let changed = !matches!(pos.map(|pos| pos.0), Some(pos) if pos == *position); if changed { @@ -111,16 +101,11 @@ fn second_pos( } fn origin( - mut clients: Query<( - &mut Client, - &Inventory, - Option<&mut Origin>, - &ClientInventoryState, - )>, - mut interacts: EventReader, + mut clients: Query<(&mut Client, &Inventory, Option<&mut Origin>, &HeldItem)>, + mut interacts: EventReader, mut commands: Commands, ) { - for InteractBlock { + for InteractBlockEvent { client: entity, hand, position, @@ -128,10 +113,10 @@ fn origin( } in interacts.iter() { if *hand == Hand::Main { - let Ok((mut client, inv, pos, inv_state)) = clients.get_mut(*entity) else { + let Ok((mut client, inv, pos, held_item)) = clients.get_mut(*entity) else { continue; }; - let slot = inv.slot(inv_state.held_item_slot()); + let slot = inv.slot(held_item.slot()); if matches!(slot, Some(ItemStack {item, ..}) if *item == ItemKind::Stick) { let changed = !matches!(pos.map(|pos| pos.0), Some(pos) if pos == *position); if changed { @@ -154,18 +139,18 @@ fn copy_schem( Option<&FirstPos>, Option<&SecondPos>, Option<&Origin>, - &ClientInventoryState, + &HeldItem, &Position, &Location, &Username, )>, instances: Query<&Instance>, - mut interacts: EventReader, + mut interacts: EventReader, biome_registry: Res, biomes: Query<&Biome>, mut commands: Commands, ) { - for InteractBlock { + for InteractBlockEvent { client: entity, hand, .. @@ -174,10 +159,10 @@ fn copy_schem( if *hand != Hand::Main { continue; } - let Ok((mut client, inv, pos1, pos2, origin, inv_state, &Position(pos), &Location(instance), Username(username))) = clients.get_mut(*entity) else { + let Ok((mut client, inv, pos1, pos2, origin, held_item, &Position(pos), &Location(instance), Username(username))) = clients.get_mut(*entity) else { continue; }; - let slot = inv.slot(inv_state.held_item_slot()); + let slot = inv.slot(held_item.slot()); if matches!(slot, Some(ItemStack {item, ..}) if *item == ItemKind::Paper) { let Some((FirstPos(pos1), SecondPos(pos2))) = pos1.zip(pos2) else { client.send_message("Specify both positions first"); @@ -206,12 +191,12 @@ fn paste_schem( &Inventory, Option<&Clipboard>, &Location, - &ClientInventoryState, + &HeldItem, &Position, )>, - mut interacts: EventReader, + mut interacts: EventReader, ) { - for InteractBlock { + for InteractBlockEvent { client: entity, hand, .. @@ -220,13 +205,13 @@ fn paste_schem( if *hand != Hand::Main { continue; } - let Ok((mut client, inv, clipboard, &Location(instance), inv_state, &Position(position))) = clients.get_mut(*entity) else { + let Ok((mut client, inv, clipboard, &Location(instance), held_item, &Position(position))) = clients.get_mut(*entity) else { continue; }; let Ok(mut instance) = instances.get_mut(instance) else { continue; }; - let slot = inv.slot(inv_state.held_item_slot()); + let slot = inv.slot(held_item.slot()); if matches!(slot, Some(ItemStack {item, ..}) if *item == ItemKind::Feather) { let Some(Clipboard(schematic)) = clipboard else { client.send_message("Copy something to clipboard first!"); @@ -247,12 +232,12 @@ fn save_schem( &mut Client, &Inventory, Option<&Clipboard>, - &ClientInventoryState, + &HeldItem, &Username, )>, - mut interacts: EventReader, + mut interacts: EventReader, ) { - for InteractBlock { + for InteractBlockEvent { client: entity, hand, .. @@ -261,10 +246,10 @@ fn save_schem( if *hand != Hand::Main { continue; } - let Ok((mut client, inv, clipboard, inv_state, Username(username))) = clients.get_mut(*entity) else { + let Ok((mut client, inv, clipboard, held_item, Username(username))) = clients.get_mut(*entity) else { continue; }; - let slot = inv.slot(inv_state.held_item_slot()); + let slot = inv.slot(held_item.slot()); if matches!(slot, Some(ItemStack {item, ..}) if *item == ItemKind::MusicDiscStal) { let Some(Clipboard(schematic)) = clipboard else { client.send_message("Copy something to clipboard first!"); @@ -278,21 +263,21 @@ fn save_schem( } fn place_blocks( - clients: Query<(&Inventory, &ClientInventoryState), With>, + clients: Query<(&Inventory, &HeldItem), With>, mut instances: Query<&mut Instance>, - mut events: EventReader, + mut events: EventReader, ) { let mut instance = instances.single_mut(); for event in events.iter() { - let Ok((inventory, inv_state)) = clients.get(event.client) else { + let Ok((inventory, held_item)) = clients.get(event.client) else { continue; }; if event.hand != Hand::Main { continue; } - let Some(stack) = inventory.slot(inv_state.held_item_slot()) else { + let Some(stack) = inventory.slot(held_item.slot()) else { continue; }; @@ -307,20 +292,20 @@ fn place_blocks( fn break_blocks( mut instances: Query<&mut Instance>, - inventories: Query<(&Inventory, &ClientInventoryState)>, - mut events: EventReader, + inventories: Query<(&Inventory, &HeldItem)>, + mut events: EventReader, ) { let mut instance = instances.single_mut(); - for Digging { + for DiggingEvent { client, position, .. } in events.iter() { - let Ok((inv, inv_state)) = inventories.get(*client) else { + let Ok((inv, held_item)) = inventories.get(*client) else { continue; }; - let slot = inv.slot(inv_state.held_item_slot()); + let slot = inv.slot(held_item.slot()); if !matches!(slot, Some(ItemStack {item, ..}) if *item == ItemKind::WoodenAxe) { instance.set_block(*position, BlockState::AIR); } diff --git a/crates/valence/src/lib.rs b/crates/valence/src/lib.rs index 7bab9e8c2..ff2448327 100644 --- a/crates/valence/src/lib.rs +++ b/crates/valence/src/lib.rs @@ -26,6 +26,8 @@ use bevy_app::{PluginGroup, PluginGroupBuilder}; #[cfg(test)] mod tests; +#[cfg(feature = "advancement")] +pub use valence_advancement as advancement; #[cfg(feature = "anvil")] pub use valence_anvil as anvil; pub use valence_core::*; @@ -66,10 +68,11 @@ pub mod prelude { pub use client::command::*; pub use client::event_loop::{EventLoopSchedule, EventLoopSet}; pub use client::interact_entity::*; + pub use client::title::SetTitle as _; pub use client::{ despawn_disconnected_clients, Client, CompassPos, DeathLocation, HasRespawnScreen, - HashedSeed, Ip, IsDebug, IsFlat, IsHardcore, OldView, OldViewDistance, OpLevel, - PrevGameMode, Properties, ReducedDebugInfo, Username, View, ViewDistance, + HashedSeed, Ip, IsDebug, IsFlat, IsHardcore, OldView, OldViewDistance, PrevGameMode, + Properties, ReducedDebugInfo, Username, View, ViewDistance, }; pub use despawn::Despawned; pub use dimension::{DimensionType, DimensionTypeRegistry}; @@ -95,13 +98,19 @@ pub mod prelude { ErasedNetworkCallbacks, NetworkCallbacks, NetworkSettings, NewClientInfo, SharedNetworkState, }; - pub use packet::s2c::play::particle::Particle; + pub use particle::Particle; #[cfg(feature = "player_list")] pub use player_list::{PlayerList, PlayerListEntry}; pub use text::{Color, Text, TextFormat}; + #[cfg(feature = "advancement")] + pub use valence_advancement::{ + event::AdvancementTabChange, Advancement, AdvancementBundle, AdvancementClientUpdate, + AdvancementCriteria, AdvancementDisplay, AdvancementFrameType, AdvancementRequirements, + }; pub use valence_core::ident; // Export the `ident!` macro. pub use valence_core::uuid::UniqueId; pub use valence_core::{translation_key, CoreSettings, Server}; + pub use valence_entity::hitbox::{Hitbox, HitboxShape}; pub use super::DefaultPlugins; use super::*; @@ -124,6 +133,7 @@ impl PluginGroup for DefaultPlugins { .add(valence_biome::BiomePlugin) .add(valence_dimension::DimensionPlugin) .add(valence_entity::EntityPlugin) + .add(valence_entity::hitbox::HitboxPlugin) .add(valence_instance::InstancePlugin) .add(valence_client::ClientPlugin); @@ -147,6 +157,13 @@ impl PluginGroup for DefaultPlugins { // No plugin... yet. } + #[cfg(feature = "advancement")] + { + group = group + .add(valence_advancement::AdvancementPlugin) + .add(valence_advancement::bevy_hierarchy::HierarchyPlugin); + } + group } } diff --git a/crates/valence/src/tests.rs b/crates/valence/src/tests.rs index 154bbd0e8..3a8452a49 100644 --- a/crates/valence/src/tests.rs +++ b/crates/valence/src/tests.rs @@ -8,11 +8,10 @@ use bevy_ecs::schedule::{LogLevel, ScheduleBuildSettings}; use bytes::{Buf, BufMut, BytesMut}; use uuid::Uuid; use valence_client::ClientBundleArgs; -use valence_core::packet::decode::{decode_packet, PacketDecoder}; -use valence_core::packet::encode::PacketEncoder; -use valence_core::packet::s2c::play::S2cPlayPacket; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::Packet; +use valence_core::protocol::decode::{PacketDecoder, PacketFrame}; +use valence_core::protocol::encode::PacketEncoder; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{Encode, Packet}; use valence_core::{ident, CoreSettings, Server}; use valence_entity::Location; use valence_network::{ConnectionMode, NetworkSettings}; @@ -117,7 +116,7 @@ impl MockClientConnection { .push_back(ReceivedPacket { timestamp: Instant::now(), id, - data: bytes.freeze(), + body: bytes.freeze(), }); } @@ -151,7 +150,6 @@ struct MockClientHelper { conn: MockClientConnection, dec: PacketDecoder, scratch: BytesMut, - collected_frames: Vec, } impl MockClientHelper { @@ -160,38 +158,39 @@ impl MockClientHelper { conn, dec: PacketDecoder::new(), scratch: BytesMut::new(), - collected_frames: vec![], } } /// Inject a packet to be treated as a packet inbound to the server. Panics /// if the packet cannot be sent. - fn send<'a>(&mut self, packet: &impl Packet<'a>) { + #[track_caller] + fn send

(&mut self, packet: &P) + where + P: Packet + Encode, + { packet - .encode_packet((&mut self.scratch).writer()) + .encode_with_id((&mut self.scratch).writer()) .expect("failed to encode packet"); self.conn.inject_recv(self.scratch.split()); } /// Collect all packets that have been sent to the client. - fn collect_sent(&mut self) -> Vec { + #[track_caller] + fn collect_sent(&mut self) -> PacketFrames { self.dec.queue_bytes(self.conn.take_sent()); - self.collected_frames.clear(); + let mut res = vec![]; while let Some(frame) = self .dec .try_next_packet() .expect("failed to decode packet frame") { - self.collected_frames.push(frame); + res.push(frame); } - self.collected_frames - .iter() - .map(|frame| decode_packet(frame).expect("failed to decode packet")) - .collect() + PacketFrames(res) } fn clear_sent(&mut self) { @@ -199,36 +198,87 @@ impl MockClientHelper { } } -macro_rules! assert_packet_order { - ($sent_packets:ident, $($packets:pat),+) => {{ - let sent_packets: &Vec = &$sent_packets; - let positions = [ - $((sent_packets.iter().position(|p| matches!(p, $packets))),)* - ]; - assert!(positions.windows(2).all(|w: &[Option]| w[0] < w[1])); - }}; -} +struct PacketFrames(Vec); + +impl PacketFrames { + #[track_caller] + fn assert_count(&self, expected_count: usize) { + let actual_count = self.0.iter().filter(|f| f.id == P::ID).count(); -macro_rules! assert_packet_count { - ($sent_packets:ident, $count:tt, $packet:pat) => {{ - let sent_packets: &Vec = &$sent_packets; - let count = sent_packets.iter().filter(|p| matches!(p, $packet)).count(); assert_eq!( - count, - $count, - "expected {} {} packets, got {}\nPackets actually found:\n[\n\t{}\n]\n", - $count, - stringify!($packet), - count, - sent_packets - .iter() - .map(|p| format!("{:?}", p)) - .collect::>() - .join(",\n\t") + expected_count, + actual_count, + "unexpected packet count for {} (expected {expected_count}, got {actual_count})", + P::NAME + ); + } + + #[track_caller] + fn assert_order(&self) { + let positions: Vec<_> = self + .0 + .iter() + .filter_map(|f| L::packets().iter().position(|(id, _)| f.id == *id)) + .collect(); + + // TODO: replace with slice::is_sorted. + let is_sorted = positions.windows(2).all(|w| w[0] <= w[1]); + + assert!( + is_sorted, + "packets out of order (expected {:?}, got {:?})", + L::packets(), + self.debug::() ); - }}; + } + + fn debug(&self) -> impl std::fmt::Debug { + self.0 + .iter() + .map(|f| { + L::packets() + .iter() + .find(|(id, _)| f.id == *id) + .cloned() + .unwrap_or((f.id, "")) + }) + .collect::>() + } +} + +trait PacketList { + fn packets() -> &'static [(i32, &'static str)]; } +macro_rules! impl_packet_list { + ($($ty:ident),*) => { + impl<$($ty: Packet,)*> PacketList for ($($ty,)*) { + fn packets() -> &'static [(i32, &'static str)] { + &[ + $( + ( + $ty::ID, + $ty::NAME + ), + )* + ] + } + } + } +} + +impl_packet_list!(A); +impl_packet_list!(A, B); +impl_packet_list!(A, B, C); +impl_packet_list!(A, B, C, D); +impl_packet_list!(A, B, C, D, E); +impl_packet_list!(A, B, C, D, E, F); +impl_packet_list!(A, B, C, D, E, F, G); +impl_packet_list!(A, B, C, D, E, F, G, H); +impl_packet_list!(A, B, C, D, E, F, G, H, I); +impl_packet_list!(A, B, C, D, E, F, G, H, I, J); +impl_packet_list!(A, B, C, D, E, F, G, H, I, J, K); + mod client; mod example; mod inventory; diff --git a/crates/valence/src/tests/client.rs b/crates/valence/src/tests/client.rs index f80151006..198faa631 100644 --- a/crates/valence/src/tests/client.rs +++ b/crates/valence/src/tests/client.rs @@ -4,8 +4,8 @@ use bevy_app::App; use bevy_ecs::world::EntityMut; use valence_client::ViewDistance; use valence_core::chunk_pos::ChunkView; -use valence_core::packet::s2c::play::{ChunkDataS2c, S2cPlayPacket, UnloadChunkS2c}; use valence_entity::Position; +use valence_instance::packet::{ChunkDataS2c, UnloadChunkS2c}; use valence_instance::Chunk; use super::*; @@ -38,8 +38,9 @@ fn client_chunk_view_change() { let mut loaded_chunks = BTreeSet::new(); - for pkt in client_helper.collect_sent() { - if let S2cPlayPacket::ChunkDataS2c(ChunkDataS2c { pos, .. }) = pkt { + for f in client_helper.collect_sent().0 { + if f.id == ChunkDataS2c::ID { + let ChunkDataS2c { pos, .. } = f.decode::().unwrap(); assert!(loaded_chunks.insert(pos), "({pos:?})"); } } @@ -57,12 +58,14 @@ fn client_chunk_view_change() { app.update(); let client = app.world.entity_mut(client_ent); - for pkt in client_helper.collect_sent() { - match pkt { - S2cPlayPacket::ChunkDataS2c(ChunkDataS2c { pos, .. }) => { + for f in client_helper.collect_sent().0 { + match f.id { + ChunkDataS2c::ID => { + let ChunkDataS2c { pos, .. } = f.decode().unwrap(); assert!(loaded_chunks.insert(pos), "({pos:?})"); } - S2cPlayPacket::UnloadChunkS2c(UnloadChunkS2c { pos }) => { + UnloadChunkS2c::ID => { + let UnloadChunkS2c { pos } = f.decode().unwrap(); assert!(loaded_chunks.remove(&pos), "({pos:?})"); } _ => {} diff --git a/crates/valence/src/tests/example.rs b/crates/valence/src/tests/example.rs index d5c5c3492..35302c6d1 100644 --- a/crates/valence/src/tests/example.rs +++ b/crates/valence/src/tests/example.rs @@ -6,8 +6,8 @@ //! Some of the tests in this file may be inferior duplicates of real tests. use bevy_app::App; -use valence_core::packet::c2s::play::PositionAndOnGround; -use valence_core::packet::s2c::play::S2cPlayPacket; +use valence_client::movement::PositionAndOnGroundC2s; +use valence_inventory::packet::{InventoryS2c, OpenScreenS2c}; use super::*; use crate::prelude::*; @@ -35,7 +35,7 @@ fn example_test_client_position() { let (client_ent, mut client_helper) = scenario_single_client(&mut app); // Send a packet as the client to the server. - let packet = PositionAndOnGround { + let packet = PositionAndOnGroundC2s { position: DVec3::new(12.0, 64.0, 0.0), on_ground: true, }; @@ -76,13 +76,11 @@ fn example_test_open_inventory() { app.world .get::(client_ent) .expect("client not found"); + let sent_packets = client_helper.collect_sent(); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::OpenScreenS2c(_)); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::InventoryS2c(_)); - assert_packet_order!( - sent_packets, - S2cPlayPacket::OpenScreenS2c(_), - S2cPlayPacket::InventoryS2c(_) - ); + sent_packets.assert_count::(1); + sent_packets.assert_count::(1); + + sent_packets.assert_order::<(OpenScreenS2c, InventoryS2c)>(); } diff --git a/crates/valence/src/tests/inventory.rs b/crates/valence/src/tests/inventory.rs index 8ce7d012d..f14abeb1b 100644 --- a/crates/valence/src/tests/inventory.rs +++ b/crates/valence/src/tests/inventory.rs @@ -1,12 +1,13 @@ use bevy_app::App; use valence_core::game_mode::GameMode; use valence_core::item::{ItemKind, ItemStack}; -use valence_core::packet::c2s::play::click_slot::{ClickMode, Slot}; -use valence_core::packet::c2s::play::ClickSlotC2s; -use valence_core::packet::s2c::play::S2cPlayPacket; +use valence_inventory::packet::{ + ClickMode, ClickSlotC2s, CloseScreenS2c, CreativeInventoryActionC2s, InventoryS2c, + OpenScreenS2c, ScreenHandlerSlotUpdateS2c, SlotChange, UpdateSelectedSlotC2s, +}; use valence_inventory::{ - convert_to_player_slot_id, ClientInventoryState, CursorItem, DropItemStack, Inventory, - InventoryKind, OpenInventory, + convert_to_player_slot_id, ClientInventoryState, CursorItem, DropItemStack, HeldItem, + Inventory, InventoryKind, OpenInventory, }; use super::*; @@ -35,13 +36,9 @@ fn test_should_open_inventory() { // Make assertions let sent_packets = client_helper.collect_sent(); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::OpenScreenS2c(_)); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::InventoryS2c(_)); - assert_packet_order!( - sent_packets, - S2cPlayPacket::OpenScreenS2c(_), - S2cPlayPacket::InventoryS2c(_) - ); + sent_packets.assert_count::(1); + sent_packets.assert_count::(1); + sent_packets.assert_order::<(OpenScreenS2c, InventoryS2c)>(); } #[test] @@ -77,7 +74,7 @@ fn test_should_close_inventory() { // Make assertions let sent_packets = client_helper.collect_sent(); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::CloseScreenS2c(_)); + sent_packets.assert_count::(1); } #[test] @@ -109,8 +106,9 @@ fn test_should_remove_invalid_open_inventory() { // Make assertions assert!(app.world.get::(client_ent).is_none()); + let sent_packets = client_helper.collect_sent(); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::CloseScreenS2c(_)); + sent_packets.assert_count::(1); } #[test] @@ -134,13 +132,14 @@ fn test_should_modify_player_inventory_click_slot() { .get::(client_ent) .unwrap() .state_id(); - client_helper.send(&valence_core::packet::c2s::play::ClickSlotC2s { + + client_helper.send(&ClickSlotC2s { window_id: 0, button: 0, - mode: valence_core::packet::c2s::play::click_slot::ClickMode::Click, + mode: ClickMode::Click, state_id: VarInt(state_id.0), slot_idx: 20, - slot_changes: vec![valence_core::packet::c2s::play::click_slot::Slot { + slot_changes: vec![SlotChange { idx: 20, item: None, }], @@ -155,20 +154,21 @@ fn test_should_modify_player_inventory_click_slot() { // because the inventory was changed as a result of the client's click, the // server should not send any packets to the client because the client // already knows about the change. - assert_packet_count!( - sent_packets, - 0, - S2cPlayPacket::InventoryS2c(_) | S2cPlayPacket::ScreenHandlerSlotUpdateS2c(_) - ); + sent_packets.assert_count::(0); + sent_packets.assert_count::(0); + let inventory = app .world .get::(client_ent) .expect("could not find inventory for client"); + assert_eq!(inventory.slot(20), None); + let cursor_item = app .world .get::(client_ent) .expect("could not find client"); + assert_eq!( cursor_item.0, Some(ItemStack::new(ItemKind::Diamond, 2, None)) @@ -205,11 +205,7 @@ fn test_should_modify_player_inventory_server_side() { let sent_packets = client_helper.collect_sent(); // because the inventory was modified server side, the client needs to be // updated with the change. - assert_packet_count!( - sent_packets, - 1, - S2cPlayPacket::ScreenHandlerSlotUpdateS2c(_) - ); + sent_packets.assert_count::(1); } #[test] @@ -231,7 +227,7 @@ fn test_should_sync_entire_player_inventory() { // Make assertions let sent_packets = client_helper.collect_sent(); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::InventoryS2c(_)); + sent_packets.assert_count::(1); } fn set_up_open_inventory(app: &mut App, client_ent: Entity) -> Entity { @@ -267,13 +263,13 @@ fn test_should_modify_open_inventory_click_slot() { let inv_state = app.world.get::(client_ent).unwrap(); let state_id = inv_state.state_id(); let window_id = inv_state.window_id(); - client_helper.send(&valence_core::packet::c2s::play::ClickSlotC2s { + client_helper.send(&ClickSlotC2s { window_id, - button: 0, - mode: valence_core::packet::c2s::play::click_slot::ClickMode::Click, state_id: VarInt(state_id.0), slot_idx: 20, - slot_changes: vec![valence_core::packet::c2s::play::click_slot::Slot { + button: 0, + mode: ClickMode::Click, + slot_changes: vec![SlotChange { idx: 20, item: None, }], @@ -288,11 +284,9 @@ fn test_should_modify_open_inventory_click_slot() { // because the inventory was modified as a result of the client's click, the // server should not send any packets to the client because the client // already knows about the change. - assert_packet_count!( - sent_packets, - 0, - S2cPlayPacket::InventoryS2c(_) | S2cPlayPacket::ScreenHandlerSlotUpdateS2c(_) - ); + sent_packets.assert_count::(0); + sent_packets.assert_count::(0); + let inventory = app .world .get::(inventory_ent) @@ -332,15 +326,13 @@ fn test_should_modify_open_inventory_server_side() { // because the inventory was modified server side, the client needs to be // updated with the change. - assert_packet_count!( - sent_packets, - 1, - S2cPlayPacket::ScreenHandlerSlotUpdateS2c(_) - ); + sent_packets.assert_count::(1); + let inventory = app .world .get::(inventory_ent) .expect("could not find inventory for client"); + assert_eq!( inventory.slot(5), Some(&ItemStack::new(ItemKind::IronIngot, 1, None)) @@ -367,7 +359,7 @@ fn test_should_sync_entire_open_inventory() { // Make assertions let sent_packets = client_helper.collect_sent(); - assert_packet_count!(sent_packets, 1, S2cPlayPacket::InventoryS2c(_)); + sent_packets.assert_count::(1); } #[test] @@ -384,12 +376,10 @@ fn test_set_creative_mode_slot_handling() { app.update(); client_helper.clear_sent(); - client_helper.send( - &valence_core::packet::c2s::play::CreativeInventoryActionC2s { - slot: 36, - clicked_item: Some(ItemStack::new(ItemKind::Diamond, 2, None)), - }, - ); + client_helper.send(&CreativeInventoryActionC2s { + slot: 36, + clicked_item: Some(ItemStack::new(ItemKind::Diamond, 2, None)), + }); app.update(); @@ -398,6 +388,7 @@ fn test_set_creative_mode_slot_handling() { .world .get::(client_ent) .expect("could not find inventory for client"); + assert_eq!( inventory.slot(36), Some(&ItemStack::new(ItemKind::Diamond, 2, None)) @@ -418,12 +409,10 @@ fn test_ignore_set_creative_mode_slot_if_not_creative() { app.update(); client_helper.clear_sent(); - client_helper.send( - &valence_core::packet::c2s::play::CreativeInventoryActionC2s { - slot: 36, - clicked_item: Some(ItemStack::new(ItemKind::Diamond, 2, None)), - }, - ); + client_helper.send(&CreativeInventoryActionC2s { + slot: 36, + clicked_item: Some(ItemStack::new(ItemKind::Diamond, 2, None)), + }); app.update(); @@ -480,16 +469,17 @@ fn test_should_handle_set_held_item() { app.update(); client_helper.clear_sent(); - client_helper.send(&valence_core::packet::c2s::play::UpdateSelectedSlotC2s { slot: 4 }); + client_helper.send(&UpdateSelectedSlotC2s { slot: 4 }); app.update(); // Make assertions - let inv_state = app + let held = app .world - .get::(client_ent) + .get::(client_ent) .expect("could not find client"); - assert_eq!(inv_state.held_item_slot(), 40); + + assert_eq!(held.slot(), 40); } #[test] @@ -525,10 +515,9 @@ fn should_not_increment_state_id_on_cursor_item_change() { } mod dropping_items { + use valence_client::packet::{PlayerAction, PlayerActionC2s}; use valence_core::block_pos::BlockPos; use valence_core::direction::Direction; - use valence_core::packet::c2s::play::click_slot::{ClickMode, Slot}; - use valence_core::packet::c2s::play::player_action::Action; use valence_inventory::convert_to_player_slot_id; use super::*; @@ -548,8 +537,8 @@ mod dropping_items { .expect("could not find inventory"); inventory.set_slot(36, ItemStack::new(ItemKind::IronIngot, 3, None)); - client_helper.send(&valence_core::packet::c2s::play::PlayerActionC2s { - action: Action::DropItem, + client_helper.send(&PlayerActionC2s { + action: PlayerAction::DropItem, position: BlockPos::new(0, 0, 0), direction: Direction::Down, sequence: VarInt(0), @@ -562,15 +551,19 @@ mod dropping_items { .world .get::(client_ent) .expect("could not find client"); + assert_eq!( inventory.slot(36), Some(&ItemStack::new(ItemKind::IronIngot, 2, None)) ); + let events = app .world .get_resource::>() .expect("expected drop item stack events"); + let events = events.iter_current_update_events().collect::>(); + assert_eq!(events.len(), 1); assert_eq!(events[0].client, client_ent); assert_eq!(events[0].from_slot, Some(36)); @@ -580,11 +573,8 @@ mod dropping_items { ); let sent_packets = client_helper.collect_sent(); - assert_packet_count!( - sent_packets, - 0, - S2cPlayPacket::ScreenHandlerSlotUpdateS2c(_) - ); + + sent_packets.assert_count::(0); } #[test] @@ -602,8 +592,8 @@ mod dropping_items { .expect("could not find inventory"); inventory.set_slot(36, ItemStack::new(ItemKind::IronIngot, 32, None)); - client_helper.send(&valence_core::packet::c2s::play::PlayerActionC2s { - action: Action::DropAllItems, + client_helper.send(&PlayerActionC2s { + action: PlayerAction::DropAllItems, position: BlockPos::new(0, 0, 0), direction: Direction::Down, sequence: VarInt(0), @@ -612,11 +602,11 @@ mod dropping_items { app.update(); // Make assertions - let inv_state = app + let held = app .world - .get::(client_ent) + .get::(client_ent) .expect("could not find client"); - assert_eq!(inv_state.held_item_slot(), 36); + assert_eq!(held.slot(), 36); let inventory = app .world .get::(client_ent) @@ -647,12 +637,10 @@ mod dropping_items { app.world.entity_mut(client_ent).insert(GameMode::Creative); - client_helper.send( - &valence_core::packet::c2s::play::CreativeInventoryActionC2s { - slot: -1, - clicked_item: Some(ItemStack::new(ItemKind::IronIngot, 32, None)), - }, - ); + client_helper.send(&CreativeInventoryActionC2s { + slot: -1, + clicked_item: Some(ItemStack::new(ItemKind::IronIngot, 32, None)), + }); app.update(); @@ -693,12 +681,12 @@ mod dropping_items { .expect("could not find client"); let state_id = inv_state.state_id().0; - client_helper.send(&valence_core::packet::c2s::play::ClickSlotC2s { + client_helper.send(&ClickSlotC2s { window_id: 0, + state_id: VarInt(state_id), slot_idx: -999, button: 0, mode: ClickMode::Click, - state_id: VarInt(state_id), slot_changes: vec![], carried_item: None, }); @@ -710,12 +698,16 @@ mod dropping_items { .world .get::(client_ent) .expect("could not find client"); + assert_eq!(cursor_item.0, None); + let events = app .world .get_resource::>() .expect("expected drop item stack events"); + let events = events.iter_current_update_events().collect::>(); + assert_eq!(events.len(), 1); assert_eq!(events[0].client, client_ent); assert_eq!(events[0].from_slot, None); @@ -738,20 +730,23 @@ mod dropping_items { .world .get_mut::(client_ent) .expect("could not find client"); + let state_id = inv_state.state_id().0; + let mut inventory = app .world .get_mut::(client_ent) .expect("could not find inventory"); + inventory.set_slot(40, ItemStack::new(ItemKind::IronIngot, 32, None)); - client_helper.send(&valence_core::packet::c2s::play::ClickSlotC2s { + client_helper.send(&ClickSlotC2s { window_id: 0, slot_idx: 40, button: 0, mode: ClickMode::DropKey, state_id: VarInt(state_id), - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 40, item: Some(ItemStack::new(ItemKind::IronIngot, 31, None)), }], @@ -765,7 +760,9 @@ mod dropping_items { .world .get_resource::>() .expect("expected drop item stack events"); + let events = events.iter_current_update_events().collect::>(); + assert_eq!(events.len(), 1); assert_eq!(events[0].client, client_ent); assert_eq!(events[0].from_slot, Some(40)); @@ -788,20 +785,23 @@ mod dropping_items { .world .get_mut::(client_ent) .expect("could not find client"); + let state_id = inv_state.state_id().0; + let mut inventory = app .world .get_mut::(client_ent) .expect("could not find inventory"); + inventory.set_slot(40, ItemStack::new(ItemKind::IronIngot, 32, None)); - client_helper.send(&valence_core::packet::c2s::play::ClickSlotC2s { + client_helper.send(&ClickSlotC2s { window_id: 0, slot_idx: 40, button: 1, // pressing control mode: ClickMode::DropKey, state_id: VarInt(state_id), - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 40, item: None, }], @@ -815,7 +815,9 @@ mod dropping_items { .world .get_resource::>() .expect("expected drop item stack events"); + let events = events.iter_current_update_events().collect::>(); + assert_eq!(events.len(), 1); assert_eq!(events[0].client, client_ent); assert_eq!(events[0].from_slot, Some(40)); @@ -840,13 +842,16 @@ mod dropping_items { .world .get_mut::(client_ent) .expect("could not find inventory"); + inventory.set_slot( convert_to_player_slot_id(InventoryKind::Generic9x3, 50), ItemStack::new(ItemKind::IronIngot, 32, None), ); + let _inventory_ent = set_up_open_inventory(&mut app, client_ent); app.update(); + client_helper.clear_sent(); let inv_state = app @@ -857,13 +862,13 @@ mod dropping_items { let state_id = inv_state.state_id().0; let window_id = inv_state.window_id(); - client_helper.send(&valence_core::packet::c2s::play::ClickSlotC2s { + client_helper.send(&ClickSlotC2s { window_id, - slot_idx: 50, - button: 0, // not pressing control - mode: ClickMode::DropKey, state_id: VarInt(state_id), - slot_changes: vec![Slot { + slot_idx: 50, // not pressing control + button: 0, + mode: ClickMode::DropKey, + slot_changes: vec![SlotChange { idx: 50, item: Some(ItemStack::new(ItemKind::IronIngot, 31, None)), }], @@ -877,21 +882,26 @@ mod dropping_items { .world .get_resource::>() .expect("expected drop item stack events"); + let player_inventory = app .world .get::(client_ent) .expect("could not find inventory"); + let events = events.iter_current_update_events().collect::>(); + assert_eq!(events.len(), 1); assert_eq!(events[0].client, client_ent); assert_eq!( events[0].from_slot, Some(convert_to_player_slot_id(InventoryKind::Generic9x3, 50)) ); + assert_eq!( events[0].stack, ItemStack::new(ItemKind::IronIngot, 1, None) ); + // Also make sure that the player inventory was updated correctly. let expected_player_slot_id = convert_to_player_slot_id(InventoryKind::Generic9x3, 50); assert_eq!( @@ -916,10 +926,12 @@ fn should_drop_item_stack_player_open_inventory_with_dropkey() { .world .get_mut::(client_ent) .expect("could not find inventory"); + inventory.set_slot( convert_to_player_slot_id(InventoryKind::Generic9x3, 50), ItemStack::new(ItemKind::IronIngot, 32, None), ); + let _inventory_ent = set_up_open_inventory(&mut app, client_ent); app.update(); @@ -933,13 +945,13 @@ fn should_drop_item_stack_player_open_inventory_with_dropkey() { let state_id = inv_state.state_id().0; let window_id = inv_state.window_id(); - client_helper.send(&valence_core::packet::c2s::play::ClickSlotC2s { + client_helper.send(&ClickSlotC2s { window_id, - slot_idx: 50, - button: 1, // pressing control, the whole stack is dropped - mode: ClickMode::DropKey, state_id: VarInt(state_id), - slot_changes: vec![Slot { + slot_idx: 50, // pressing control, the whole stack is dropped + button: 1, + mode: ClickMode::DropKey, + slot_changes: vec![SlotChange { idx: 50, item: None, }], @@ -953,11 +965,14 @@ fn should_drop_item_stack_player_open_inventory_with_dropkey() { .world .get_resource::>() .expect("expected drop item stack events"); + let player_inventory = app .world .get::(client_ent) .expect("could not find inventory"); + let events = events.iter_current_update_events().collect::>(); + assert_eq!(events.len(), 1); assert_eq!(events[0].client, client_ent); assert_eq!( @@ -968,6 +983,7 @@ fn should_drop_item_stack_player_open_inventory_with_dropkey() { events[0].stack, ItemStack::new(ItemKind::IronIngot, 32, None) ); + // Also make sure that the player inventory was updated correctly. let expected_player_slot_id = convert_to_player_slot_id(InventoryKind::Generic9x3, 50); assert_eq!(player_inventory.slot(expected_player_slot_id), None); @@ -996,15 +1012,15 @@ fn dragging_items() { button: 2, mode: ClickMode::Drag, slot_changes: vec![ - Slot { + SlotChange { idx: 9, item: Some(ItemStack::new(ItemKind::Diamond, 21, None)), }, - Slot { + SlotChange { idx: 10, item: Some(ItemStack::new(ItemKind::Diamond, 21, None)), }, - Slot { + SlotChange { idx: 11, item: Some(ItemStack::new(ItemKind::Diamond, 21, None)), }, @@ -1015,20 +1031,23 @@ fn dragging_items() { app.update(); let sent_packets = client_helper.collect_sent(); - assert_eq!(sent_packets.len(), 0); + assert_eq!(sent_packets.0.len(), 0); let cursor_item = app .world .get::(client_ent) .expect("could not find client"); + assert_eq!( cursor_item.0, Some(ItemStack::new(ItemKind::Diamond, 1, None)) ); + let inventory = app .world .get::(client_ent) .expect("could not find inventory"); + for i in 9..12 { assert_eq!( inventory.slot(i), diff --git a/crates/valence/src/tests/weather.rs b/crates/valence/src/tests/weather.rs index dc6822f6c..ee4166bd2 100644 --- a/crates/valence/src/tests/weather.rs +++ b/crates/valence/src/tests/weather.rs @@ -1,51 +1,10 @@ use bevy_app::App; +use valence_client::packet::GameStateChangeS2c; use valence_client::weather::{Rain, Thunder}; use valence_client::Client; -use valence_core::packet::s2c::play::game_state_change::GameEventKind; -use valence_core::packet::s2c::play::{GameStateChangeS2c, S2cPlayPacket}; use super::*; -fn assert_weather_packets(sent_packets: Vec) { - assert_packet_count!(sent_packets, 6, S2cPlayPacket::GameStateChangeS2c(_)); - - assert_packet_order!( - sent_packets, - S2cPlayPacket::GameStateChangeS2c(GameStateChangeS2c { - kind: GameEventKind::BeginRaining, - value: _ - }), - S2cPlayPacket::GameStateChangeS2c(GameStateChangeS2c { - kind: GameEventKind::RainLevelChange, - value: _ - }), - S2cPlayPacket::GameStateChangeS2c(GameStateChangeS2c { - kind: GameEventKind::ThunderLevelChange, - value: _ - }), - S2cPlayPacket::GameStateChangeS2c(GameStateChangeS2c { - kind: GameEventKind::EndRaining, - value: _ - }) - ); - - if let S2cPlayPacket::GameStateChangeS2c(pkt) = sent_packets[1] { - assert_eq!(pkt.value, 0.5); - } - - if let S2cPlayPacket::GameStateChangeS2c(pkt) = sent_packets[2] { - assert_eq!(pkt.value, 1.0); - } - - if let S2cPlayPacket::GameStateChangeS2c(pkt) = sent_packets[3] { - assert_eq!(pkt.value, 0.5); - } - - if let S2cPlayPacket::GameStateChangeS2c(pkt) = sent_packets[4] { - assert_eq!(pkt.value, 1.0); - } -} - #[test] fn test_weather_instance() { let mut app = App::new(); @@ -139,3 +98,8 @@ fn test_weather_client() { assert_weather_packets(sent_packets); } + +#[track_caller] +fn assert_weather_packets(sent_packets: PacketFrames) { + sent_packets.assert_count::(6); +} diff --git a/crates/valence_advancement/Cargo.toml b/crates/valence_advancement/Cargo.toml new file mode 100644 index 000000000..76d0aebd0 --- /dev/null +++ b/crates/valence_advancement/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "valence_advancement" +version.workspace = true +edition.workspace = true + +[dependencies] +valence_core.workspace = true +valence_client.workspace = true +bevy_app.workspace = true +bevy_ecs.workspace = true +bevy_hierarchy.workspace = true +rustc-hash.workspace = true +anyhow.workspace = true \ No newline at end of file diff --git a/crates/valence_advancement/README.md b/crates/valence_advancement/README.md new file mode 100644 index 000000000..4aa5d3ab2 --- /dev/null +++ b/crates/valence_advancement/README.md @@ -0,0 +1,7 @@ +# valence_advancement + +Everything related to Minecraft advancements. + +### Warning +- Each advancement should be scheduled to be sent to each unique client. +- Advancement identifier is not mutable and changing it can cause bugs. \ No newline at end of file diff --git a/crates/valence_advancement/src/event.rs b/crates/valence_advancement/src/event.rs new file mode 100644 index 000000000..565e7d457 --- /dev/null +++ b/crates/valence_advancement/src/event.rs @@ -0,0 +1,30 @@ +use bevy_ecs::prelude::{Entity, EventReader, EventWriter}; +use valence_client::event_loop::PacketEvent; +use valence_core::ident::Ident; + +use crate::packet::AdvancementTabC2s; + +/// This event sends when the client changes or closes advancement's tab. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct AdvancementTabChange { + pub client: Entity, + /// If None then the client has closed advancement's tabs. + pub opened_tab: Option>, +} + +pub(crate) fn handle_advancement_tab_change( + mut packets: EventReader, + mut advancement_tab_change_events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + advancement_tab_change_events.send(AdvancementTabChange { + client: packet.client, + opened_tab: match pkt { + AdvancementTabC2s::ClosedScreen => None, + AdvancementTabC2s::OpenedTab { tab_id } => Some(tab_id.into()), + }, + }) + } + } +} diff --git a/crates/valence_advancement/src/lib.rs b/crates/valence_advancement/src/lib.rs new file mode 100644 index 000000000..391c675bc --- /dev/null +++ b/crates/valence_advancement/src/lib.rs @@ -0,0 +1,481 @@ +#![doc = include_str!("../README.md")] +#![allow(clippy::type_complexity)] + +pub mod event; +pub mod packet; + +use std::borrow::Cow; +use std::io::Write; +use std::time::{SystemTime, UNIX_EPOCH}; + +use bevy_app::{CoreSet, Plugin}; +use bevy_ecs::prelude::{Bundle, Component, Entity}; +use bevy_ecs::query::{Added, Changed, Or, With}; +use bevy_ecs::schedule::{IntoSystemConfig, IntoSystemSetConfig, SystemSet}; +use bevy_ecs::system::{Commands, Query, SystemParam}; +pub use bevy_hierarchy; +use bevy_hierarchy::{Children, Parent}; +use event::{handle_advancement_tab_change, AdvancementTabChange}; +use packet::SelectAdvancementTabS2c; +use rustc_hash::FxHashMap; +use valence_client::{Client, FlushPacketsSet, SpawnClientsSet}; +use valence_core::ident::Ident; +use valence_core::item::ItemStack; +use valence_core::protocol::encode::WritePacket; +use valence_core::protocol::raw::RawBytes; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Encode, Packet}; +use valence_core::text::Text; + +pub struct AdvancementPlugin; + +#[derive(SystemSet, Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub struct WriteAdvancementPacketToClientsSet; + +#[derive(SystemSet, Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub struct WriteAdvancementToCacheSet; + +impl Plugin for AdvancementPlugin { + fn build(&self, app: &mut bevy_app::App) { + app.configure_sets(( + WriteAdvancementPacketToClientsSet + .in_base_set(CoreSet::PostUpdate) + .before(FlushPacketsSet), + WriteAdvancementToCacheSet + .in_base_set(CoreSet::PostUpdate) + .before(WriteAdvancementPacketToClientsSet), + )) + .add_event::() + .add_system( + add_advancement_update_component_to_new_clients + .after(SpawnClientsSet) + .in_base_set(CoreSet::PreUpdate), + ) + .add_system(handle_advancement_tab_change.in_base_set(CoreSet::PreUpdate)) + .add_system(update_advancement_cached_bytes.in_set(WriteAdvancementToCacheSet)) + .add_system(send_advancement_update_packet.in_set(WriteAdvancementPacketToClientsSet)); + } +} + +/// Components for advancement that are required +/// Optional components: +/// [AdvancementDisplay] +/// [Parent] - parent advancement +#[derive(Bundle)] +pub struct AdvancementBundle { + pub advancement: Advancement, + pub requirements: AdvancementRequirements, + pub cached_bytes: AdvancementCachedBytes, +} + +fn add_advancement_update_component_to_new_clients( + mut commands: Commands, + query: Query>, +) { + for client in query.iter() { + commands + .entity(client) + .insert(AdvancementClientUpdate::default()); + } +} + +#[derive(SystemParam, Debug)] +struct UpdateAdvancementCachedBytesQuery<'w, 's> { + advancement_id_query: Query<'w, 's, &'static Advancement>, + criteria_query: Query<'w, 's, &'static AdvancementCriteria>, +} + +impl<'w, 's> UpdateAdvancementCachedBytesQuery<'w, 's> { + fn write( + &self, + a_identifier: &Advancement, + a_requirements: &AdvancementRequirements, + a_display: Option<&AdvancementDisplay>, + a_children: Option<&Children>, + a_parent: Option<&Parent>, + w: impl Write, + ) -> anyhow::Result<()> { + let Self { + advancement_id_query, + criteria_query, + } = self; + + let mut pkt = packet::Advancement { + parent_id: None, + display_data: None, + criteria: vec![], + requirements: vec![], + }; + + if let Some(a_parent) = a_parent { + let a_identifier = advancement_id_query.get(a_parent.get())?; + pkt.parent_id = Some(a_identifier.0.borrowed()); + } + + if let Some(a_display) = a_display { + pkt.display_data = Some(packet::AdvancementDisplay { + title: Cow::Borrowed(&a_display.title), + description: Cow::Borrowed(&a_display.description), + icon: &a_display.icon, + frame_type: VarInt(a_display.frame_type as _), + flags: a_display.flags(), + background_texture: a_display.background_texture.as_ref().map(|v| v.borrowed()), + x_coord: a_display.x_coord, + y_coord: a_display.y_coord, + }); + } + + if let Some(a_children) = a_children { + for a_child in a_children.iter() { + let Ok(c_identifier) = criteria_query.get(*a_child) else { continue; }; + pkt.criteria.push((c_identifier.0.borrowed(), ())); + } + } + + for requirements in a_requirements.0.iter() { + let mut requirements_p = vec![]; + for requirement in requirements { + let c_identifier = criteria_query.get(*requirement)?; + requirements_p.push(c_identifier.0.as_str()); + } + pkt.requirements.push(packet::AdvancementRequirements { + requirement: requirements_p, + }); + } + + (&a_identifier.0, pkt).encode(w) + } +} + +fn update_advancement_cached_bytes( + mut query: Query< + ( + &Advancement, + &AdvancementRequirements, + &mut AdvancementCachedBytes, + Option<&AdvancementDisplay>, + Option<&Children>, + Option<&Parent>, + ), + Or<( + Changed, + Changed, + Changed, + Changed, + )>, + >, + update_advancement_cached_bytes_query: UpdateAdvancementCachedBytesQuery, +) { + for (a_identifier, a_requirements, mut a_bytes, a_display, a_children, a_parent) in + query.iter_mut() + { + a_bytes.0.clear(); + update_advancement_cached_bytes_query + .write( + a_identifier, + a_requirements, + a_display, + a_children, + a_parent, + &mut a_bytes.0, + ) + .expect("Failed to write an advancement"); + } +} + +#[derive(SystemParam, Debug)] +#[allow(clippy::type_complexity)] +pub(crate) struct SingleAdvancementUpdateQuery<'w, 's> { + advancement_bytes_query: Query<'w, 's, &'static AdvancementCachedBytes>, + advancement_id_query: Query<'w, 's, &'static Advancement>, + criteria_query: Query<'w, 's, &'static AdvancementCriteria>, + parent_query: Query<'w, 's, &'static Parent>, +} + +#[derive(Debug)] +pub(crate) struct AdvancementUpdateEncodeS2c<'w, 's, 'a> { + client_update: AdvancementClientUpdate, + queries: &'a SingleAdvancementUpdateQuery<'w, 's>, +} + +impl<'w, 's, 'a> Encode for AdvancementUpdateEncodeS2c<'w, 's, 'a> { + fn encode(&self, w: impl Write) -> anyhow::Result<()> { + let SingleAdvancementUpdateQuery { + advancement_bytes_query, + advancement_id_query, + criteria_query, + parent_query, + } = self.queries; + + let AdvancementClientUpdate { + new_advancements, + remove_advancements, + progress, + force_tab_update: _, + reset, + } = &self.client_update; + + let mut pkt = packet::GenericAdvancementUpdateS2c { + reset: *reset, + advancement_mapping: vec![], + identifiers: vec![], + progress_mapping: vec![], + }; + + for new_advancement in new_advancements { + let a_cached_bytes = advancement_bytes_query.get(*new_advancement)?; + pkt.advancement_mapping + .push(RawBytes(a_cached_bytes.0.as_slice())); + } + + for remove_advancement in remove_advancements { + let a_identifier = advancement_id_query.get(*remove_advancement)?; + pkt.identifiers.push(a_identifier.0.borrowed()); + } + + let mut progress_mapping: FxHashMap)>> = + FxHashMap::default(); + for progress in progress { + let a = parent_query.get(progress.0)?; + progress_mapping + .entry(a.get()) + .and_modify(|v| v.push(*progress)) + .or_insert(vec![*progress]); + } + + for (a, c_progresses) in progress_mapping { + let a_identifier = advancement_id_query.get(a)?; + let mut c_progresses_p = vec![]; + for (c, c_progress) in c_progresses { + let c_identifier = criteria_query.get(c)?; + c_progresses_p.push(packet::AdvancementCriteria { + criterion_identifier: c_identifier.0.borrowed(), + criterion_progress: c_progress, + }); + } + pkt.progress_mapping + .push((a_identifier.0.borrowed(), c_progresses_p)); + } + + pkt.encode(w) + } +} + +impl<'w, 's, 'a> Packet for AdvancementUpdateEncodeS2c<'w, 's, 'a> { + const ID: i32 = packet_id::ADVANCEMENT_UPDATE_S2C; + const NAME: &'static str = "AdvancementUpdateEncodeS2c"; +} + +#[allow(clippy::type_complexity)] +fn send_advancement_update_packet( + mut client: Query<(&mut AdvancementClientUpdate, &mut Client)>, + update_single_query: SingleAdvancementUpdateQuery, +) { + for (mut advancement_client_update, mut client) in client.iter_mut() { + match advancement_client_update.force_tab_update { + ForceTabUpdate::None => {} + ForceTabUpdate::First => { + client.write_packet(&SelectAdvancementTabS2c { identifier: None }) + } + ForceTabUpdate::Spec(spec) => { + if let Ok(a_identifier) = update_single_query.advancement_id_query.get(spec) { + client.write_packet(&SelectAdvancementTabS2c { + identifier: Some(a_identifier.0.borrowed()), + }); + } + } + } + + if ForceTabUpdate::None != advancement_client_update.force_tab_update { + advancement_client_update.force_tab_update = ForceTabUpdate::None; + } + + if advancement_client_update.new_advancements.is_empty() + && advancement_client_update.progress.is_empty() + && advancement_client_update.remove_advancements.is_empty() + && !advancement_client_update.reset + { + continue; + } + + let advancement_client_update = std::mem::replace( + advancement_client_update.as_mut(), + AdvancementClientUpdate { + reset: false, + ..Default::default() + }, + ); + + client.write_packet(&AdvancementUpdateEncodeS2c { + queries: &update_single_query, + client_update: advancement_client_update, + }); + } +} + +/// Advancement's id. May not be updated. +#[derive(Component)] +pub struct Advancement(Ident>); + +impl Advancement { + pub fn new(ident: Ident>) -> Advancement { + Self(ident) + } + + pub fn get(&self) -> &Ident> { + &self.0 + } +} + +#[derive(Clone, Copy)] +pub enum AdvancementFrameType { + Task, + Challenge, + Goal, +} + +/// Advancement display. Optional component +#[derive(Component)] +pub struct AdvancementDisplay { + pub title: Text, + pub description: Text, + pub icon: Option, + pub frame_type: AdvancementFrameType, + pub show_toast: bool, + pub hidden: bool, + pub background_texture: Option>>, + pub x_coord: f32, + pub y_coord: f32, +} + +impl AdvancementDisplay { + pub(crate) fn flags(&self) -> i32 { + let mut flags = 0; + flags |= self.background_texture.is_some() as i32; + flags |= (self.show_toast as i32) << 1; + flags |= (self.hidden as i32) << 2; + flags + } +} + +/// Criteria's identifier. May not be updated +#[derive(Component)] +pub struct AdvancementCriteria(Ident>); + +impl AdvancementCriteria { + pub fn new(ident: Ident>) -> Self { + Self(ident) + } + + pub fn get(&self) -> &Ident> { + &self.0 + } +} + +/// Requirements for advancement to be completed. +/// All columns should be completed, column is completed when any of criteria in +/// this column is completed. +#[derive(Component, Default)] +pub struct AdvancementRequirements(pub Vec>); + +#[derive(Component, Default)] +pub struct AdvancementCachedBytes(pub(crate) Vec); + +#[derive(Default, Debug, PartialEq)] +pub enum ForceTabUpdate { + #[default] + None, + First, + /// Should contain only root advancement otherwise the first will be chosen + Spec(Entity), +} + +#[derive(Component, Debug)] +pub struct AdvancementClientUpdate { + /// Which advancement's descriptions send to client + pub new_advancements: Vec, + /// Which advancements remove from client + pub remove_advancements: Vec, + /// Criteria progress update. + /// If None then criteria is not done otherwise it is done + pub progress: Vec<(Entity, Option)>, + /// Forces client to open a tab + pub force_tab_update: ForceTabUpdate, + /// Defines if other advancements should be removed. + /// Also with this flag, client will not show a toast for advancements, + /// which are completed. When the packet is sent, turns to false + pub reset: bool, +} + +impl Default for AdvancementClientUpdate { + fn default() -> Self { + Self { + new_advancements: vec![], + remove_advancements: vec![], + progress: vec![], + force_tab_update: ForceTabUpdate::default(), + reset: true, + } + } +} + +impl AdvancementClientUpdate { + pub(crate) fn walk_advancements( + root: Entity, + children_query: &Query<&Children>, + advancement_check_query: &Query<(), With>, + func: &mut impl FnMut(Entity), + ) { + func(root); + if let Ok(children) = children_query.get(root) { + for child in children.iter() { + let child = *child; + if advancement_check_query.get(child).is_ok() { + Self::walk_advancements(child, children_query, advancement_check_query, func); + } + } + } + } + + /// Sends all advancements from the root + pub fn send_advancements( + &mut self, + root: Entity, + children_query: &Query<&Children>, + advancement_check_query: &Query<(), With>, + ) { + Self::walk_advancements(root, children_query, advancement_check_query, &mut |e| { + self.new_advancements.push(e) + }); + } + + /// Removes all advancements from the root + pub fn remove_advancements( + &mut self, + root: Entity, + children_query: &Query<&Children>, + advancement_check_query: &Query<(), With>, + ) { + Self::walk_advancements(root, children_query, advancement_check_query, &mut |e| { + self.remove_advancements.push(e) + }); + } + + /// Marks criteria as done + pub fn criteria_done(&mut self, criteria: Entity) { + self.progress.push(( + criteria, + Some( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64, + ), + )) + } + + /// Marks criteria as undone + pub fn criteria_undone(&mut self, criteria: Entity) { + self.progress.push((criteria, None)) + } +} diff --git a/crates/valence_core/src/packet/s2c/play/advancement_update.rs b/crates/valence_advancement/src/packet.rs similarity index 65% rename from crates/valence_core/src/packet/s2c/play/advancement_update.rs rename to crates/valence_advancement/src/packet.rs index 91bcd4eee..cb58fd555 100644 --- a/crates/valence_core/src/packet/s2c/play/advancement_update.rs +++ b/crates/valence_advancement/src/packet.rs @@ -1,24 +1,28 @@ use std::borrow::Cow; use std::io::Write; -use crate::ident::Ident; -use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct AdvancementUpdateS2c<'a> { +use valence_core::ident::Ident; +use valence_core::item::ItemStack; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_core::text::Text; + +pub type AdvancementUpdateS2c<'a> = + GenericAdvancementUpdateS2c<'a, (Ident>, Advancement<'a, Option>)>; + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ADVANCEMENT_UPDATE_S2C)] +pub struct GenericAdvancementUpdateS2c<'a, AM: 'a> { pub reset: bool, - pub advancement_mapping: Vec<(Ident>, Advancement<'a>)>, + pub advancement_mapping: Vec, pub identifiers: Vec>>, pub progress_mapping: Vec<(Ident>, Vec>)>, } #[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct Advancement<'a> { +pub struct Advancement<'a, I> { pub parent_id: Option>>, - pub display_data: Option>, + pub display_data: Option>, pub criteria: Vec<(Ident>, ())>, pub requirements: Vec>, } @@ -29,10 +33,10 @@ pub struct AdvancementRequirements<'a> { } #[derive(Clone, PartialEq, Debug)] -pub struct AdvancementDisplay<'a> { +pub struct AdvancementDisplay<'a, I> { pub title: Cow<'a, Text>, pub description: Cow<'a, Text>, - pub icon: Option, + pub icon: I, pub frame_type: VarInt, pub flags: i32, pub background_texture: Option>>, @@ -48,7 +52,7 @@ pub struct AdvancementCriteria<'a> { pub criterion_progress: Option, } -impl Encode for AdvancementDisplay<'_> { +impl Encode for AdvancementDisplay<'_, I> { fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { self.title.encode(&mut w)?; self.description.encode(&mut w)?; @@ -68,11 +72,11 @@ impl Encode for AdvancementDisplay<'_> { } } -impl<'a> Decode<'a> for AdvancementDisplay<'a> { +impl<'a, I: Decode<'a>> Decode<'a> for AdvancementDisplay<'a, I> { fn decode(r: &mut &'a [u8]) -> anyhow::Result { let title = >::decode(r)?; let description = >::decode(r)?; - let icon = Option::::decode(r)?; + let icon = I::decode(r)?; let frame_type = VarInt::decode(r)?; let flags = i32::decode(r)?; @@ -97,3 +101,16 @@ impl<'a> Decode<'a> for AdvancementDisplay<'a> { }) } } + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ADVANCEMENT_TAB_C2S)] +pub enum AdvancementTabC2s<'a> { + OpenedTab { tab_id: Ident> }, + ClosedScreen, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SELECT_ADVANCEMENT_TAB_S2C)] +pub struct SelectAdvancementTabS2c<'a> { + pub identifier: Option>>, +} diff --git a/crates/valence_anvil/src/lib.rs b/crates/valence_anvil/src/lib.rs index 2b635eea9..5b5d31e24 100644 --- a/crates/valence_anvil/src/lib.rs +++ b/crates/valence_anvil/src/lib.rs @@ -56,7 +56,7 @@ pub enum ReadChunkError { #[error(transparent)] Io(#[from] io::Error), #[error(transparent)] - Nbt(#[from] valence_nbt::Error), + Nbt(#[from] valence_nbt::binary::Error), #[error("invalid chunk sector offset")] BadSectorOffset, #[error("invalid chunk size")] @@ -180,7 +180,7 @@ impl AnvilWorld { b => return Err(ReadChunkError::UnknownCompressionScheme(b)), }; - let (data, _) = valence_nbt::from_binary_slice(&mut nbt_slice)?; + let (data, _) = Compound::from_binary(&mut nbt_slice)?; if !nbt_slice.is_empty() { return Err(ReadChunkError::IncompleteNbtRead); diff --git a/crates/valence_block/Cargo.toml b/crates/valence_block/Cargo.toml index 11cb1236e..0fcdcd2ce 100644 --- a/crates/valence_block/Cargo.toml +++ b/crates/valence_block/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true valence_core.workspace = true thiserror.workspace = true anyhow.workspace = true +glam.workspace = true [build-dependencies] anyhow.workspace = true diff --git a/crates/valence_block/build.rs b/crates/valence_block/build.rs index 92b472f43..a3d7829e2 100644 --- a/crates/valence_block/build.rs +++ b/crates/valence_block/build.rs @@ -38,8 +38,8 @@ impl Block { #[derive(Deserialize, Clone, Debug)] struct BlockEntityKind { id: u32, - namespace: String, - path: String, + ident: String, + name: String, } #[derive(Deserialize, Clone, Debug)] @@ -86,7 +86,7 @@ fn build() -> anyhow::Result { let kind_to_translation_key_arms = blocks .iter() .map(|b| { - let kind = ident(b.name.to_pascal_case()); + let kind = ident(b.name.replace('.', "_").to_pascal_case()); let translation_key = &b.translation_key; quote! { Self::#kind => #translation_key, @@ -97,7 +97,7 @@ fn build() -> anyhow::Result { let state_to_kind_arms = blocks .iter() .map(|b| { - let name = ident(b.name.to_pascal_case()); + let name = ident(b.name.replace('.', "_").to_pascal_case()); let mut token_stream = TokenStream::new(); let min_id = b.min_state_id(); @@ -161,14 +161,10 @@ fn build() -> anyhow::Result { let max_y = s.max_y; let max_z = s.max_z; quote! { - [ - #min_x, - #min_y, - #min_z, - #max_x, - #max_y, - #max_z, - ] + Aabb { + min: dvec3(#min_x, #min_y, #min_z), + max: dvec3(#max_x, #max_y, #max_z), + } } }); @@ -191,13 +187,13 @@ fn build() -> anyhow::Result { .iter() .filter(|&b| !b.properties.is_empty()) .map(|b| { - let block_kind_name = ident(b.name.to_pascal_case()); + let block_kind_name = ident(b.name.replace('.', "_").to_pascal_case()); let arms = b .properties .iter() .map(|p| { - let prop_name = ident(p.name.to_pascal_case()); + let prop_name = ident(p.name.replace('.', "_").to_pascal_case()); let min_state_id = b.min_state_id(); let product: u16 = b .properties @@ -211,7 +207,7 @@ fn build() -> anyhow::Result { let arms = p.values.iter().enumerate().map(|(i, v)| { let value_idx = i as u16; - let value_name = ident(v.to_pascal_case()); + let value_name = ident(v.replace('.', "_").to_pascal_case()); quote! { #value_idx => Some(PropValue::#value_name), } @@ -239,13 +235,13 @@ fn build() -> anyhow::Result { .iter() .filter(|&b| !b.properties.is_empty()) .map(|b| { - let block_kind_name = ident(b.name.to_pascal_case()); + let block_kind_name = ident(b.name.replace('.', "_").to_pascal_case()); let arms = b .properties .iter() .map(|p| { - let prop_name = ident(p.name.to_pascal_case()); + let prop_name = ident(p.name.replace('.', "_").to_pascal_case()); let min_state_id = b.min_state_id(); let product: u16 = b .properties @@ -263,7 +259,7 @@ fn build() -> anyhow::Result { .enumerate() .map(|(i, v)| { let val_idx = i as u16; - let val_name = ident(v.to_pascal_case()); + let val_name = ident(v.replace('.', "_").to_pascal_case()); quote! { PropValue::#val_name => Self(self.0 - (self.0 - #min_state_id) / #product % #values_count * #product @@ -293,7 +289,7 @@ fn build() -> anyhow::Result { let default_block_states = blocks .iter() .map(|b| { - let name = ident(b.name.to_shouty_snake_case()); + let name = ident(b.name.replace('.', "_").to_shouty_snake_case()); let state = b.default_state_id; let doc = format!("The default block state for `{}`.", b.name); quote! { @@ -307,10 +303,11 @@ fn build() -> anyhow::Result { .iter() .filter(|b| b.wall_variant_id.is_some()) .map(|b| { - let block_name = ident(b.name.to_shouty_snake_case()); + let block_name = ident(b.name.replace('.', "_").to_shouty_snake_case()); let wall_block_name = ident( blocks[b.wall_variant_id.unwrap() as usize] .name + .replace('.', "_") .to_shouty_snake_case(), ); quote! { @@ -335,8 +332,8 @@ fn build() -> anyhow::Result { let kind_to_state_arms = blocks .iter() .map(|b| { - let kind = ident(b.name.to_pascal_case()); - let state = ident(b.name.to_shouty_snake_case()); + let kind = ident(b.name.replace('.', "_").to_pascal_case()); + let state = ident(b.name.replace('.', "_").to_shouty_snake_case()); quote! { BlockKind::#kind => BlockState::#state, } @@ -345,14 +342,14 @@ fn build() -> anyhow::Result { let block_kind_variants = blocks .iter() - .map(|b| ident(b.name.to_pascal_case())) + .map(|b| ident(b.name.replace('.', "_").to_pascal_case())) .collect::>(); let block_kind_from_str_arms = blocks .iter() .map(|b| { let name = &b.name; - let name_ident = ident(name.to_pascal_case()); + let name_ident = ident(name.replace('.', "_").to_pascal_case()); quote! { #name => Some(BlockKind::#name_ident), } @@ -363,7 +360,7 @@ fn build() -> anyhow::Result { .iter() .map(|b| { let name = &b.name; - let name_ident = ident(name.to_pascal_case()); + let name_ident = ident(name.replace('.', "_").to_pascal_case()); quote! { BlockKind::#name_ident => #name, } @@ -374,8 +371,11 @@ fn build() -> anyhow::Result { .iter() .filter(|&b| !b.properties.is_empty()) .map(|b| { - let name = ident(b.name.to_pascal_case()); - let prop_names = b.properties.iter().map(|p| ident(p.name.to_pascal_case())); + let name = ident(b.name.replace('.', "_").to_pascal_case()); + let prop_names = b + .properties + .iter() + .map(|p| ident(p.name.replace('.', "_").to_pascal_case())); quote! { Self::#name => &[#(PropName::#prop_names,)*], @@ -386,7 +386,7 @@ fn build() -> anyhow::Result { let block_kind_to_item_kind_arms = blocks .iter() .map(|block| { - let name = ident(block.name.to_pascal_case()); + let name = ident(block.name.replace('.', "_").to_pascal_case()); let item_id = block.item_id; quote! { @@ -399,7 +399,7 @@ fn build() -> anyhow::Result { .iter() .filter(|block| block.item_id != 0) .map(|block| { - let name = ident(block.name.to_pascal_case()); + let name = ident(block.name.replace('.', "_").to_pascal_case()); let item_id = block.item_id; quote! { @@ -411,7 +411,7 @@ fn build() -> anyhow::Result { let block_kind_from_raw_arms = blocks .iter() .map(|block| { - let name = ident(block.name.to_pascal_case()); + let name = ident(block.name.replace('.', "_").to_pascal_case()); let id = block.id; quote! { @@ -423,10 +423,10 @@ fn build() -> anyhow::Result { let block_entity_kind_variants = block_entity_types .iter() .map(|block_entity| { - let name = ident(block_entity.path.to_pascal_case()); + let name = ident(block_entity.name.replace('.', "_").to_pascal_case()); let doc = format!( - "The block entity type `{}:{}` (ID {}).", - block_entity.namespace, block_entity.path, block_entity.id + "The block entity type `{}` (ID {}).", + block_entity.name, block_entity.id ); quote! { #[doc = #doc] @@ -439,7 +439,7 @@ fn build() -> anyhow::Result { .iter() .map(|block_entity| { let id = block_entity.id; - let name = ident(block_entity.path.to_pascal_case()); + let name = ident(block_entity.name.replace('.', "_").to_pascal_case()); quote! { #id => Some(Self::#name), @@ -451,7 +451,7 @@ fn build() -> anyhow::Result { .iter() .map(|block_entity| { let id = block_entity.id; - let name = ident(block_entity.path.to_pascal_case()); + let name = ident(block_entity.name.replace('.', "_").to_pascal_case()); quote! { Self::#name => #id, @@ -462,12 +462,11 @@ fn build() -> anyhow::Result { let block_entity_kind_from_ident_arms = block_entity_types .iter() .map(|block_entity| { - let name = ident(block_entity.path.to_pascal_case()); - let namespace = &block_entity.namespace; - let path = &block_entity.path; + let name = ident(block_entity.name.replace('.', "_").to_pascal_case()); + let ident = &block_entity.ident; quote! { - (#namespace, #path) => Some(Self::#name), + #ident => Some(Self::#name), } }) .collect::(); @@ -475,11 +474,8 @@ fn build() -> anyhow::Result { let block_entity_kind_to_ident_arms = block_entity_types .iter() .map(|block_entity| { - let name = ident(block_entity.path.to_pascal_case()); - let namespace = &block_entity.namespace; - let path = &block_entity.path; - - let ident = format!("{namespace}:{path}"); + let name = ident(block_entity.name.replace('.', "_").to_pascal_case()); + let ident = &block_entity.ident; quote! { Self::#name => ident!(#ident), @@ -496,13 +492,13 @@ fn build() -> anyhow::Result { let prop_name_variants = prop_names .iter() - .map(|&name| ident(name.to_pascal_case())) + .map(|&name| ident(name.replace('.', "_").to_pascal_case())) .collect::>(); let prop_name_from_str_arms = prop_names .iter() .map(|&name| { - let ident = ident(name.to_pascal_case()); + let ident = ident(name.replace('.', "_").to_pascal_case()); quote! { #name => Some(PropName::#ident), } @@ -512,7 +508,7 @@ fn build() -> anyhow::Result { let prop_name_to_str_arms = prop_names .iter() .map(|&name| { - let ident = ident(name.to_pascal_case()); + let ident = ident(name.replace('.', "_").to_pascal_case()); quote! { PropName::#ident => #name, } @@ -529,13 +525,13 @@ fn build() -> anyhow::Result { let prop_value_variants = prop_values .iter() - .map(|val| ident(val.to_pascal_case())) + .map(|val| ident(val.replace('.', "_").to_pascal_case())) .collect::>(); let prop_value_from_str_arms = prop_values .iter() .map(|val| { - let ident = ident(val.to_pascal_case()); + let ident = ident(val.replace('.', "_").to_pascal_case()); quote! { #val => Some(PropValue::#ident), } @@ -545,7 +541,7 @@ fn build() -> anyhow::Result { let prop_value_to_str_arms = prop_values .iter() .map(|val| { - let ident = ident(val.to_pascal_case()); + let ident = ident(val.replace('.', "_").to_pascal_case()); quote! { PropValue::#ident => #val, } @@ -577,6 +573,9 @@ fn build() -> anyhow::Result { let prop_value_count = prop_values.len(); Ok(quote! { + use valence_core::aabb::Aabb; + use glam::dvec3; + /// Represents the state of a block. This does not include block entity data such as /// the text on a sign, the design on a banner, or the content of a spawner. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] @@ -682,11 +681,11 @@ fn build() -> anyhow::Result { } } - const SHAPES: [[f64; 6]; #shape_count] = [ + const SHAPES: [Aabb; #shape_count] = [ #(#shapes,)* ]; - pub fn collision_shapes(self) -> impl ExactSizeIterator + FusedIterator + Clone { + pub fn collision_shapes(self) -> impl ExactSizeIterator + FusedIterator + Clone { let shape_idxs: &'static [u16] = match self.0 { #state_to_collision_shapes_arms _ => &[], @@ -943,13 +942,13 @@ fn build() -> anyhow::Result { } pub fn from_ident(ident: Ident<&str>) -> Option { - match (ident.namespace(), ident.path()) { + match ident.as_str() { #block_entity_kind_from_ident_arms _ => None } } - pub const fn ident(self) -> Ident<&'static str> { + pub fn ident(self) -> Ident<&'static str> { match self { #block_entity_kind_to_ident_arms } diff --git a/crates/valence_block/src/lib.rs b/crates/valence_block/src/lib.rs index 4a83c5668..d11227ed0 100644 --- a/crates/valence_block/src/lib.rs +++ b/crates/valence_block/src/lib.rs @@ -29,8 +29,8 @@ use thiserror::Error; use valence_core::ident; use valence_core::ident::Ident; use valence_core::item::ItemKind; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::{Decode, Encode}; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{Decode, Encode}; include!(concat!(env!("OUT_DIR"), "/block.rs")); diff --git a/crates/valence_client/Cargo.toml b/crates/valence_client/Cargo.toml index 8394490c9..617a44924 100644 --- a/crates/valence_client/Cargo.toml +++ b/crates/valence_client/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true anyhow.workspace = true bevy_app.workspace = true bevy_ecs.workspace = true +bitfield-struct.workspace = true bytes.workspace = true glam.workspace = true rand.workspace = true @@ -17,5 +18,5 @@ valence_core.workspace = true valence_dimension.workspace = true valence_entity.workspace = true valence_instance.workspace = true +valence_nbt.workspace = true valence_registry.workspace = true - diff --git a/crates/valence_client/src/action.rs b/crates/valence_client/src/action.rs index 086d4681f..4e548e6fb 100644 --- a/crates/valence_client/src/action.rs +++ b/crates/valence_client/src/action.rs @@ -1,13 +1,14 @@ use valence_core::block_pos::BlockPos; use valence_core::direction::Direction; -use valence_core::packet::c2s::play::player_action::Action; -use valence_core::packet::c2s::play::PlayerActionC2s; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; use super::*; use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; +use crate::packet::{PlayerAction, PlayerActionC2s}; pub(super) fn build(app: &mut App) { - app.add_event::() + app.add_event::() .add_system( handle_player_action .in_schedule(EventLoopSchedule) @@ -17,7 +18,7 @@ pub(super) fn build(app: &mut App) { } #[derive(Copy, Clone, Debug)] -pub struct Digging { +pub struct DiggingEvent { pub client: Entity, pub position: BlockPos, pub direction: Direction, @@ -47,7 +48,7 @@ impl ActionSequence { fn handle_player_action( mut clients: Query<&mut ActionSequence>, mut packets: EventReader, - mut digging_events: EventWriter, + mut digging_events: EventWriter, ) { for packet in packets.iter() { if let Some(pkt) = packet.decode::() { @@ -59,28 +60,28 @@ fn handle_player_action( // TODO: check that blocks are being broken at the appropriate speeds. match pkt.action { - Action::StartDestroyBlock => digging_events.send(Digging { + PlayerAction::StartDestroyBlock => digging_events.send(DiggingEvent { client: packet.client, position: pkt.position, direction: pkt.direction, state: DiggingState::Start, }), - Action::AbortDestroyBlock => digging_events.send(Digging { + PlayerAction::AbortDestroyBlock => digging_events.send(DiggingEvent { client: packet.client, position: pkt.position, direction: pkt.direction, state: DiggingState::Abort, }), - Action::StopDestroyBlock => digging_events.send(Digging { + PlayerAction::StopDestroyBlock => digging_events.send(DiggingEvent { client: packet.client, position: pkt.position, direction: pkt.direction, state: DiggingState::Stop, }), - Action::DropAllItems => {} - Action::DropItem => {} - Action::ReleaseUseItem => todo!(), // TODO: release use item. - Action::SwapItemWithOffhand => {} + PlayerAction::DropAllItems => {} + PlayerAction::DropItem => {} + PlayerAction::ReleaseUseItem => {} + PlayerAction::SwapItemWithOffhand => {} } } } @@ -99,3 +100,9 @@ fn acknowledge_player_actions( } } } + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_ACTION_RESPONSE_S2C)] +pub struct PlayerActionResponseS2c { + pub sequence: VarInt, +} diff --git a/crates/valence_client/src/chat.rs b/crates/valence_client/src/chat.rs new file mode 100644 index 000000000..4be42ae2a --- /dev/null +++ b/crates/valence_client/src/chat.rs @@ -0,0 +1,51 @@ +// TODO: delete this module in favor of valence_chat. + +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; +use valence_core::protocol::encode::WritePacket; +use valence_core::protocol::packet::chat::{ChatMessageC2s, GameMessageS2c}; +use valence_core::text::Text; + +use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; +use crate::Client; + +pub(super) fn build(app: &mut App) { + app.add_event::().add_system( + handle_chat_message + .in_schedule(EventLoopSchedule) + .in_base_set(EventLoopSet::PreUpdate), + ); +} + +#[derive(Clone, Debug)] +pub struct ChatMessageEvent { + pub client: Entity, + pub message: Box, + pub timestamp: u64, +} + +impl Client { + /// Sends a system message to the player which is visible in the chat. The + /// message is only visible to this client. + pub fn send_message(&mut self, msg: impl Into) { + self.write_packet(&GameMessageS2c { + chat: msg.into().into(), + overlay: false, + }); + } +} + +pub fn handle_chat_message( + mut packets: EventReader, + mut events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + events.send(ChatMessageEvent { + client: packet.client, + message: pkt.message.into(), + timestamp: pkt.timestamp, + }); + } + } +} diff --git a/crates/valence_client/src/command.rs b/crates/valence_client/src/command.rs index f6b9a915d..f3d5e4685 100644 --- a/crates/valence_client/src/command.rs +++ b/crates/valence_client/src/command.rs @@ -1,7 +1,7 @@ use bevy_app::prelude::*; use bevy_ecs::prelude::*; -use valence_core::packet::c2s::play::client_command::Action; -use valence_core::packet::c2s::play::ClientCommandC2s; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; use valence_entity::entity::Flags; use valence_entity::{entity, Pose}; @@ -74,7 +74,7 @@ fn handle_client_command( for packet in packets.iter() { if let Some(pkt) = packet.decode::() { match pkt.action { - Action::StartSneaking => { + ClientCommand::StartSneaking => { if let Ok((mut pose, mut flags)) = clients.get_mut(packet.client) { pose.0 = Pose::Sneaking; flags.set_sneaking(true); @@ -85,7 +85,7 @@ fn handle_client_command( state: SneakState::Start, }) } - Action::StopSneaking => { + ClientCommand::StopSneaking => { if let Ok((mut pose, mut flags)) = clients.get_mut(packet.client) { pose.0 = Pose::Standing; flags.set_sneaking(false); @@ -96,10 +96,10 @@ fn handle_client_command( state: SneakState::Stop, }) } - Action::LeaveBed => leave_bed_events.send(LeaveBed { + ClientCommand::LeaveBed => leave_bed_events.send(LeaveBed { client: packet.client, }), - Action::StartSprinting => { + ClientCommand::StartSprinting => { if let Ok((_, mut flags)) = clients.get_mut(packet.client) { flags.set_sprinting(true); } @@ -109,7 +109,7 @@ fn handle_client_command( state: SprintState::Start, }); } - Action::StopSprinting => { + ClientCommand::StopSprinting => { if let Ok((_, mut flags)) = clients.get_mut(packet.client) { flags.set_sprinting(false); } @@ -119,18 +119,18 @@ fn handle_client_command( state: SprintState::Stop, }) } - Action::StartJumpWithHorse => jump_with_horse_events.send(JumpWithHorse { + ClientCommand::StartJumpWithHorse => jump_with_horse_events.send(JumpWithHorse { client: packet.client, state: JumpWithHorseState::Start { power: pkt.jump_boost.0 as u8, }, }), - Action::StopJumpWithHorse => jump_with_horse_events.send(JumpWithHorse { + ClientCommand::StopJumpWithHorse => jump_with_horse_events.send(JumpWithHorse { client: packet.client, state: JumpWithHorseState::Stop, }), - Action::OpenHorseInventory => {} // TODO - Action::StartFlyingWithElytra => { + ClientCommand::OpenHorseInventory => {} // TODO + ClientCommand::StartFlyingWithElytra => { if let Ok((mut pose, _)) = clients.get_mut(packet.client) { pose.0 = Pose::FallFlying; } @@ -141,3 +141,24 @@ fn handle_client_command( } } } + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CLIENT_COMMAND_C2S)] +pub struct ClientCommandC2s { + pub entity_id: VarInt, + pub action: ClientCommand, + pub jump_boost: VarInt, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub enum ClientCommand { + StartSneaking, + StopSneaking, + LeaveBed, + StartSprinting, + StopSprinting, + StartJumpWithHorse, + StopJumpWithHorse, + OpenHorseInventory, + StartFlyingWithElytra, +} diff --git a/crates/valence_client/src/custom_payload.rs b/crates/valence_client/src/custom_payload.rs new file mode 100644 index 000000000..ed7b0b8b7 --- /dev/null +++ b/crates/valence_client/src/custom_payload.rs @@ -0,0 +1,58 @@ +use valence_core::protocol::raw::RawBytes; +use valence_core::protocol::{packet_id, Decode, Encode}; + +use super::*; +use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; + +pub(super) fn build(app: &mut App) { + app.add_event::().add_system( + handle_custom_payload + .in_schedule(EventLoopSchedule) + .in_base_set(EventLoopSet::PreUpdate), + ); +} + +#[derive(Clone, Debug)] +pub struct CustomPayloadEvent { + pub client: Entity, + pub channel: Ident, + pub data: Box<[u8]>, +} + +impl Client { + pub fn send_custom_payload(&mut self, channel: Ident<&str>, data: &[u8]) { + self.write_packet(&CustomPayloadS2c { + channel: channel.into(), + data: data.into(), + }); + } +} + +fn handle_custom_payload( + mut packets: EventReader, + mut events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + events.send(CustomPayloadEvent { + client: packet.client, + channel: pkt.channel.into(), + data: pkt.data.0.into(), + }) + } + } +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CUSTOM_PAYLOAD_C2S)] +pub struct CustomPayloadC2s<'a> { + pub channel: Ident>, + pub data: RawBytes<'a>, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CUSTOM_PAYLOAD_S2C)] +pub struct CustomPayloadS2c<'a> { + pub channel: Ident>, + pub data: RawBytes<'a>, +} diff --git a/crates/valence_client/src/event_loop.rs b/crates/valence_client/src/event_loop.rs index a6930faed..d829317dd 100644 --- a/crates/valence_client/src/event_loop.rs +++ b/crates/valence_client/src/event_loop.rs @@ -6,7 +6,8 @@ use bevy_ecs::schedule::ScheduleLabel; use bevy_ecs::system::SystemState; use bytes::Bytes; use tracing::{debug, warn}; -use valence_core::packet::{Decode, Packet}; +use valence_core::protocol::{Decode, Packet}; +use valence_entity::hitbox::HitboxUpdateSet; use crate::{Client, SpawnClientsSet}; @@ -14,7 +15,8 @@ pub(super) fn build(app: &mut App) { app.configure_set( RunEventLoopSet .in_base_set(CoreSet::PreUpdate) - .after(SpawnClientsSet), + .after(SpawnClientsSet) + .after(HitboxUpdateSet), ) .add_system(run_event_loop.in_set(RunEventLoopSet)) .add_event::(); @@ -67,9 +69,9 @@ impl PacketEvent { #[inline] pub fn decode<'a, P>(&'a self) -> Option

where - P: Packet<'a> + Decode<'a>, + P: Packet + Decode<'a>, { - if self.id == P::PACKET_ID { + if self.id == P::ID { let mut r = &self.data[..]; match P::decode(&mut r) { @@ -81,13 +83,13 @@ impl PacketEvent { warn!( "missed {} bytes while decoding packet {} (ID = {})", r.len(), - pkt.packet_name(), - P::PACKET_ID + P::NAME, + P::ID ); debug!("complete packet after partial decode: {pkt:?}"); } Err(e) => { - warn!("failed to decode packet with ID of {}: {e:#}", P::PACKET_ID); + warn!("failed to decode packet with ID of {}: {e:#}", P::ID); } } } @@ -118,7 +120,7 @@ fn run_event_loop( client: entity, timestamp: pkt.timestamp, id: pkt.id, - data: pkt.data, + data: pkt.body, }); let remaining = client.connection().len(); @@ -152,7 +154,7 @@ fn run_event_loop( client: *entity, timestamp: pkt.timestamp, id: pkt.id, - data: pkt.data, + data: pkt.body, }); *remaining -= 1; // Keep looping as long as there are packets to process this tick. diff --git a/crates/valence_client/src/hand_swing.rs b/crates/valence_client/src/hand_swing.rs new file mode 100644 index 000000000..86713508a --- /dev/null +++ b/crates/valence_client/src/hand_swing.rs @@ -0,0 +1,49 @@ +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; +use valence_core::hand::Hand; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_entity::{EntityAnimation, EntityAnimations}; + +use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; + +pub(super) fn build(app: &mut App) { + app.add_event::().add_system( + handle_hand_swing + .in_schedule(EventLoopSchedule) + .in_base_set(EventLoopSet::PreUpdate), + ); +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct HandSwingEvent { + pub client: Entity, + pub hand: Hand, +} + +fn handle_hand_swing( + mut packets: EventReader, + mut clients: Query<&mut EntityAnimations>, + mut events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + if let Ok(mut anim) = clients.get_mut(packet.client) { + anim.trigger(match pkt.hand { + Hand::Main => EntityAnimation::SwingMainHand, + Hand::Off => EntityAnimation::SwingOffHand, + }); + } + + events.send(HandSwingEvent { + client: packet.client, + hand: pkt.hand, + }); + } + } +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::HAND_SWING_C2S)] +pub struct HandSwingC2s { + pub hand: Hand, +} diff --git a/crates/valence_client/src/interact_block.rs b/crates/valence_client/src/interact_block.rs new file mode 100644 index 000000000..53d3ceabf --- /dev/null +++ b/crates/valence_client/src/interact_block.rs @@ -0,0 +1,73 @@ +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; +use glam::Vec3; +use valence_core::block_pos::BlockPos; +use valence_core::direction::Direction; +use valence_core::hand::Hand; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; + +use crate::action::ActionSequence; +use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; + +pub(super) fn build(app: &mut App) { + app.add_event::().add_system( + handle_interact_block + .in_schedule(EventLoopSchedule) + .in_base_set(EventLoopSet::PreUpdate), + ); +} + +#[derive(Copy, Clone, Debug)] +pub struct InteractBlockEvent { + pub client: Entity, + /// The hand that was used + pub hand: Hand, + /// The location of the block that was interacted with + pub position: BlockPos, + /// The face of the block that was clicked + pub face: Direction, + /// The position inside of the block that was clicked on + pub cursor_pos: Vec3, + /// Whether or not the player's head is inside a block + pub head_inside_block: bool, + /// Sequence number for synchronization + pub sequence: i32, +} + +fn handle_interact_block( + mut packets: EventReader, + mut clients: Query<&mut ActionSequence>, + mut events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + if let Ok(mut action_seq) = clients.get_mut(packet.client) { + action_seq.update(pkt.sequence.0); + } + + // TODO: check that the block interaction is valid. + + events.send(InteractBlockEvent { + client: packet.client, + hand: pkt.hand, + position: pkt.position, + face: pkt.face, + cursor_pos: pkt.cursor_pos, + head_inside_block: pkt.head_inside_block, + sequence: pkt.sequence.0, + }); + } + } +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_INTERACT_BLOCK_C2S)] +pub struct PlayerInteractBlockC2s { + pub hand: Hand, + pub position: BlockPos, + pub face: Direction, + pub cursor_pos: Vec3, + pub head_inside_block: bool, + pub sequence: VarInt, +} diff --git a/crates/valence_client/src/interact_entity.rs b/crates/valence_client/src/interact_entity.rs index 110563dc7..9593a9100 100644 --- a/crates/valence_client/src/interact_entity.rs +++ b/crates/valence_client/src/interact_entity.rs @@ -1,13 +1,15 @@ use bevy_app::prelude::*; use bevy_ecs::prelude::*; -use valence_core::packet::c2s::play::player_interact_entity::EntityInteraction; -use valence_core::packet::c2s::play::PlayerInteractEntityC2s; +use glam::Vec3; +use valence_core::hand::Hand; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; use valence_entity::EntityManager; use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; pub(super) fn build(app: &mut App) { - app.add_event::().add_system( + app.add_event::().add_system( handle_interact_entity .in_schedule(EventLoopSchedule) .in_base_set(EventLoopSet::PreUpdate), @@ -15,7 +17,7 @@ pub(super) fn build(app: &mut App) { } #[derive(Copy, Clone, Debug)] -pub struct InteractEntity { +pub struct InteractEntityEvent { pub client: Entity, /// The entity being interacted with. pub entity: Entity, @@ -25,10 +27,17 @@ pub struct InteractEntity { pub interact: EntityInteraction, } +#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] +pub enum EntityInteraction { + Interact(Hand), + Attack, + InteractAt { target: Vec3, hand: Hand }, +} + fn handle_interact_entity( mut packets: EventReader, entities: Res, - mut events: EventWriter, + mut events: EventWriter, ) { for packet in packets.iter() { if let Some(pkt) = packet.decode::() { @@ -37,7 +46,7 @@ fn handle_interact_entity( // within some configurable tolerance level. if let Some(entity) = entities.get_by_id(pkt.entity_id.0) { - events.send(InteractEntity { + events.send(InteractEntityEvent { client: packet.client, entity, sneaking: pkt.sneaking, @@ -47,3 +56,11 @@ fn handle_interact_entity( } } } + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_INTERACT_ENTITY_C2S)] +pub struct PlayerInteractEntityC2s { + pub entity_id: VarInt, + pub interact: EntityInteraction, + pub sneaking: bool, +} diff --git a/crates/valence_client/src/interact_item.rs b/crates/valence_client/src/interact_item.rs new file mode 100644 index 000000000..a2e23ec99 --- /dev/null +++ b/crates/valence_client/src/interact_item.rs @@ -0,0 +1,50 @@ +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; +use valence_core::hand::Hand; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; + +use crate::action::ActionSequence; +use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; + +pub(super) fn build(app: &mut App) { + app.add_event::().add_system( + handle_player_interact_item + .in_schedule(EventLoopSchedule) + .in_base_set(EventLoopSet::PreUpdate), + ); +} + +#[derive(Copy, Clone, Debug)] +pub struct InteractItemEvent { + pub client: Entity, + pub hand: Hand, + pub sequence: i32, +} + +fn handle_player_interact_item( + mut packets: EventReader, + mut clients: Query<&mut ActionSequence>, + mut events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + if let Ok(mut action_seq) = clients.get_mut(packet.client) { + action_seq.update(pkt.sequence.0); + } + + events.send(InteractItemEvent { + client: packet.client, + hand: pkt.hand, + sequence: pkt.sequence.0, + }); + } + } +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_INTERACT_ITEM_C2S)] +pub struct PlayerInteractItemC2s { + pub hand: Hand, + pub sequence: VarInt, +} diff --git a/crates/valence_client/src/keepalive.rs b/crates/valence_client/src/keepalive.rs index baa5af58d..c903cec9e 100644 --- a/crates/valence_client/src/keepalive.rs +++ b/crates/valence_client/src/keepalive.rs @@ -1,4 +1,4 @@ -use valence_core::packet::c2s::play::KeepAliveC2s; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; use valence_core::CoreSettings; use super::*; @@ -81,3 +81,15 @@ fn handle_keepalive_response( } } } + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::KEEP_ALIVE_C2S)] +pub struct KeepAliveC2s { + pub id: u64, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::KEEP_ALIVE_S2C)] +pub struct KeepAliveS2c { + pub id: u64, +} diff --git a/crates/valence_client/src/lib.rs b/crates/valence_client/src/lib.rs index 37c6eb64f..aec65a0bd 100644 --- a/crates/valence_client/src/lib.rs +++ b/crates/valence_client/src/lib.rs @@ -29,6 +29,10 @@ use bevy_ecs::query::WorldQuery; use bevy_ecs::system::Command; use bytes::{Bytes, BytesMut}; use glam::{DVec3, Vec3}; +use packet::{ + DeathMessageS2c, DisconnectS2c, GameEventKind, GameJoinS2c, GameStateChangeS2c, + PlayerRespawnS2c, PlayerSpawnPositionS2c, PlayerSpawnS2c, +}; use rand::Rng; use tracing::warn; use uuid::Uuid; @@ -38,45 +42,51 @@ use valence_core::chunk_pos::{ChunkPos, ChunkView}; use valence_core::despawn::Despawned; use valence_core::game_mode::GameMode; use valence_core::ident::Ident; -use valence_core::packet::byte_angle::ByteAngle; -use valence_core::packet::encode::{PacketEncoder, WritePacket}; -use valence_core::packet::global_pos::GlobalPos; -use valence_core::packet::s2c::play::game_state_change::GameEventKind; -use valence_core::packet::s2c::play::particle::Particle; -use valence_core::packet::s2c::play::player_position_look::Flags as PlayerPositionLookFlags; -use valence_core::packet::s2c::play::{ - ChunkLoadDistanceS2c, ChunkRenderDistanceCenterS2c, CustomPayloadS2c, DeathMessageS2c, - DisconnectS2c, EntitiesDestroyS2c, EntitySetHeadYawS2c, EntitySpawnS2c, EntityStatusS2c, - EntityTrackerUpdateS2c, EntityVelocityUpdateS2c, ExperienceOrbSpawnS2c, GameJoinS2c, - GameMessageS2c, GameStateChangeS2c, KeepAliveS2c, OverlayMessageS2c, ParticleS2c, PlaySoundS2c, - PlayerActionResponseS2c, PlayerPositionLookS2c, PlayerRespawnS2c, PlayerSpawnPositionS2c, - PlayerSpawnS2c, ResourcePackSendS2c, SubtitleS2c, TitleFadeS2c, TitleS2c, UnloadChunkS2c, -}; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::Packet; +use valence_core::particle::{Particle, ParticleS2c}; use valence_core::property::Property; +use valence_core::protocol::byte_angle::ByteAngle; +use valence_core::protocol::encode::{PacketEncoder, WritePacket}; +use valence_core::protocol::global_pos::GlobalPos; +use valence_core::protocol::packet::sound::{PlaySoundS2c, Sound, SoundCategory}; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{Encode, Packet}; use valence_core::scratch::ScratchBuf; -use valence_core::sound::{Sound, SoundCategory}; use valence_core::text::Text; use valence_core::uuid::UniqueId; use valence_core::Server; +use valence_entity::packet::{ + EntitiesDestroyS2c, EntitySetHeadYawS2c, EntitySpawnS2c, EntityStatusS2c, + EntityTrackerUpdateS2c, EntityVelocityUpdateS2c, ExperienceOrbSpawnS2c, +}; use valence_entity::player::PlayerEntityBundle; use valence_entity::{ ClearEntityChangesSet, EntityId, EntityKind, EntityStatus, HeadYaw, Location, Look, ObjectData, OldLocation, OldPosition, OnGround, PacketByteRange, Position, TrackedData, Velocity, }; +use valence_instance::packet::{ + ChunkLoadDistanceS2c, ChunkRenderDistanceCenterS2c, UnloadChunkS2c, +}; use valence_instance::{ClearInstanceChangesSet, Instance, WriteUpdatePacketsToInstancesSet}; -use valence_registry::{RegistryCodec, RegistryCodecSet}; +use valence_registry::{RegistryCodec, RegistryCodecSet, TagsRegistry}; pub mod action; +pub mod chat; pub mod command; +pub mod custom_payload; pub mod event_loop; +pub mod hand_swing; +pub mod interact_block; pub mod interact_entity; +pub mod interact_item; pub mod keepalive; -pub mod misc; pub mod movement; +pub mod op_level; +pub mod packet; +pub mod resource_pack; pub mod settings; +pub mod status; pub mod teleport; +pub mod title; pub mod weather; pub struct ClientPlugin; @@ -115,7 +125,6 @@ impl Plugin for ClientPlugin { update_game_mode, update_tracked_data.after(WriteUpdatePacketsToInstancesSet), init_tracked_data.after(WriteUpdatePacketsToInstancesSet), - update_op_level, ) .in_set(UpdateClientsSet), ) @@ -136,10 +145,17 @@ impl Plugin for ClientPlugin { keepalive::build(app); interact_entity::build(app); settings::build(app); - misc::build(app); action::build(app); teleport::build(app); weather::build(app); + chat::build(app); + custom_payload::build(app); + hand_swing::build(app); + interact_block::build(app); + interact_item::build(app); + op_level::build(app); + resource_pack::build(app); + status::build(app); } } @@ -156,7 +172,7 @@ pub struct ClientBundle { pub properties: Properties, pub compass_pos: CompassPos, pub game_mode: GameMode, - pub op_level: OpLevel, + pub op_level: op_level::OpLevel, pub action_sequence: action::ActionSequence, pub view_distance: ViewDistance, pub old_view_distance: OldViewDistance, @@ -189,7 +205,7 @@ impl ClientBundle { properties: Properties(args.properties), compass_pos: CompassPos::default(), game_mode: GameMode::default(), - op_level: OpLevel::default(), + op_level: op_level::OpLevel::default(), action_sequence: action::ActionSequence::default(), view_distance: ViewDistance::default(), old_view_distance: OldViewDistance(2), @@ -261,7 +277,7 @@ pub struct ReceivedPacket { /// This packet's ID. pub id: i32, /// The content of the packet, excluding the leading varint packet ID. - pub data: Bytes, + pub body: Bytes, } impl Drop for Client { @@ -273,7 +289,10 @@ impl Drop for Client { /// Writes packets into this client's packet buffer. The buffer is flushed at /// the end of the tick. impl WritePacket for Client { - fn write_packet<'a>(&mut self, packet: &impl Packet<'a>) { + fn write_packet

(&mut self, packet: &P) + where + P: Packet + Encode, + { self.enc.write_packet(packet) } @@ -307,22 +326,6 @@ impl Client { } } - /// Sends a system message to the player which is visible in the chat. The - /// message is only visible to this client. - pub fn send_message(&mut self, msg: impl Into) { - self.write_packet(&GameMessageS2c { - chat: msg.into().into(), - overlay: false, - }); - } - - pub fn send_custom_payload(&mut self, channel: Ident<&str>, data: &[u8]) { - self.write_packet(&CustomPayloadS2c { - channel: channel.into(), - data: data.into(), - }); - } - /// Kills the client and shows `message` on the death screen. If an entity /// killed the player, you should supply it as `killer`. pub fn kill(&mut self, killer: Option, message: impl Into) { @@ -341,69 +344,6 @@ impl Client { }); } - /// Requests that the client download and enable a resource pack. - /// - /// # Arguments - /// * `url` - The URL of the resource pack file. - /// * `hash` - The SHA-1 hash of the resource pack file. Any value other - /// than a 40-character hexadecimal string is ignored by the client. - /// * `forced` - Whether a client should be kicked from the server upon - /// declining the pack (this is enforced client-side) - /// * `prompt_message` - A message to be displayed with the resource pack - /// dialog. - pub fn set_resource_pack( - &mut self, - url: &str, - hash: &str, - forced: bool, - prompt_message: Option, - ) { - self.write_packet(&ResourcePackSendS2c { - url, - hash, - forced, - prompt_message: prompt_message.map(|t| t.into()), - }); - } - - /// Sets the title this client sees. - /// - /// A title is a large piece of text displayed in the center of the screen - /// which may also include a subtitle underneath it. The title can be - /// configured to fade in and out using the [`TitleFadeS2c`] - /// struct. - pub fn set_title( - &mut self, - title: impl Into, - subtitle: impl Into, - animation: impl Into>, - ) { - let title = title.into().into(); - let subtitle = subtitle.into(); - - self.write_packet(&TitleS2c { title_text: title }); - - if !subtitle.is_empty() { - self.write_packet(&SubtitleS2c { - subtitle_text: subtitle.into(), - }); - } - - if let Some(anim) = animation.into() { - self.write_packet(&anim); - } - } - - /// Sets the action bar for this client. - /// - /// The action bar is a small piece of text displayed at the bottom of the - /// screen, above the hotbar. - pub fn set_action_bar(&mut self, text: impl Into) { - self.write_packet(&OverlayMessageS2c { - action_bar_text: text.into().into(), - }); - } - /// Puts a particle effect at the given position, only for this client. /// /// If you want to show a particle effect to all players, use @@ -593,20 +533,6 @@ pub struct Ip(pub IpAddr); #[derive(Component, Copy, Clone, PartialEq, Eq, Default, Debug)] pub struct CompassPos(pub BlockPos); -#[derive(Component, Clone, PartialEq, Eq, Default, Debug)] -pub struct OpLevel(u8); - -impl OpLevel { - pub fn get(&self) -> u8 { - self.0 - } - - /// Sets the op level. Value is clamped to `0..=3`. - pub fn set(&mut self, lvl: u8) { - self.0 = lvl.min(3); - } -} - #[derive(Component, Clone, PartialEq, Eq, Debug)] pub struct ViewDistance(u8); @@ -752,6 +678,7 @@ struct ClientJoinQuery { fn initial_join( codec: Res, + tags: Res, mut clients: Query>, instances: Query<&Instance>, mut commands: Commands, @@ -798,6 +725,8 @@ fn initial_join( last_death_location, }); + q.client.enc.append_bytes(tags.sync_tags_packet()); + /* // TODO: enable all the features? q.client.write_packet(&FeatureFlags { @@ -1269,12 +1198,3 @@ fn update_tracked_data(mut clients: Query<(&mut Client, &TrackedData)>) { } } } - -fn update_op_level(mut clients: Query<(&mut Client, &OpLevel), Changed>) { - for (mut client, lvl) in &mut clients { - client.write_packet(&EntityStatusS2c { - entity_id: 0, - entity_status: 24 + lvl.0, - }); - } -} diff --git a/crates/valence_client/src/misc.rs b/crates/valence_client/src/misc.rs deleted file mode 100644 index a3f6fb043..000000000 --- a/crates/valence_client/src/misc.rs +++ /dev/null @@ -1,159 +0,0 @@ -use bevy_app::prelude::*; -use bevy_ecs::prelude::*; -use glam::Vec3; -use valence_core::block_pos::BlockPos; -use valence_core::direction::Direction; -use valence_core::hand::Hand; -use valence_core::packet::c2s::play::{ - ChatMessageC2s, ClientStatusC2s, HandSwingC2s, PlayerInteractBlockC2s, PlayerInteractItemC2s, - ResourcePackStatusC2s, -}; -use valence_entity::{EntityAnimation, EntityAnimations}; - -use super::action::ActionSequence; -use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; - -pub(super) fn build(app: &mut App) { - app.add_event::() - .add_event::() - .add_event::() - .add_event::() - .add_event::() - .add_event::() - .add_system( - handle_misc_packets - .in_schedule(EventLoopSchedule) - .in_base_set(EventLoopSet::PreUpdate), - ); -} - -#[derive(Copy, Clone, Debug)] -pub struct HandSwing { - pub client: Entity, - pub hand: Hand, -} - -#[derive(Copy, Clone, Debug)] -pub struct InteractBlock { - pub client: Entity, - /// The hand that was used - pub hand: Hand, - /// The location of the block that was interacted with - pub position: BlockPos, - /// The face of the block that was clicked - pub face: Direction, - /// The position inside of the block that was clicked on - pub cursor_pos: Vec3, - /// Whether or not the player's head is inside a block - pub head_inside_block: bool, - /// Sequence number for synchronization - pub sequence: i32, -} - -#[derive(Clone, Debug)] -pub struct ChatMessage { - pub client: Entity, - pub message: Box, - pub timestamp: u64, -} - -#[derive(Copy, Clone, Debug)] -pub struct Respawn { - pub client: Entity, -} - -#[derive(Copy, Clone, Debug)] -pub struct RequestStats { - pub client: Entity, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum ResourcePackStatus { - /// The client has accepted the server's resource pack. - Accepted, - /// The client has declined the server's resource pack. - Declined, - /// The client has successfully loaded the server's resource pack. - Loaded, - /// The client has failed to download the server's resource pack. - FailedDownload, -} - -#[derive(Clone, Debug)] -pub struct ResourcePackStatusChange { - pub client: Entity, - pub status: ResourcePackStatus, -} - -#[allow(clippy::too_many_arguments)] -fn handle_misc_packets( - mut packets: EventReader, - mut clients: Query<(&mut ActionSequence, &mut EntityAnimations)>, - mut hand_swing_events: EventWriter, - mut interact_block_events: EventWriter, - mut chat_message_events: EventWriter, - mut respawn_events: EventWriter, - mut request_stats_events: EventWriter, - mut resource_pack_status_change_events: EventWriter, -) { - for packet in packets.iter() { - if let Some(pkt) = packet.decode::() { - if let Ok((_, mut animations)) = clients.get_mut(packet.client) { - animations.trigger(match pkt.hand { - Hand::Main => EntityAnimation::SwingMainHand, - Hand::Off => EntityAnimation::SwingOffHand, - }); - } - - hand_swing_events.send(HandSwing { - client: packet.client, - hand: pkt.hand, - }); - } else if let Some(pkt) = packet.decode::() { - if let Ok((mut action_seq, _)) = clients.get_mut(packet.client) { - action_seq.update(pkt.sequence.0); - } - - interact_block_events.send(InteractBlock { - client: packet.client, - hand: pkt.hand, - position: pkt.position, - face: pkt.face, - cursor_pos: pkt.cursor_pos, - head_inside_block: pkt.head_inside_block, - sequence: pkt.sequence.0, - }); - } else if let Some(pkt) = packet.decode::() { - if let Ok((mut action_seq, _)) = clients.get_mut(packet.client) { - action_seq.update(pkt.sequence.0); - } - - // TODO - } else if let Some(pkt) = packet.decode::() { - chat_message_events.send(ChatMessage { - client: packet.client, - message: pkt.message.into(), - timestamp: pkt.timestamp, - }); - } else if let Some(pkt) = packet.decode::() { - match pkt { - ClientStatusC2s::PerformRespawn => respawn_events.send(Respawn { - client: packet.client, - }), - ClientStatusC2s::RequestStats => request_stats_events.send(RequestStats { - client: packet.client, - }), - } - } else if let Some(pkt) = packet.decode::() { - resource_pack_status_change_events.send(ResourcePackStatusChange { - client: packet.client, - status: match pkt { - ResourcePackStatusC2s::Accepted => ResourcePackStatus::Accepted, - ResourcePackStatusC2s::Declined => ResourcePackStatus::Declined, - ResourcePackStatusC2s::SuccessfullyLoaded => ResourcePackStatus::Loaded, - ResourcePackStatusC2s::FailedDownload => ResourcePackStatus::FailedDownload, - }, - }); - } - } -} diff --git a/crates/valence_client/src/movement.rs b/crates/valence_client/src/movement.rs index 941a8ccf8..efe251016 100644 --- a/crates/valence_client/src/movement.rs +++ b/crates/valence_client/src/movement.rs @@ -1,9 +1,7 @@ use bevy_app::prelude::*; use bevy_ecs::prelude::*; use glam::DVec3; -use valence_core::packet::c2s::play::{ - Full, LookAndOnGround, OnGroundOnly, PositionAndOnGround, VehicleMoveC2s, -}; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; use valence_entity::{HeadYaw, Look, OnGround, Position}; use super::teleport::TeleportState; @@ -11,7 +9,7 @@ use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; pub(super) fn build(app: &mut App) { app.init_resource::() - .add_event::() + .add_event::() .add_system( handle_client_movement .in_schedule(EventLoopSchedule) @@ -27,7 +25,7 @@ pub struct MovementSettings { /// Event sent when a client successfully moves. #[derive(Clone, Debug)] -pub struct Movement { +pub struct MovementEvent { pub client: Entity, pub position: DVec3, pub old_position: DVec3, @@ -46,14 +44,14 @@ fn handle_client_movement( &mut OnGround, &mut TeleportState, )>, - mut movement_events: EventWriter, + mut movement_events: EventWriter, ) { for packet in packets.iter() { - if let Some(pkt) = packet.decode::() { + if let Some(pkt) = packet.decode::() { if let Ok((pos, look, head_yaw, on_ground, teleport_state)) = clients.get_mut(packet.client) { - let mov = Movement { + let mov = MovementEvent { client: packet.client, position: pkt.position, old_position: pos.0, @@ -73,11 +71,11 @@ fn handle_client_movement( &mut movement_events, ); } - } else if let Some(pkt) = packet.decode::() { + } else if let Some(pkt) = packet.decode::() { if let Ok((pos, look, head_yaw, on_ground, teleport_state)) = clients.get_mut(packet.client) { - let mov = Movement { + let mov = MovementEvent { client: packet.client, position: pkt.position, old_position: pos.0, @@ -100,11 +98,11 @@ fn handle_client_movement( &mut movement_events, ); } - } else if let Some(pkt) = packet.decode::() { + } else if let Some(pkt) = packet.decode::() { if let Ok((pos, look, head_yaw, on_ground, teleport_state)) = clients.get_mut(packet.client) { - let mov = Movement { + let mov = MovementEvent { client: packet.client, position: pos.0, old_position: pos.0, @@ -127,11 +125,11 @@ fn handle_client_movement( &mut movement_events, ); } - } else if let Some(pkt) = packet.decode::() { + } else if let Some(pkt) = packet.decode::() { if let Ok((pos, look, head_yaw, on_ground, teleport_state)) = clients.get_mut(packet.client) { - let mov = Movement { + let mov = MovementEvent { client: packet.client, position: pos.0, old_position: pos.0, @@ -155,7 +153,7 @@ fn handle_client_movement( if let Ok((pos, look, head_yaw, on_ground, teleport_state)) = clients.get_mut(packet.client) { - let mov = Movement { + let mov = MovementEvent { client: packet.client, position: pkt.position, old_position: pos.0, @@ -183,13 +181,13 @@ fn handle_client_movement( } fn handle( - mov: Movement, + mov: MovementEvent, mut pos: Mut, mut look: Mut, mut head_yaw: Mut, mut on_ground: Mut, mut teleport_state: Mut, - movement_events: &mut EventWriter, + movement_events: &mut EventWriter, ) { if teleport_state.pending_teleports() != 0 { return; @@ -207,3 +205,41 @@ fn handle( movement_events.send(mov); } + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::POSITION_AND_ON_GROUND)] +pub struct PositionAndOnGroundC2s { + pub position: DVec3, + pub on_ground: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::FULL)] +pub struct FullC2s { + pub position: DVec3, + pub yaw: f32, + pub pitch: f32, + pub on_ground: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOOK_AND_ON_GROUND)] +pub struct LookAndOnGroundC2s { + pub yaw: f32, + pub pitch: f32, + pub on_ground: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ON_GROUND_ONLY)] +pub struct OnGroundOnlyC2s { + pub on_ground: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::VEHICLE_MOVE_C2S)] +pub struct VehicleMoveC2s { + pub position: DVec3, + pub yaw: f32, + pub pitch: f32, +} diff --git a/crates/valence_client/src/op_level.rs b/crates/valence_client/src/op_level.rs new file mode 100644 index 000000000..edbbf257c --- /dev/null +++ b/crates/valence_client/src/op_level.rs @@ -0,0 +1,30 @@ +use valence_entity::packet::EntityStatusS2c; + +use super::*; + +pub(super) fn build(app: &mut App) { + app.add_system(update_op_level.in_set(UpdateClientsSet)); +} + +#[derive(Component, Clone, PartialEq, Eq, Default, Debug)] +pub struct OpLevel(u8); + +impl OpLevel { + pub fn get(&self) -> u8 { + self.0 + } + + /// Sets the op level. Value is clamped to `0..=3`. + pub fn set(&mut self, lvl: u8) { + self.0 = lvl.min(3); + } +} + +fn update_op_level(mut clients: Query<(&mut Client, &OpLevel), Changed>) { + for (mut client, lvl) in &mut clients { + client.write_packet(&EntityStatusS2c { + entity_id: 0, + entity_status: 24 + lvl.0, + }); + } +} diff --git a/crates/valence_client/src/packet.rs b/crates/valence_client/src/packet.rs new file mode 100644 index 000000000..ffa0e6ea7 --- /dev/null +++ b/crates/valence_client/src/packet.rs @@ -0,0 +1,468 @@ +//! Common packets for this crate. + +use std::borrow::Cow; + +use bitfield_struct::bitfield; +use glam::DVec3; +use uuid::Uuid; +use valence_core::block_pos::BlockPos; +use valence_core::difficulty::Difficulty; +use valence_core::direction::Direction; +use valence_core::game_mode::GameMode; +use valence_core::hand::Hand; +use valence_core::ident::Ident; +use valence_core::protocol::byte_angle::ByteAngle; +use valence_core::protocol::global_pos::GlobalPos; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::var_long::VarLong; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_core::text::Text; +use valence_nbt::Compound; + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BUNDLE_SPLITTER)] +pub struct BundleSplitterS2c; + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BOAT_PADDLE_STATE_C2S)] +pub struct BoatPaddleStateC2s { + pub left_paddle_turning: bool, + pub right_paddle_turning: bool, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BOOK_UPDATE_C2S)] +pub struct BookUpdateC2s<'a> { + pub slot: VarInt, + pub entries: Vec<&'a str>, + pub title: Option<&'a str>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::JIGSAW_GENERATING_C2S)] +pub struct JigsawGeneratingC2s { + pub position: BlockPos, + pub levels: VarInt, + pub keep_jigsaws: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAY_PONG_C2S)] +pub struct PlayPongC2s { + pub id: i32, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_ACTION_C2S)] +pub struct PlayerActionC2s { + pub action: PlayerAction, + pub position: BlockPos, + pub direction: Direction, + pub sequence: VarInt, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub enum PlayerAction { + StartDestroyBlock, + AbortDestroyBlock, + StopDestroyBlock, + DropAllItems, + DropItem, + ReleaseUseItem, + SwapItemWithOffhand, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_INPUT_C2S)] +pub struct PlayerInputC2s { + pub sideways: f32, + pub forward: f32, + pub flags: PlayerInputFlags, +} + +#[bitfield(u8)] +#[derive(PartialEq, Eq, Encode, Decode)] +pub struct PlayerInputFlags { + pub jump: bool, + pub unmount: bool, + #[bits(6)] + _pad: u8, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::QUERY_BLOCK_NBT_C2S)] +pub struct QueryBlockNbtC2s { + pub transaction_id: VarInt, + pub position: BlockPos, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::QUERY_ENTITY_NBT_C2S)] +pub struct QueryEntityNbtC2s { + pub transaction_id: VarInt, + pub entity_id: VarInt, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SPECTATOR_TELEPORT_C2S)] +pub struct SpectatorTeleportC2s { + pub target: Uuid, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_COMMAND_BLOCK_MINECART_C2S)] +pub struct UpdateCommandBlockMinecartC2s<'a> { + pub entity_id: VarInt, + pub command: &'a str, + pub track_output: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_COMMAND_BLOCK_C2S)] +pub struct UpdateCommandBlockC2s<'a> { + pub position: BlockPos, + pub command: &'a str, + pub mode: UpdateCommandBlockMode, + pub flags: UpdateCommandBlockFlags, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub enum UpdateCommandBlockMode { + Sequence, + Auto, + Redstone, +} + +#[bitfield(u8)] +#[derive(PartialEq, Eq, Encode, Decode)] +pub struct UpdateCommandBlockFlags { + pub track_output: bool, + pub conditional: bool, + pub automatic: bool, + #[bits(5)] + _pad: u8, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_DIFFICULTY_LOCK_C2S)] +pub struct UpdateDifficultyLockC2s { + pub locked: bool, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_DIFFICULTY_C2S)] +pub struct UpdateDifficultyC2s { + pub difficulty: Difficulty, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_JIGSAW_C2S)] +pub struct UpdateJigsawC2s<'a> { + pub position: BlockPos, + pub name: Ident>, + pub target: Ident>, + pub pool: Ident>, + pub final_state: &'a str, + pub joint_type: &'a str, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_PLAYER_ABILITIES_C2S)] +pub enum UpdatePlayerAbilitiesC2s { + #[packet(tag = 0b00)] + StopFlying, + #[packet(tag = 0b10)] + StartFlying, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_SIGN_C2S)] +pub struct UpdateSignC2s<'a> { + pub position: BlockPos, + pub lines: [&'a str; 4], +} + +pub mod structure_block { + + use super::*; + + #[derive(Copy, Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::UPDATE_STRUCTURE_BLOCK_C2S)] + pub struct UpdateStructureBlockC2s<'a> { + pub position: BlockPos, + pub action: Action, + pub mode: Mode, + pub name: &'a str, + pub offset_xyz: [i8; 3], + pub size_xyz: [i8; 3], + pub mirror: Mirror, + pub rotation: Rotation, + pub metadata: &'a str, + pub integrity: f32, + pub seed: VarLong, + pub flags: Flags, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum Action { + UpdateData, + SaveStructure, + LoadStructure, + DetectSize, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum Mode { + Save, + Load, + Corner, + Data, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum Mirror { + None, + LeftRight, + FrontBack, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum Rotation { + None, + Clockwise90, + Clockwise180, + Counterclockwise90, + } + + #[bitfield(u8)] + #[derive(PartialEq, Eq, Encode, Decode)] + pub struct Flags { + pub ignore_entities: bool, + pub show_air: bool, + pub show_bounding_box: bool, + #[bits(5)] + _pad: u8, + } +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::DEATH_MESSAGE_S2C)] +pub struct DeathMessageS2c<'a> { + pub player_id: VarInt, + /// Killer's entity ID, -1 if no killer + pub entity_id: i32, + pub message: Cow<'a, Text>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::DAMAGE_TILT_S2C)] +pub struct DamageTiltS2c { + /// The ID of the entity taking damage. + pub entity_id: VarInt, + /// The direction the damage is coming from in relation to the entity. + pub yaw: f32, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::DIFFICULTY_S2C)] +pub struct DifficultyS2c { + pub difficulty: Difficulty, + pub locked: bool, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::DISCONNECT_S2C)] +pub struct DisconnectS2c<'a> { + pub reason: Cow<'a, Text>, +} + +/// Unused by notchian clients. +#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTER_COMBAT_S2C)] +pub struct EnterCombatS2c; + +/// Unused by notchian clients. +#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::END_COMBAT_S2C)] +pub struct EndCombatS2c { + pub duration: VarInt, + pub entity_id: i32, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::EXPERIENCE_BAR_UPDATE_S2C)] +pub struct ExperienceBarUpdateS2c { + pub bar: f32, + pub level: VarInt, + pub total_xp: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::FEATURES_S2C)] +pub struct FeaturesS2c<'a> { + pub features: Vec>>, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::GAME_JOIN_S2C)] +pub struct GameJoinS2c<'a> { + pub entity_id: i32, + pub is_hardcore: bool, + pub game_mode: GameMode, + /// Same values as `game_mode` but with -1 to indicate no previous. + pub previous_game_mode: i8, + pub dimension_names: Vec>>, + pub registry_codec: Cow<'a, Compound>, + pub dimension_type_name: Ident>, + pub dimension_name: Ident>, + pub hashed_seed: i64, + pub max_players: VarInt, + pub view_distance: VarInt, + pub simulation_distance: VarInt, + pub reduced_debug_info: bool, + pub enable_respawn_screen: bool, + pub is_debug: bool, + pub is_flat: bool, + pub last_death_location: Option>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::GAME_STATE_CHANGE_S2C)] +pub struct GameStateChangeS2c { + pub kind: GameEventKind, + pub value: f32, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub enum GameEventKind { + NoRespawnBlockAvailable, + EndRaining, + BeginRaining, + ChangeGameMode, + WinGame, + DemoEvent, + ArrowHitPlayer, + RainLevelChange, + ThunderLevelChange, + PlayPufferfishStingSound, + PlayElderGuardianMobAppearance, + EnableRespawnScreen, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::HEALTH_UPDATE_S2C)] +pub struct HealthUpdateS2c { + pub health: f32, + pub food: VarInt, + pub food_saturation: f32, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_ABILITIES_S2C)] +pub struct PlayerAbilitiesS2c { + pub flags: PlayerAbilitiesFlags, + pub flying_speed: f32, + pub fov_modifier: f32, +} + +#[bitfield(u8)] +#[derive(PartialEq, Eq, Encode, Decode)] +pub struct PlayerAbilitiesFlags { + pub invulnerable: bool, + pub flying: bool, + pub allow_flying: bool, + pub instant_break: bool, + #[bits(4)] + _pad: u8, +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_RESPAWN_S2C)] +pub struct PlayerRespawnS2c<'a> { + pub dimension_type_name: Ident>, + pub dimension_name: Ident>, + pub hashed_seed: u64, + pub game_mode: GameMode, + pub previous_game_mode: i8, + pub is_debug: bool, + pub is_flat: bool, + pub copy_metadata: bool, + pub last_death_location: Option>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_SPAWN_POSITION_S2C)] +pub struct PlayerSpawnPositionS2c { + pub position: BlockPos, + pub angle: f32, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_SPAWN_S2C)] +pub struct PlayerSpawnS2c { + pub entity_id: VarInt, + pub player_uuid: Uuid, + pub position: DVec3, + pub yaw: ByteAngle, + pub pitch: ByteAngle, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SERVER_METADATA_S2C)] +pub struct ServerMetadataS2c<'a> { + pub motd: Cow<'a, Text>, + pub icon: Option<&'a [u8]>, + pub enforce_secure_chat: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SIGN_EDITOR_OPEN_S2C)] +pub struct SignEditorOpenS2c { + pub location: BlockPos, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SIMULATION_DISTANCE_S2C)] +pub struct SimulationDistanceS2c { + pub simulation_distance: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::STATISTICS_S2C)] +pub struct StatisticsS2c { + pub statistics: Vec, +} + +#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] +pub struct Statistic { + pub category_id: VarInt, + pub statistic_id: VarInt, + pub value: VarInt, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::VEHICLE_MOVE_S2C)] +pub struct VehicleMoveS2c { + pub position: DVec3, + pub yaw: f32, + pub pitch: f32, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::OPEN_WRITTEN_BOOK_S2C)] +pub struct OpenWrittenBookS2c { + pub hand: Hand, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAY_PING_S2C)] +pub struct PlayPingS2c { + pub id: i32, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::NBT_QUERY_RESPONSE_S2C)] +pub struct NbtQueryResponseS2c { + pub transaction_id: VarInt, + pub nbt: Compound, +} diff --git a/crates/valence_client/src/resource_pack.rs b/crates/valence_client/src/resource_pack.rs new file mode 100644 index 000000000..855dc2bfc --- /dev/null +++ b/crates/valence_client/src/resource_pack.rs @@ -0,0 +1,89 @@ +use std::borrow::Cow; + +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; +use valence_core::protocol::encode::WritePacket; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_core::text::Text; + +use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; +use crate::Client; + +pub(super) fn build(app: &mut App) { + app.add_event::().add_system( + handle_resource_pack_status + .in_schedule(EventLoopSchedule) + .in_base_set(EventLoopSet::PreUpdate), + ); +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct ResourcePackStatusEvent { + pub client: Entity, + pub status: ResourcePackStatus, +} + +pub use ResourcePackStatusC2s as ResourcePackStatus; + +impl Client { + /// Requests that the client download and enable a resource pack. + /// + /// # Arguments + /// * `url` - The URL of the resource pack file. + /// * `hash` - The SHA-1 hash of the resource pack file. Any value other + /// than a 40-character hexadecimal string is ignored by the client. + /// * `forced` - Whether a client should be kicked from the server upon + /// declining the pack (this is enforced client-side) + /// * `prompt_message` - A message to be displayed with the resource pack + /// dialog. + pub fn set_resource_pack( + &mut self, + url: &str, + hash: &str, + forced: bool, + prompt_message: Option, + ) { + self.write_packet(&ResourcePackSendS2c { + url, + hash, + forced, + prompt_message: prompt_message.map(|t| t.into()), + }); + } +} + +fn handle_resource_pack_status( + mut packets: EventReader, + mut events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + events.send(ResourcePackStatusEvent { + client: packet.client, + status: pkt, + }) + } + } +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::RESOURCE_PACK_SEND_S2C)] +pub struct ResourcePackSendS2c<'a> { + pub url: &'a str, + pub hash: &'a str, + pub forced: bool, + pub prompt_message: Option>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::RESOURCE_PACK_STATUS_C2S)] +pub enum ResourcePackStatusC2s { + /// The client has successfully loaded the server's resource pack. + SuccessfullyLoaded, + /// The client has declined the server's resource pack. + Declined, + /// The client has failed to download the server's resource pack. + FailedDownload, + /// The client has accepted the server's resource pack. + Accepted, +} diff --git a/crates/valence_client/src/settings.rs b/crates/valence_client/src/settings.rs index 3e6581ffe..1a6009fba 100644 --- a/crates/valence_client/src/settings.rs +++ b/crates/valence_client/src/settings.rs @@ -1,9 +1,11 @@ -pub use valence_core::packet::c2s::play::client_settings::ChatMode; -use valence_core::packet::c2s::play::ClientSettingsC2s; -pub use valence_entity::player::{MainArm, PlayerModelParts}; +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; +use bitfield_struct::bitfield; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_entity::player::{self, PlayerModelParts}; -use super::*; use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; +use crate::ViewDistance; pub(super) fn build(app: &mut App) { app.add_system( @@ -13,6 +15,7 @@ pub(super) fn build(app: &mut App) { ); } +/// Component containing client-controlled settings about a client. #[derive(Component, Default, Debug)] pub struct ClientSettings { pub locale: Box, @@ -28,7 +31,7 @@ fn handle_client_settings( &mut ViewDistance, &mut ClientSettings, &mut PlayerModelParts, - &mut MainArm, + &mut player::MainArm, )>, ) { for packet in packets.iter() { @@ -45,8 +48,55 @@ fn handle_client_settings( settings.allow_server_listings = pkt.allow_server_listings; model_parts.set_if_neq(PlayerModelParts(u8::from(pkt.displayed_skin_parts) as i8)); - main_arm.set_if_neq(MainArm(pkt.main_arm as i8)); + main_arm.set_if_neq(player::MainArm(pkt.main_arm as i8)); } } } } + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CLIENT_SETTINGS_C2S)] +pub struct ClientSettingsC2s<'a> { + pub locale: &'a str, + pub view_distance: u8, + pub chat_mode: ChatMode, + pub chat_colors: bool, + pub displayed_skin_parts: DisplayedSkinParts, + pub main_arm: MainArm, + pub enable_text_filtering: bool, + pub allow_server_listings: bool, +} + +#[bitfield(u8)] +#[derive(PartialEq, Eq, Encode, Decode)] +pub struct DisplayedSkinParts { + pub cape: bool, + pub jacket: bool, + pub left_sleeve: bool, + pub right_sleeve: bool, + pub left_pants_leg: bool, + pub right_pants_leg: bool, + pub hat: bool, + _pad: bool, +} + +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Encode, Decode)] +pub enum ChatMode { + Enabled, + CommandsOnly, + #[default] + Hidden, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Encode, Decode)] +pub enum MainArm { + Left, + #[default] + Right, +} + +impl From for player::MainArm { + fn from(value: MainArm) -> Self { + Self(value as i8) + } +} diff --git a/crates/valence_client/src/status.rs b/crates/valence_client/src/status.rs new file mode 100644 index 000000000..ba163c740 --- /dev/null +++ b/crates/valence_client/src/status.rs @@ -0,0 +1,50 @@ +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; + +use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; + +pub(super) fn build(app: &mut App) { + app.add_event::() + .add_event::() + .add_system( + handle_status + .in_schedule(EventLoopSchedule) + .in_base_set(EventLoopSet::PreUpdate), + ); +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct RequestRespawnEvent { + pub client: Entity, +} + +pub struct RequestStatsEvent { + pub client: Entity, +} + +fn handle_status( + mut packets: EventReader, + mut respawn_events: EventWriter, + mut request_stats_events: EventWriter, +) { + for packet in packets.iter() { + if let Some(pkt) = packet.decode::() { + match pkt { + ClientStatusC2s::PerformRespawn => respawn_events.send(RequestRespawnEvent { + client: packet.client, + }), + ClientStatusC2s::RequestStats => request_stats_events.send(RequestStatsEvent { + client: packet.client, + }), + } + } + } +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CLIENT_STATUS_C2S)] +pub enum ClientStatusC2s { + PerformRespawn, + RequestStats, +} diff --git a/crates/valence_client/src/teleport.rs b/crates/valence_client/src/teleport.rs index 003ab6e84..fb8a8cddd 100644 --- a/crates/valence_client/src/teleport.rs +++ b/crates/valence_client/src/teleport.rs @@ -1,4 +1,7 @@ -use valence_core::packet::c2s::play::TeleportConfirmC2s; +use bitfield_struct::bitfield; +use glam::DVec3; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; use super::*; use crate::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent}; @@ -79,7 +82,7 @@ fn teleport( yaw: if changed_yaw { look.yaw } else { 0.0 }, pitch: if changed_pitch { look.pitch } else { 0.0 }, flags, - teleport_id: VarInt(state.teleport_id_counter as i32), + teleport_id: (state.teleport_id_counter as i32).into(), }); state.pending_teleports = state.pending_teleports.wrapping_add(1); @@ -122,3 +125,31 @@ fn handle_teleport_confirmations( } } } + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::TELEPORT_CONFIRM_C2S)] +pub struct TeleportConfirmC2s { + pub teleport_id: VarInt, +} + +#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_POSITION_LOOK_S2C)] +pub struct PlayerPositionLookS2c { + pub position: DVec3, + pub yaw: f32, + pub pitch: f32, + pub flags: PlayerPositionLookFlags, + pub teleport_id: VarInt, +} + +#[bitfield(u8)] +#[derive(PartialEq, Eq, Encode, Decode)] +pub struct PlayerPositionLookFlags { + pub x: bool, + pub y: bool, + pub z: bool, + pub y_rot: bool, + pub x_rot: bool, + #[bits(3)] + _pad: u8, +} diff --git a/crates/valence_client/src/title.rs b/crates/valence_client/src/title.rs new file mode 100644 index 000000000..1510efacc --- /dev/null +++ b/crates/valence_client/src/title.rs @@ -0,0 +1,94 @@ +use valence_core::protocol::{packet_id, Decode, Encode}; + +use super::*; + +pub trait SetTitle { + /// Displays a title to a client. + /// + /// A title is a large piece of text displayed in the center of the screen + /// which may also include a subtitle underneath it. The title can be + /// configured to fade in and out using + /// [`set_title_times`](Self::set_title_times). + fn set_title(&mut self, text: impl Into); + + fn set_subtitle(&mut self, text: impl Into); + + fn set_action_bar(&mut self, text: impl Into); + + /// - `fade_in`: Ticks to spend fading in. + /// - `stay`: Ticks to keep the title displayed. + /// - `fade_out`: Ticks to spend fading out. + fn set_title_times(&mut self, fade_in: i32, stay: i32, fade_out: i32); + + fn clear_title(&mut self); + + fn reset_title(&mut self); +} + +impl SetTitle for T { + fn set_title(&mut self, text: impl Into) { + self.write_packet(&TitleS2c { + title_text: text.into().into(), + }); + } + + fn set_subtitle(&mut self, text: impl Into) { + self.write_packet(&SubtitleS2c { + subtitle_text: text.into().into(), + }); + } + + fn set_action_bar(&mut self, text: impl Into) { + self.write_packet(&OverlayMessageS2c { + action_bar_text: text.into().into(), + }); + } + + fn set_title_times(&mut self, fade_in: i32, stay: i32, fade_out: i32) { + self.write_packet(&TitleFadeS2c { + fade_in, + stay, + fade_out, + }); + } + + fn clear_title(&mut self) { + self.write_packet(&ClearTitleS2c { reset: false }); + } + + fn reset_title(&mut self) { + self.write_packet(&ClearTitleS2c { reset: true }); + } +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::TITLE_S2C)] +pub struct TitleS2c<'a> { + pub title_text: Cow<'a, Text>, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SUBTITLE_S2C)] +pub struct SubtitleS2c<'a> { + pub subtitle_text: Cow<'a, Text>, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::OVERLAY_MESSAGE_S2C)] +pub struct OverlayMessageS2c<'a> { + pub action_bar_text: Cow<'a, Text>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::TITLE_FADE_S2C)] +pub struct TitleFadeS2c { + pub fade_in: i32, + pub stay: i32, + pub fade_out: i32, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CLEAR_TITLE_S2C)] +pub struct ClearTitleS2c { + pub reset: bool, +} diff --git a/crates/valence_client/src/weather.rs b/crates/valence_client/src/weather.rs index c47d2ed03..045c7071b 100644 --- a/crates/valence_client/src/weather.rs +++ b/crates/valence_client/src/weather.rs @@ -15,10 +15,8 @@ //! New joined players are handled, so that they are get weather events from //! the instance. -use valence_core::packet::s2c::play::game_state_change::GameEventKind; -use valence_core::packet::s2c::play::GameStateChangeS2c; - use super::*; +use crate::packet::{GameEventKind, GameStateChangeS2c}; #[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)] struct UpdateWeatherPerInstanceSet; diff --git a/crates/valence_core/Cargo.toml b/crates/valence_core/Cargo.toml index 74a8baa39..4430321a6 100644 --- a/crates/valence_core/Cargo.toml +++ b/crates/valence_core/Cargo.toml @@ -24,7 +24,7 @@ serde_json.workspace = true thiserror.workspace = true tracing.workspace = true uuid = { workspace = true, features = ["serde"] } -valence_nbt.workspace = true +valence_nbt = { workspace = true, features = ["binary"] } valence_core_macros.workspace = true url.workspace = true base64.workspace = true diff --git a/crates/valence_core/build/item.rs b/crates/valence_core/build/item.rs index 84315f3f9..e8ebc1583 100644 --- a/crates/valence_core/build/item.rs +++ b/crates/valence_core/build/item.rs @@ -36,7 +36,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|item| { let id = &item.id; - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); quote! { #id => Some(Self::#name), @@ -48,7 +48,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|item| { let id = &item.id; - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); quote! { Self::#name => #id, @@ -60,7 +60,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|item| { let str_name = &item.name; - let name = ident(str_name.to_pascal_case()); + let name = ident(str_name.replace('.', "_").to_pascal_case()); quote! { #str_name => Some(Self::#name), } @@ -71,7 +71,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|item| { let str_name = &item.name; - let name = ident(str_name.to_pascal_case()); + let name = ident(str_name.replace('.', "_").to_pascal_case()); quote! { Self::#name => #str_name, } @@ -81,7 +81,7 @@ pub fn build() -> anyhow::Result { let item_kind_to_translation_key_arms = items .iter() .map(|item| { - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); let translation_key = &item.translation_key; quote! { Self::#name => #translation_key, @@ -91,13 +91,13 @@ pub fn build() -> anyhow::Result { let item_kind_variants = items .iter() - .map(|item| ident(item.name.to_pascal_case())) + .map(|item| ident(item.name.replace('.', "_").to_pascal_case())) .collect::>(); let item_kind_to_max_stack_arms = items .iter() .map(|item| { - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); let max_stack = item.max_stack; quote! { @@ -110,7 +110,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|item| match &item.food { Some(food_component) => { - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); let hunger = food_component.hunger; let saturation = food_component.saturation; let always_edible = food_component.always_edible; @@ -136,7 +136,7 @@ pub fn build() -> anyhow::Result { .iter() .filter(|item| item.max_durability != 0) .map(|item| { - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); let max_durability = item.max_durability; quote! { @@ -149,7 +149,7 @@ pub fn build() -> anyhow::Result { .iter() .filter(|item| item.enchantability != 0) .map(|item| { - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); let ench = item.enchantability; quote! { @@ -162,7 +162,7 @@ pub fn build() -> anyhow::Result { .iter() .filter(|item| item.fireproof) .map(|item| { - let name = ident(item.name.to_pascal_case()); + let name = ident(item.name.replace('.', "_").to_pascal_case()); quote! { Self::#name => true, diff --git a/crates/valence_core/build/packet_id.rs b/crates/valence_core/build/packet_id.rs index 31168c840..bcbd6f6b1 100644 --- a/crates/valence_core/build/packet_id.rs +++ b/crates/valence_core/build/packet_id.rs @@ -1,4 +1,4 @@ -use heck::ToPascalCase; +use heck::ToShoutySnakeCase; use proc_macro2::TokenStream; use quote::quote; use serde::Deserialize; @@ -21,15 +21,14 @@ pub fn build() -> anyhow::Result { for packet in packets { let stripped_name = packet.name.strip_suffix("Packet").unwrap_or(&packet.name); - let name_ident = ident(stripped_name.to_pascal_case()); + let name_ident = ident(stripped_name.to_shouty_snake_case()); let id = packet.id; - let doc = format!("Side: {}\nState: {}", packet.side, packet.state); + let doc = format!("Side: {}\n\nState: {}", packet.side, packet.state); consts.extend([quote! { #[doc = #doc] - #[allow(non_upper_case_globals)] - pub(crate) const #name_ident: i32 = #id; + pub const #name_ident: i32 = #id; }]); } diff --git a/crates/valence_core/build/sound.rs b/crates/valence_core/build/sound.rs index ac307896c..e4e260e92 100644 --- a/crates/valence_core/build/sound.rs +++ b/crates/valence_core/build/sound.rs @@ -20,7 +20,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|sound| { let id = &sound.id; - let name = ident(sound.name.to_pascal_case()); + let name = ident(sound.name.replace('.', "_").to_pascal_case()); quote! { #id => Some(Self::#name), @@ -32,7 +32,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|sound| { let id = &sound.id; - let name = ident(sound.name.to_pascal_case()); + let name = ident(sound.name.replace('.', "_").to_pascal_case()); quote! { Self::#name => #id, @@ -44,7 +44,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|sound| { let str_name = &sound.name; - let name = ident(str_name.to_pascal_case()); + let name = ident(str_name.replace('.', "_").to_pascal_case()); quote! { #str_name => Some(Self::#name), } @@ -55,7 +55,7 @@ pub fn build() -> anyhow::Result { .iter() .map(|sound| { let str_name = &sound.name; - let name = ident(str_name.to_pascal_case()); + let name = ident(str_name.replace('.', "_").to_pascal_case()); quote! { Self::#name => ident!(#str_name), } @@ -64,7 +64,7 @@ pub fn build() -> anyhow::Result { let sound_variants = sounds .iter() - .map(|sound| ident(sound.name.to_pascal_case())) + .map(|sound| ident(sound.name.replace('.', "_").to_pascal_case())) .collect::>(); Ok(quote! { diff --git a/crates/valence_core/build/translation_key.rs b/crates/valence_core/build/translation_key.rs index ee7c999ea..d0f4e36c9 100644 --- a/crates/valence_core/build/translation_key.rs +++ b/crates/valence_core/build/translation_key.rs @@ -24,7 +24,7 @@ pub fn build() -> anyhow::Result { let translation_key_consts = translations .iter() .map(|translation| { - let const_id = ident(translation.key.to_shouty_snake_case()); + let const_id = ident(translation.key.replace('.', "_").to_shouty_snake_case()); let key = &translation.key; let english_translation = &translation.english_translation; let doc = format!("\"{}\"", escape(english_translation)); diff --git a/crates/valence_core/src/aabb.rs b/crates/valence_core/src/aabb.rs index 80413d2b9..3c6949d91 100644 --- a/crates/valence_core/src/aabb.rs +++ b/crates/valence_core/src/aabb.rs @@ -1,3 +1,5 @@ +use std::ops::Add; + use glam::DVec3; /// An axis-aligned bounding box. `min` is expected to be <= `max` @@ -35,4 +37,32 @@ impl Aabb { }, } } + + pub fn intersects(&self, second: Aabb) -> bool { + self.max.x >= second.min.x + && second.max.x >= self.min.x + && self.max.y >= second.min.y + && second.max.y >= self.min.y + && self.max.z >= second.min.z + && second.max.z >= self.min.z + } +} + +impl Add for Aabb { + type Output = Aabb; + + fn add(self, rhs: DVec3) -> Self::Output { + Self { + min: self.min + rhs, + max: self.max + rhs, + } + } +} + +impl Add for DVec3 { + type Output = Aabb; + + fn add(self, rhs: Aabb) -> Self::Output { + rhs + self + } } diff --git a/crates/valence_core/src/block_pos.rs b/crates/valence_core/src/block_pos.rs index 01fc23612..32c5694ea 100644 --- a/crates/valence_core/src/block_pos.rs +++ b/crates/valence_core/src/block_pos.rs @@ -3,7 +3,7 @@ use std::io::Write; use anyhow::bail; use crate::direction::Direction; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; /// Represents an absolute block position in world space. #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Debug)] diff --git a/crates/valence_core/src/chunk_pos.rs b/crates/valence_core/src/chunk_pos.rs index d4a499435..2f804bfe0 100644 --- a/crates/valence_core/src/chunk_pos.rs +++ b/crates/valence_core/src/chunk_pos.rs @@ -1,7 +1,7 @@ use glam::DVec3; use crate::block_pos::BlockPos; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; /// The X and Z position of a chunk. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash, Debug, Encode, Decode)] diff --git a/crates/valence_core/src/difficulty.rs b/crates/valence_core/src/difficulty.rs index 4283027ed..b3d3d7c95 100644 --- a/crates/valence_core/src/difficulty.rs +++ b/crates/valence_core/src/difficulty.rs @@ -1,4 +1,4 @@ -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] pub enum Difficulty { diff --git a/crates/valence_core/src/direction.rs b/crates/valence_core/src/direction.rs index a8f64fdf0..8562d8fed 100644 --- a/crates/valence_core/src/direction.rs +++ b/crates/valence_core/src/direction.rs @@ -1,6 +1,6 @@ use bevy_ecs::prelude::*; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Component)] pub enum Direction { diff --git a/crates/valence_core/src/game_mode.rs b/crates/valence_core/src/game_mode.rs index 715640be3..c23c2a6a8 100644 --- a/crates/valence_core/src/game_mode.rs +++ b/crates/valence_core/src/game_mode.rs @@ -1,6 +1,6 @@ use bevy_ecs::prelude::*; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; #[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Encode, Decode, Component)] pub enum GameMode { diff --git a/crates/valence_core/src/hand.rs b/crates/valence_core/src/hand.rs index dc20640ce..957a01cf5 100644 --- a/crates/valence_core/src/hand.rs +++ b/crates/valence_core/src/hand.rs @@ -1,4 +1,4 @@ -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; #[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Encode, Decode)] pub enum Hand { diff --git a/crates/valence_core/src/ident.rs b/crates/valence_core/src/ident.rs index 66df6a418..a5fac9244 100644 --- a/crates/valence_core/src/ident.rs +++ b/crates/valence_core/src/ident.rs @@ -11,7 +11,7 @@ use serde::de::Error as _; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use thiserror::Error; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; #[doc(hidden)] pub mod __private { @@ -132,6 +132,12 @@ impl Ident { } } +impl<'a> Ident> { + pub fn borrowed(&self) -> Ident> { + Ident::new_unchecked(Cow::Borrowed(self.as_str())) + } +} + fn parse(string: Cow) -> Result>, IdentError> { let check_namespace = |s: &str| { !s.is_empty() diff --git a/crates/valence_core/src/item.rs b/crates/valence_core/src/item.rs index e97545408..9d3814a28 100644 --- a/crates/valence_core/src/item.rs +++ b/crates/valence_core/src/item.rs @@ -3,8 +3,8 @@ use std::io::Write; use anyhow::{ensure, Context}; use valence_nbt::Compound; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; +use crate::protocol::var_int::VarInt; +use crate::protocol::{Decode, Encode}; include!(concat!(env!("OUT_DIR"), "/item.rs")); diff --git a/crates/valence_core/src/lib.rs b/crates/valence_core/src/lib.rs index bf5fc1c79..85e3ff92b 100644 --- a/crates/valence_core/src/lib.rs +++ b/crates/valence_core/src/lib.rs @@ -28,11 +28,11 @@ pub mod game_mode; pub mod hand; pub mod ident; pub mod item; -pub mod packet; +pub mod particle; pub mod player_textures; pub mod property; +pub mod protocol; pub mod scratch; -pub mod sound; pub mod text; pub mod translation_key; pub mod uuid; @@ -51,8 +51,8 @@ use crate::despawn::despawn_marked_entities; pub mod __private { pub use anyhow::{anyhow, bail, ensure, Context, Result}; - pub use crate::packet::var_int::VarInt; - pub use crate::packet::{Decode, Encode, Packet}; + pub use crate::protocol::var_int::VarInt; + pub use crate::protocol::{Decode, Encode, Packet}; } // Needed to make proc macros work. @@ -142,7 +142,7 @@ impl Default for CoreSettings { } /// Contains global server state accessible as a [`Resource`]. -#[derive(Resource)] +#[derive(Resource, Default)] pub struct Server { /// Incremented on every tick. current_tick: i64, diff --git a/crates/valence_core/src/packet.rs b/crates/valence_core/src/packet.rs deleted file mode 100644 index 16d22beaf..000000000 --- a/crates/valence_core/src/packet.rs +++ /dev/null @@ -1,654 +0,0 @@ -//! Types and functions used in Minecraft's packets. Structs for each packet are -//! defined here too. -//! -//! Client-to-server packets live in [`c2s`] while server-to-client packets are -//! in [`s2c`]. - -pub mod array; -pub mod byte_angle; -pub mod decode; -pub mod encode; -pub mod global_pos; -pub mod impls; -pub mod message_signature; -pub mod raw; -pub mod var_int; -pub mod var_long; - -use std::io::Write; - -pub use valence_core_macros::{Decode, Encode, Packet}; - -/// The maximum number of bytes in a single Minecraft packet. -pub const MAX_PACKET_SIZE: i32 = 2097152; - -/// The `Encode` trait allows objects to be written to the Minecraft protocol. -/// It is the inverse of [`Decode`]. -/// -/// # Deriving -/// -/// This trait can be implemented automatically for structs and enums by using -/// the [`Encode`][macro] derive macro. All components of the type must -/// implement `Encode`. Components are encoded in the order they appear in the -/// type definition. -/// -/// For enums, the variant to encode is marked by a leading [`VarInt`] -/// discriminant (tag). The discriminant value can be changed using the `#[tag = -/// ...]` attribute on the variant in question. Discriminant values are assigned -/// to variants using rules similar to regular enum discriminants. -/// -/// ``` -/// use valence_core::packet::Encode; -/// -/// #[derive(Encode)] -/// struct MyStruct<'a> { -/// first: i32, -/// second: &'a str, -/// third: [f64; 3], -/// } -/// -/// #[derive(Encode)] -/// enum MyEnum { -/// First, // tag = 0 -/// Second, // tag = 1 -/// #[tag = 25] -/// Third, // tag = 25 -/// Fourth, // tag = 26 -/// } -/// -/// let value = MyStruct { -/// first: 10, -/// second: "hello", -/// third: [1.5, 3.14, 2.718], -/// }; -/// -/// let mut buf = vec![]; -/// value.encode(&mut buf).unwrap(); -/// -/// println!("{buf:?}"); -/// ``` -/// -/// [macro]: valence_core_macros::Encode -/// [`VarInt`]: var_int::VarInt -pub trait Encode { - /// Writes this object to the provided writer. - /// - /// If this type also implements [`Decode`] then successful calls to this - /// function returning `Ok(())` must always successfully [`decode`] using - /// the data that was written to the writer. The exact number of bytes - /// that were originally written must be consumed during the decoding. - /// - /// [`decode`]: Decode::decode - fn encode(&self, w: impl Write) -> anyhow::Result<()>; - - /// Like [`Encode::encode`], except that a whole slice of values is encoded. - /// - /// This method must be semantically equivalent to encoding every element of - /// the slice in sequence with no leading length prefix (which is exactly - /// what the default implementation does), but a more efficient - /// implementation may be used. - /// - /// This optimization is very important for some types like `u8` where - /// [`write_all`] is used. Because impl specialization is unavailable in - /// stable Rust, we must make the slice specialization part of this trait. - /// - /// [`write_all`]: Write::write_all - fn encode_slice(slice: &[Self], mut w: impl Write) -> anyhow::Result<()> - where - Self: Sized, - { - for value in slice { - value.encode(&mut w)?; - } - - Ok(()) - } -} - -/// The `Decode` trait allows objects to be read from the Minecraft protocol. It -/// is the inverse of [`Encode`]. -/// -/// `Decode` is parameterized by a lifetime. This allows the decoded value to -/// borrow data from the byte slice it was read from. -/// -/// # Deriving -/// -/// This trait can be implemented automatically for structs and enums by using -/// the [`Decode`][macro] derive macro. All components of the type must -/// implement `Decode`. Components are decoded in the order they appear in the -/// type definition. -/// -/// For enums, the variant to decode is determined by a leading [`VarInt`] -/// discriminant (tag). The discriminant value can be changed using the `#[tag = -/// ...]` attribute on the variant in question. Discriminant values are assigned -/// to variants using rules similar to regular enum discriminants. -/// -/// ``` -/// use valence_core::packet::Decode; -/// -/// #[derive(PartialEq, Debug, Decode)] -/// struct MyStruct { -/// first: i32, -/// second: MyEnum, -/// } -/// -/// #[derive(PartialEq, Debug, Decode)] -/// enum MyEnum { -/// First, // tag = 0 -/// Second, // tag = 1 -/// #[tag = 25] -/// Third, // tag = 25 -/// Fourth, // tag = 26 -/// } -/// -/// let mut r: &[u8] = &[0, 0, 0, 0, 26]; -/// -/// let value = MyStruct::decode(&mut r).unwrap(); -/// let expected = MyStruct { -/// first: 0, -/// second: MyEnum::Fourth, -/// }; -/// -/// assert_eq!(value, expected); -/// assert!(r.is_empty()); -/// ``` -/// -/// [macro]: valence_core_macros::Decode -/// [`VarInt`]: var_int::VarInt -pub trait Decode<'a>: Sized { - /// Reads this object from the provided byte slice. - /// - /// Implementations of `Decode` are expected to shrink the slice from the - /// front as bytes are read. - fn decode(r: &mut &'a [u8]) -> anyhow::Result; -} - -/// Like [`Encode`] + [`Decode`], but implementations must read and write a -/// leading [`VarInt`] packet ID before any other data. -/// -/// # Deriving -/// -/// This trait can be implemented automatically by using the -/// [`Packet`][macro] derive macro. The trait is implemented by reading or -/// writing the packet ID provided in the `#[packet_id = ...]` helper attribute -/// followed by a call to [`Encode::encode`] or [`Decode::decode`]. The target -/// type must implement [`Encode`], [`Decode`], and [`std::fmt::Debug`]. -/// -/// ``` -/// use valence_core::packet::{Decode, Encode, Packet}; -/// -/// #[derive(Encode, Decode, Packet, Debug)] -/// #[packet_id = 42] -/// struct MyStruct { -/// first: i32, -/// } -/// -/// let value = MyStruct { first: 123 }; -/// let mut buf = vec![]; -/// -/// value.encode_packet(&mut buf).unwrap(); -/// println!("{buf:?}"); -/// ``` -/// -/// [macro]: valence_core_macros::Packet -/// [`VarInt`]: var_int::VarInt -pub trait Packet<'a>: Sized + std::fmt::Debug { - /// The packet returned by [`Self::packet_id`]. If the packet ID is not - /// statically known, then a negative value is used instead. - const PACKET_ID: i32 = -1; - /// Returns the ID of this packet. - fn packet_id(&self) -> i32; - /// Returns the name of this packet, typically without whitespace or - /// additional formatting. - fn packet_name(&self) -> &str; - /// Like [`Encode::encode`], but a leading [`VarInt`] packet ID must be - /// written by this method first. - /// - /// [`VarInt`]: var_int::VarInt - fn encode_packet(&self, w: impl Write) -> anyhow::Result<()>; - /// Like [`Decode::decode`], but a leading [`VarInt`] packet ID must be read - /// by this method first. - /// - /// [`VarInt`]: var_int::VarInt - fn decode_packet(r: &mut &'a [u8]) -> anyhow::Result; -} - -/// Defines an enum of packets and implements [`Packet`] for each. -macro_rules! packet_group { - ( - $(#[$attrs:meta])* - $enum_name:ident<$enum_life:lifetime> { - $($packet:ident $(<$life:lifetime>)?),* $(,)? - } - ) => { - $(#[$attrs])* - pub enum $enum_name<$enum_life> { - $( - $packet($packet $(<$life>)?), - )* - } - - $( - impl<$enum_life> From<$packet $(<$life>)?> for $enum_name<$enum_life> { - fn from(p: $packet $(<$life>)?) -> Self { - Self::$packet(p) - } - } - - impl<$enum_life> $crate::packet::Packet<$enum_life> for $packet$(<$life>)? { - const PACKET_ID: i32 = $crate::packet::id::$packet; - - fn packet_id(&self) -> i32 { - Self::PACKET_ID - } - - fn packet_name(&self) -> &str { - stringify!($packet) - } - - #[allow(unused_imports)] - fn encode_packet(&self, mut w: impl std::io::Write) -> $crate::__private::Result<()> { - use $crate::__private::*; - - VarInt(Self::PACKET_ID) - .encode(&mut w) - .context("failed to encode packet ID")?; - - self.encode(w) - } - - #[allow(unused_imports)] - fn decode_packet(r: &mut &$enum_life [u8]) -> $crate::__private::Result { - use $crate::__private::*; - - let id = VarInt::decode(r).context("failed to decode packet ID")?.0; - ensure!(id == Self::PACKET_ID, "unexpected packet ID {} (expected {})", id, Self::PACKET_ID); - - Self::decode(r) - } - } - )* - - impl<$enum_life> $crate::packet::Packet<$enum_life> for $enum_name<$enum_life> { - fn packet_id(&self) -> i32 { - match self { - $( - Self::$packet(_) => <$packet as $crate::packet::Packet>::PACKET_ID, - )* - } - } - - fn packet_name(&self) -> &str { - match self { - $( - Self::$packet(pkt) => pkt.packet_name(), - )* - } - } - - fn encode_packet(&self, mut w: impl std::io::Write) -> $crate::__private::Result<()> { - use $crate::__private::*; - - match self { - $( - Self::$packet(pkt) => { - VarInt(<$packet as Packet>::PACKET_ID).encode(&mut w)?; - pkt.encode(w)?; - } - )* - } - - Ok(()) - } - - fn decode_packet(r: &mut &$enum_life [u8]) -> $crate::__private::Result { - use $crate::__private::*; - - let id = VarInt::decode(r)?.0; - Ok(match id { - $( - <$packet as Packet>::PACKET_ID => - Self::$packet($packet::decode(r)?), - )* - id => bail!("unknown packet ID {} while decoding {}", id, stringify!($enum_name)), - }) - } - } - - impl<$enum_life> std::fmt::Debug for $enum_name<$enum_life> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - $( - Self::$packet(pkt) => pkt.fmt(f), - )* - } - } - } - }; - // No lifetime on the enum in this case. - ( - $(#[$attrs:meta])* - $enum_name:ident { - $($packet:ident),* $(,)? - } - ) => { - $(#[$attrs])* - pub enum $enum_name { - $( - $packet($packet), - )* - } - - $( - impl From<$packet> for $enum_name { - fn from(p: $packet) -> Self { - Self::$packet(p) - } - } - - impl $crate::__private::Packet<'_> for $packet { - const PACKET_ID: i32 = $crate::packet::id::$packet; - - fn packet_id(&self) -> i32 { - Self::PACKET_ID - } - - fn packet_name(&self) -> &str { - stringify!($packet) - } - - #[allow(unused_imports)] - fn encode_packet(&self, mut w: impl std::io::Write) -> $crate::__private::Result<()> { - use $crate::__private::*; - - VarInt(Self::PACKET_ID) - .encode(&mut w) - .context("failed to encode packet ID")?; - - self.encode(w) - } - - #[allow(unused_imports)] - fn decode_packet(r: &mut &[u8]) -> $crate::__private::Result { - use $crate::__private::*; - - let id = VarInt::decode(r).context("failed to decode packet ID")?.0; - ensure!(id == Self::PACKET_ID, "unexpected packet ID {} (expected {})", id, Self::PACKET_ID); - - Self::decode(r) - } - } - )* - - impl $crate::__private::Packet<'_> for $enum_name { - fn packet_id(&self) -> i32 { - use $crate::__private::*; - - match self { - $( - Self::$packet(_) => <$packet as Packet>::PACKET_ID, - )* - } - } - - fn packet_name(&self) -> &str { - match self { - $( - Self::$packet(pkt) => pkt.packet_name(), - )* - } - } - - fn encode_packet(&self, mut w: impl std::io::Write) -> $crate::__private::Result<()> { - use $crate::__private::*; - - match self { - $( - Self::$packet(pkt) => { - VarInt(<$packet as Packet>::PACKET_ID).encode(&mut w)?; - pkt.encode(w)?; - } - )* - } - - Ok(()) - } - - fn decode_packet(r: &mut &[u8]) -> $crate::__private::Result { - use $crate::__private::*; - - let id = VarInt::decode(r)?.0; - Ok(match id { - $( - <$packet as Packet>::PACKET_ID => - Self::$packet($packet::decode(r)?), - )* - id => anyhow::bail!("unknown packet ID {} while decoding {}", id, stringify!($enum_name)), - }) - } - } - - impl std::fmt::Debug for $enum_name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - $( - Self::$packet(pkt) => pkt.fmt(f), - )* - } - } - } - } -} - -pub mod c2s; -pub mod s2c; - -/// Contains the packet ID for every packet. Because the constants are private -/// to the crate, the compiler will yell at us when we forget to use one. -mod id { - include!(concat!(env!("OUT_DIR"), "/packet_id.rs")); -} - -#[allow(dead_code)] -#[cfg(test)] -mod tests { - use std::borrow::Cow; - - use bytes::BytesMut; - - use super::*; - use crate::packet::c2s::play::{C2sPlayPacket, HandSwingC2s}; - use crate::packet::decode::{decode_packet, PacketDecoder}; - use crate::packet::encode::PacketEncoder; - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 1] - struct RegularStruct { - foo: i32, - bar: bool, - baz: f64, - } - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 2] - struct UnitStruct; - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 3] - struct EmptyStruct {} - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 4] - struct TupleStruct(i32, bool, f64); - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 5] - struct StructWithGenerics<'z, T = ()> { - foo: &'z str, - bar: T, - } - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 6] - struct TupleStructWithGenerics<'z, T = ()>(&'z str, i32, T); - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 7] - enum RegularEnum { - Empty, - Tuple(i32, bool, f64), - Fields { foo: i32, bar: bool, baz: f64 }, - } - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 8] - enum EmptyEnum {} - - #[derive(Encode, Decode, Packet, Debug)] - #[packet_id = 0xbeef] - enum EnumWithGenericsAndTags<'z, T = ()> { - #[tag = 5] - First { - foo: &'z str, - }, - Second(&'z str), - #[tag = 0xff] - Third, - #[tag = 0] - Fourth(T), - } - - #[allow(unconditional_recursion, clippy::extra_unused_type_parameters)] - fn assert_has_impls<'a, T>() - where - T: Encode + Decode<'a> + Packet<'a>, - { - assert_has_impls::(); - assert_has_impls::(); - assert_has_impls::(); - assert_has_impls::(); - assert_has_impls::(); - assert_has_impls::(); - assert_has_impls::(); - assert_has_impls::(); - assert_has_impls::(); - } - - #[test] - fn packet_name() { - assert_eq!(UnitStruct.packet_name(), "UnitStruct"); - assert_eq!(RegularEnum::Empty.packet_name(), "RegularEnum"); - assert_eq!( - StructWithGenerics { - foo: "blah", - bar: () - } - .packet_name(), - "StructWithGenerics" - ); - assert_eq!( - C2sPlayPacket::HandSwingC2s(HandSwingC2s { - hand: Default::default() - }) - .packet_name(), - "HandSwingC2s" - ); - } - - use crate::block_pos::BlockPos; - use crate::hand::Hand; - use crate::ident::Ident; - use crate::item::{ItemKind, ItemStack}; - use crate::packet::var_int::VarInt; - use crate::packet::var_long::VarLong; - use crate::text::{Text, TextFormat}; - - #[cfg(feature = "encryption")] - const CRYPT_KEY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - - #[derive(PartialEq, Debug, Encode, Decode, Packet)] - #[packet_id = 42] - struct TestPacket<'a> { - a: bool, - b: u8, - c: i32, - d: f32, - e: f64, - f: BlockPos, - g: Hand, - h: Ident>, - i: Option, - j: Text, - k: VarInt, - l: VarLong, - m: &'a str, - n: &'a [u8; 10], - o: [u128; 3], - } - - impl<'a> TestPacket<'a> { - fn new(string: &'a str) -> Self { - Self { - a: true, - b: 12, - c: -999, - d: 5.001, - e: 1e10, - f: BlockPos::new(1, 2, 3), - g: Hand::Off, - h: Ident::new("minecraft:whatever").unwrap(), - i: Some(ItemStack::new(ItemKind::WoodenSword, 12, None)), - j: "my ".into_text() + "fancy".italic() + " text", - k: VarInt(123), - l: VarLong(456), - m: string, - n: &[7; 10], - o: [123456789; 3], - } - } - } - - fn check_test_packet(dec: &mut PacketDecoder, string: &str) { - let frame = dec.try_next_packet().unwrap().unwrap(); - - let pkt = decode_packet::(&frame).unwrap(); - - assert_eq!(&pkt, &TestPacket::new(string)); - } - - #[test] - fn packets_round_trip() { - let mut buf = BytesMut::new(); - - let mut enc = PacketEncoder::new(); - - enc.append_packet(&TestPacket::new("first")).unwrap(); - #[cfg(feature = "compression")] - enc.set_compression(Some(0)); - enc.append_packet(&TestPacket::new("second")).unwrap(); - buf.unsplit(enc.take()); - #[cfg(feature = "encryption")] - enc.enable_encryption(&CRYPT_KEY); - enc.append_packet(&TestPacket::new("third")).unwrap(); - enc.prepend_packet(&TestPacket::new("fourth")).unwrap(); - - buf.unsplit(enc.take()); - - let mut dec = PacketDecoder::new(); - - dec.queue_bytes(buf); - - check_test_packet(&mut dec, "first"); - - #[cfg(feature = "compression")] - dec.set_compression(Some(0)); - - check_test_packet(&mut dec, "second"); - - #[cfg(feature = "encryption")] - dec.enable_encryption(&CRYPT_KEY); - - check_test_packet(&mut dec, "fourth"); - check_test_packet(&mut dec, "third"); - } -} diff --git a/crates/valence_core/src/packet/c2s.rs b/crates/valence_core/src/packet/c2s.rs deleted file mode 100644 index ec7c0d40d..000000000 --- a/crates/valence_core/src/packet/c2s.rs +++ /dev/null @@ -1,205 +0,0 @@ -pub mod handshake { - pub use handshake::HandshakeC2s; - - #[allow(clippy::module_inception)] - pub mod handshake; - - packet_group! { - #[derive(Clone)] - C2sHandshakePacket<'a> { - HandshakeC2s<'a> - } - } -} - -pub mod status { - pub use query_ping::QueryPingC2s; - pub use query_request::QueryRequestC2s; - - pub mod query_ping; - pub mod query_request; - - packet_group! { - #[derive(Clone)] - C2sStatusPacket { - QueryPingC2s, - QueryRequestC2s, - } - } -} - -pub mod login { - pub use login_hello::LoginHelloC2s; - pub use login_key::LoginKeyC2s; - pub use login_query_response::LoginQueryResponseC2s; - - pub mod login_hello; - pub mod login_key; - pub mod login_query_response; - - packet_group! { - #[derive(Clone)] - C2sLoginPacket<'a> { - LoginHelloC2s<'a>, - LoginKeyC2s<'a>, - LoginQueryResponseC2s<'a>, - } - } -} - -pub mod play { - pub use advancement_tab::AdvancementTabC2s; - pub use boat_paddle::BoatPaddleStateC2s; - pub use book_update::BookUpdateC2s; - pub use button_click::ButtonClickC2s; - pub use chat_message::ChatMessageC2s; - pub use click_slot::ClickSlotC2s; - pub use client_command::ClientCommandC2s; - pub use client_settings::ClientSettingsC2s; - pub use client_status::ClientStatusC2s; - pub use close_handled_screen::CloseHandledScreenC2s; - pub use command_execution::CommandExecutionC2s; - pub use craft_request::CraftRequestC2s; - pub use creative_inventory_action::CreativeInventoryActionC2s; - pub use custom_payload::CustomPayloadC2s; - pub use hand_swing::HandSwingC2s; - pub use jigsaw_generating::JigsawGeneratingC2s; - pub use keep_alive::KeepAliveC2s; - pub use message_acknowledgment::MessageAcknowledgmentC2s; - pub use pick_from_inventory::PickFromInventoryC2s; - pub use play_pong::PlayPongC2s; - pub use player_action::PlayerActionC2s; - pub use player_input::PlayerInputC2s; - pub use player_interact_block::PlayerInteractBlockC2s; - pub use player_interact_entity::PlayerInteractEntityC2s; - pub use player_interact_item::PlayerInteractItemC2s; - pub use player_move::{Full, LookAndOnGround, OnGroundOnly, PositionAndOnGround}; - pub use player_session::PlayerSessionC2s; - pub use query_block_nbt::QueryBlockNbtC2s; - pub use query_entity_nbt::QueryEntityNbtC2s; - pub use recipe_book_data::RecipeBookDataC2s; - pub use recipe_category_options::RecipeCategoryOptionsC2s; - pub use rename_item::RenameItemC2s; - pub use request_command_completions::RequestCommandCompletionsC2s; - pub use resource_pack_status::ResourcePackStatusC2s; - pub use select_merchant_trade::SelectMerchantTradeC2s; - pub use spectator_teleport::SpectatorTeleportC2s; - pub use teleport_confirm::TeleportConfirmC2s; - pub use update_beacon::UpdateBeaconC2s; - pub use update_command_block::UpdateCommandBlockC2s; - pub use update_command_block_minecart::UpdateCommandBlockMinecartC2s; - pub use update_difficulty::UpdateDifficultyC2s; - pub use update_difficulty_lock::UpdateDifficultyLockC2s; - pub use update_jigsaw::UpdateJigsawC2s; - pub use update_player_abilities::UpdatePlayerAbilitiesC2s; - pub use update_selected_slot::UpdateSelectedSlotC2s; - pub use update_sign::UpdateSignC2s; - pub use update_structure_block::UpdateStructureBlockC2s; - pub use vehicle_move::VehicleMoveC2s; - - pub mod advancement_tab; - pub mod boat_paddle; - pub mod book_update; - pub mod button_click; - pub mod chat_message; - pub mod click_slot; - pub mod client_command; - pub mod client_settings; - pub mod client_status; - pub mod close_handled_screen; - pub mod command_execution; - pub mod craft_request; - pub mod creative_inventory_action; - pub mod custom_payload; - pub mod hand_swing; - pub mod jigsaw_generating; - pub mod keep_alive; - pub mod message_acknowledgment; - pub mod pick_from_inventory; - pub mod play_pong; - pub mod player_action; - pub mod player_input; - pub mod player_interact_block; - pub mod player_interact_entity; - pub mod player_interact_item; - pub mod player_move; - pub mod player_session; - pub mod query_block_nbt; - pub mod query_entity_nbt; - pub mod recipe_book_data; - pub mod recipe_category_options; - pub mod rename_item; - pub mod request_command_completions; - pub mod resource_pack_status; - pub mod select_merchant_trade; - pub mod spectator_teleport; - pub mod teleport_confirm; - pub mod update_beacon; - pub mod update_command_block; - pub mod update_command_block_minecart; - pub mod update_difficulty; - pub mod update_difficulty_lock; - pub mod update_jigsaw; - pub mod update_player_abilities; - pub mod update_selected_slot; - pub mod update_sign; - pub mod update_structure_block; - pub mod vehicle_move; - - packet_group! { - #[derive(Clone)] - C2sPlayPacket<'a> { - AdvancementTabC2s<'a>, - BoatPaddleStateC2s, - BookUpdateC2s<'a>, - ButtonClickC2s, - ChatMessageC2s<'a>, - ClickSlotC2s, - ClientCommandC2s, - ClientSettingsC2s<'a>, - ClientStatusC2s, - CloseHandledScreenC2s, - CommandExecutionC2s<'a>, - CraftRequestC2s<'a>, - CreativeInventoryActionC2s, - CustomPayloadC2s<'a>, - Full, - HandSwingC2s, - JigsawGeneratingC2s, - KeepAliveC2s, - LookAndOnGround, - MessageAcknowledgmentC2s, - OnGroundOnly, - PickFromInventoryC2s, - PlayerActionC2s, - PlayerInputC2s, - PlayerInteractBlockC2s, - PlayerInteractEntityC2s, - PlayerInteractItemC2s, - PlayerSessionC2s<'a>, - PlayPongC2s, - PositionAndOnGround, - QueryBlockNbtC2s, - QueryEntityNbtC2s, - RecipeBookDataC2s<'a>, - RecipeCategoryOptionsC2s, - RenameItemC2s<'a>, - RequestCommandCompletionsC2s<'a>, - ResourcePackStatusC2s, - SelectMerchantTradeC2s, - SpectatorTeleportC2s, - TeleportConfirmC2s, - UpdateBeaconC2s, - UpdateCommandBlockC2s<'a>, - UpdateCommandBlockMinecartC2s<'a>, - UpdateDifficultyC2s, - UpdateDifficultyLockC2s, - UpdateJigsawC2s<'a>, - UpdatePlayerAbilitiesC2s, - UpdateSelectedSlotC2s, - UpdateSignC2s<'a>, - UpdateStructureBlockC2s<'a>, - VehicleMoveC2s, - } - } -} diff --git a/crates/valence_core/src/packet/c2s/handshake/handshake.rs b/crates/valence_core/src/packet/c2s/handshake/handshake.rs deleted file mode 100644 index 6b14d9736..000000000 --- a/crates/valence_core/src/packet/c2s/handshake/handshake.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct HandshakeC2s<'a> { - pub protocol_version: VarInt, - pub server_address: &'a str, - pub server_port: u16, - pub next_state: NextState, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Encode, Decode)] -pub enum NextState { - #[tag = 1] - Status, - #[tag = 2] - Login, -} diff --git a/crates/valence_core/src/packet/c2s/login/login_hello.rs b/crates/valence_core/src/packet/c2s/login/login_hello.rs deleted file mode 100644 index ff5046bf4..000000000 --- a/crates/valence_core/src/packet/c2s/login/login_hello.rs +++ /dev/null @@ -1,9 +0,0 @@ -use uuid::Uuid; - -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct LoginHelloC2s<'a> { - pub username: &'a str, // TODO: bound this - pub profile_id: Option, -} diff --git a/crates/valence_core/src/packet/c2s/login/login_key.rs b/crates/valence_core/src/packet/c2s/login/login_key.rs deleted file mode 100644 index d89b7fa48..000000000 --- a/crates/valence_core/src/packet/c2s/login/login_key.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct LoginKeyC2s<'a> { - pub shared_secret: &'a [u8], - pub verify_token: &'a [u8], -} diff --git a/crates/valence_core/src/packet/c2s/login/login_query_response.rs b/crates/valence_core/src/packet/c2s/login/login_query_response.rs deleted file mode 100644 index a20254338..000000000 --- a/crates/valence_core/src/packet/c2s/login/login_query_response.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::raw::RawBytes; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct LoginQueryResponseC2s<'a> { - pub message_id: VarInt, - pub data: Option>, -} diff --git a/crates/valence_core/src/packet/c2s/play/advancement_tab.rs b/crates/valence_core/src/packet/c2s/play/advancement_tab.rs deleted file mode 100644 index b7ffadc28..000000000 --- a/crates/valence_core/src/packet/c2s/play/advancement_tab.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub enum AdvancementTabC2s<'a> { - OpenedTab { tab_id: Ident> }, - ClosedScreen, -} diff --git a/crates/valence_core/src/packet/c2s/play/boat_paddle.rs b/crates/valence_core/src/packet/c2s/play/boat_paddle.rs deleted file mode 100644 index edbefeffd..000000000 --- a/crates/valence_core/src/packet/c2s/play/boat_paddle.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct BoatPaddleStateC2s { - pub left_paddle_turning: bool, - pub right_paddle_turning: bool, -} diff --git a/crates/valence_core/src/packet/c2s/play/book_update.rs b/crates/valence_core/src/packet/c2s/play/book_update.rs deleted file mode 100644 index bdd54cc1a..000000000 --- a/crates/valence_core/src/packet/c2s/play/book_update.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct BookUpdateC2s<'a> { - pub slot: VarInt, - pub entries: Vec<&'a str>, - pub title: Option<&'a str>, -} diff --git a/crates/valence_core/src/packet/c2s/play/button_click.rs b/crates/valence_core/src/packet/c2s/play/button_click.rs deleted file mode 100644 index 83a537be9..000000000 --- a/crates/valence_core/src/packet/c2s/play/button_click.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ButtonClickC2s { - pub window_id: i8, - pub button_id: i8, -} diff --git a/crates/valence_core/src/packet/c2s/play/chat_message.rs b/crates/valence_core/src/packet/c2s/play/chat_message.rs deleted file mode 100644 index a2e9d95a6..000000000 --- a/crates/valence_core/src/packet/c2s/play/chat_message.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ChatMessageC2s<'a> { - pub message: &'a str, - pub timestamp: u64, - pub salt: u64, - pub signature: Option<&'a [u8; 256]>, - pub message_count: VarInt, - // This is a bitset of 20; each bit represents one - // of the last 20 messages received and whether or not - // the message was acknowledged by the client - pub acknowledgement: [u8; 3], -} diff --git a/crates/valence_core/src/packet/c2s/play/click_slot.rs b/crates/valence_core/src/packet/c2s/play/click_slot.rs deleted file mode 100644 index ad03ce693..000000000 --- a/crates/valence_core/src/packet/c2s/play/click_slot.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ClickSlotC2s { - pub window_id: u8, - pub state_id: VarInt, - pub slot_idx: i16, - /// The button used to click the slot. An enum can't easily be used for this - /// because the meaning of this value depends on the mode. - pub button: i8, - pub mode: ClickMode, - pub slot_changes: Vec, - pub carried_item: Option, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Encode, Decode)] -pub enum ClickMode { - Click, - ShiftClick, - Hotbar, - CreativeMiddleClick, - DropKey, - Drag, - DoubleClick, -} - -#[derive(Clone, Debug, Encode, Decode)] -pub struct Slot { - pub idx: i16, - pub item: Option, -} diff --git a/crates/valence_core/src/packet/c2s/play/client_command.rs b/crates/valence_core/src/packet/c2s/play/client_command.rs deleted file mode 100644 index 8d785e9cb..000000000 --- a/crates/valence_core/src/packet/c2s/play/client_command.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ClientCommandC2s { - pub entity_id: VarInt, - pub action: Action, - pub jump_boost: VarInt, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Action { - StartSneaking, - StopSneaking, - LeaveBed, - StartSprinting, - StopSprinting, - StartJumpWithHorse, - StopJumpWithHorse, - OpenHorseInventory, - StartFlyingWithElytra, -} diff --git a/crates/valence_core/src/packet/c2s/play/client_settings.rs b/crates/valence_core/src/packet/c2s/play/client_settings.rs deleted file mode 100644 index 1dfc7c99f..000000000 --- a/crates/valence_core/src/packet/c2s/play/client_settings.rs +++ /dev/null @@ -1,43 +0,0 @@ -use bitfield_struct::bitfield; - -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ClientSettingsC2s<'a> { - pub locale: &'a str, - pub view_distance: u8, - pub chat_mode: ChatMode, - pub chat_colors: bool, - pub displayed_skin_parts: DisplayedSkinParts, - pub main_arm: MainArm, - pub enable_text_filtering: bool, - pub allow_server_listings: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Encode, Decode)] -pub enum ChatMode { - Enabled, - CommandsOnly, - #[default] - Hidden, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct DisplayedSkinParts { - pub cape: bool, - pub jacket: bool, - pub left_sleeve: bool, - pub right_sleeve: bool, - pub left_pants_leg: bool, - pub right_pants_leg: bool, - pub hat: bool, - _pad: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Encode, Decode)] -pub enum MainArm { - Left, - #[default] - Right, -} diff --git a/crates/valence_core/src/packet/c2s/play/client_status.rs b/crates/valence_core/src/packet/c2s/play/client_status.rs deleted file mode 100644 index 4a95300f3..000000000 --- a/crates/valence_core/src/packet/c2s/play/client_status.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub enum ClientStatusC2s { - PerformRespawn, - RequestStats, -} diff --git a/crates/valence_core/src/packet/c2s/play/close_handled_screen.rs b/crates/valence_core/src/packet/c2s/play/close_handled_screen.rs deleted file mode 100644 index 6b1412368..000000000 --- a/crates/valence_core/src/packet/c2s/play/close_handled_screen.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct CloseHandledScreenC2s { - pub window_id: i8, -} diff --git a/crates/valence_core/src/packet/c2s/play/command_execution.rs b/crates/valence_core/src/packet/c2s/play/command_execution.rs deleted file mode 100644 index a7285b368..000000000 --- a/crates/valence_core/src/packet/c2s/play/command_execution.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CommandExecutionC2s<'a> { - pub command: &'a str, - pub timestamp: u64, - pub salt: u64, - pub argument_signatures: Vec>, - pub message_count: VarInt, - //// This is a bitset of 20; each bit represents one - //// of the last 20 messages received and whether or not - //// the message was acknowledged by the client - pub acknowledgement: [u8; 3], -} - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct CommandArgumentSignature<'a> { - pub argument_name: &'a str, - pub signature: &'a [u8; 256], -} diff --git a/crates/valence_core/src/packet/c2s/play/craft_request.rs b/crates/valence_core/src/packet/c2s/play/craft_request.rs deleted file mode 100644 index b762f4e43..000000000 --- a/crates/valence_core/src/packet/c2s/play/craft_request.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CraftRequestC2s<'a> { - pub window_id: i8, - pub recipe: Ident>, - pub make_all: bool, -} diff --git a/crates/valence_core/src/packet/c2s/play/creative_inventory_action.rs b/crates/valence_core/src/packet/c2s/play/creative_inventory_action.rs deleted file mode 100644 index 6d1f8f990..000000000 --- a/crates/valence_core/src/packet/c2s/play/creative_inventory_action.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::item::ItemStack; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CreativeInventoryActionC2s { - pub slot: i16, - pub clicked_item: Option, -} diff --git a/crates/valence_core/src/packet/c2s/play/custom_payload.rs b/crates/valence_core/src/packet/c2s/play/custom_payload.rs deleted file mode 100644 index e1ab154a7..000000000 --- a/crates/valence_core/src/packet/c2s/play/custom_payload.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::raw::RawBytes; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CustomPayloadC2s<'a> { - pub channel: Ident>, - pub data: RawBytes<'a>, -} diff --git a/crates/valence_core/src/packet/c2s/play/hand_swing.rs b/crates/valence_core/src/packet/c2s/play/hand_swing.rs deleted file mode 100644 index fe2238d9e..000000000 --- a/crates/valence_core/src/packet/c2s/play/hand_swing.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::hand::Hand; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct HandSwingC2s { - pub hand: Hand, -} diff --git a/crates/valence_core/src/packet/c2s/play/jigsaw_generating.rs b/crates/valence_core/src/packet/c2s/play/jigsaw_generating.rs deleted file mode 100644 index c67a728cc..000000000 --- a/crates/valence_core/src/packet/c2s/play/jigsaw_generating.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct JigsawGeneratingC2s { - pub position: BlockPos, - pub levels: VarInt, - pub keep_jigsaws: bool, -} diff --git a/crates/valence_core/src/packet/c2s/play/keep_alive.rs b/crates/valence_core/src/packet/c2s/play/keep_alive.rs deleted file mode 100644 index 8e6ced7e5..000000000 --- a/crates/valence_core/src/packet/c2s/play/keep_alive.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct KeepAliveC2s { - pub id: u64, -} diff --git a/crates/valence_core/src/packet/c2s/play/message_acknowledgment.rs b/crates/valence_core/src/packet/c2s/play/message_acknowledgment.rs deleted file mode 100644 index 41924d8f2..000000000 --- a/crates/valence_core/src/packet/c2s/play/message_acknowledgment.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct MessageAcknowledgmentC2s { - pub message_count: VarInt, -} diff --git a/crates/valence_core/src/packet/c2s/play/pick_from_inventory.rs b/crates/valence_core/src/packet/c2s/play/pick_from_inventory.rs deleted file mode 100644 index 7e0d6dcca..000000000 --- a/crates/valence_core/src/packet/c2s/play/pick_from_inventory.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PickFromInventoryC2s { - pub slot_to_use: VarInt, -} diff --git a/crates/valence_core/src/packet/c2s/play/play_pong.rs b/crates/valence_core/src/packet/c2s/play/play_pong.rs deleted file mode 100644 index 7b6be2f4c..000000000 --- a/crates/valence_core/src/packet/c2s/play/play_pong.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayPongC2s { - pub id: i32, -} diff --git a/crates/valence_core/src/packet/c2s/play/player_action.rs b/crates/valence_core/src/packet/c2s/play/player_action.rs deleted file mode 100644 index 6c5067825..000000000 --- a/crates/valence_core/src/packet/c2s/play/player_action.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::direction::Direction; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerActionC2s { - pub action: Action, - pub position: BlockPos, - pub direction: Direction, - pub sequence: VarInt, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Action { - StartDestroyBlock, - AbortDestroyBlock, - StopDestroyBlock, - DropAllItems, - DropItem, - ReleaseUseItem, - SwapItemWithOffhand, -} diff --git a/crates/valence_core/src/packet/c2s/play/player_input.rs b/crates/valence_core/src/packet/c2s/play/player_input.rs deleted file mode 100644 index 6772a5294..000000000 --- a/crates/valence_core/src/packet/c2s/play/player_input.rs +++ /dev/null @@ -1,19 +0,0 @@ -use bitfield_struct::bitfield; - -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerInputC2s { - pub sideways: f32, - pub forward: f32, - pub flags: Flags, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct Flags { - pub jump: bool, - pub unmount: bool, - #[bits(6)] - _pad: u8, -} diff --git a/crates/valence_core/src/packet/c2s/play/player_interact_block.rs b/crates/valence_core/src/packet/c2s/play/player_interact_block.rs deleted file mode 100644 index 9fe96df7e..000000000 --- a/crates/valence_core/src/packet/c2s/play/player_interact_block.rs +++ /dev/null @@ -1,17 +0,0 @@ -use glam::Vec3; - -use crate::block_pos::BlockPos; -use crate::direction::Direction; -use crate::hand::Hand; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerInteractBlockC2s { - pub hand: Hand, - pub position: BlockPos, - pub face: Direction, - pub cursor_pos: Vec3, - pub head_inside_block: bool, - pub sequence: VarInt, -} diff --git a/crates/valence_core/src/packet/c2s/play/player_interact_entity.rs b/crates/valence_core/src/packet/c2s/play/player_interact_entity.rs deleted file mode 100644 index 45594072e..000000000 --- a/crates/valence_core/src/packet/c2s/play/player_interact_entity.rs +++ /dev/null @@ -1,19 +0,0 @@ -use glam::Vec3; - -use crate::hand::Hand; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerInteractEntityC2s { - pub entity_id: VarInt, - pub interact: EntityInteraction, - pub sneaking: bool, -} - -#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] -pub enum EntityInteraction { - Interact(Hand), - Attack, - InteractAt { target: Vec3, hand: Hand }, -} diff --git a/crates/valence_core/src/packet/c2s/play/player_interact_item.rs b/crates/valence_core/src/packet/c2s/play/player_interact_item.rs deleted file mode 100644 index 437f18430..000000000 --- a/crates/valence_core/src/packet/c2s/play/player_interact_item.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::hand::Hand; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerInteractItemC2s { - pub hand: Hand, - pub sequence: VarInt, -} diff --git a/crates/valence_core/src/packet/c2s/play/player_move.rs b/crates/valence_core/src/packet/c2s/play/player_move.rs deleted file mode 100644 index 492ef303c..000000000 --- a/crates/valence_core/src/packet/c2s/play/player_move.rs +++ /dev/null @@ -1,29 +0,0 @@ -use glam::DVec3; - -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PositionAndOnGround { - pub position: DVec3, - pub on_ground: bool, -} - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct Full { - pub position: DVec3, - pub yaw: f32, - pub pitch: f32, - pub on_ground: bool, -} - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct LookAndOnGround { - pub yaw: f32, - pub pitch: f32, - pub on_ground: bool, -} - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct OnGroundOnly { - pub on_ground: bool, -} diff --git a/crates/valence_core/src/packet/c2s/play/player_session.rs b/crates/valence_core/src/packet/c2s/play/player_session.rs deleted file mode 100644 index f97c5fb03..000000000 --- a/crates/valence_core/src/packet/c2s/play/player_session.rs +++ /dev/null @@ -1,12 +0,0 @@ -use uuid::Uuid; - -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerSessionC2s<'a> { - pub session_id: Uuid, - // Public key - pub expires_at: i64, - pub public_key_data: &'a [u8], - pub key_signature: &'a [u8], -} diff --git a/crates/valence_core/src/packet/c2s/play/query_block_nbt.rs b/crates/valence_core/src/packet/c2s/play/query_block_nbt.rs deleted file mode 100644 index d20c57011..000000000 --- a/crates/valence_core/src/packet/c2s/play/query_block_nbt.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct QueryBlockNbtC2s { - pub transaction_id: VarInt, - pub position: BlockPos, -} diff --git a/crates/valence_core/src/packet/c2s/play/query_entity_nbt.rs b/crates/valence_core/src/packet/c2s/play/query_entity_nbt.rs deleted file mode 100644 index 4476680b2..000000000 --- a/crates/valence_core/src/packet/c2s/play/query_entity_nbt.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct QueryEntityNbtC2s { - pub transaction_id: VarInt, - pub entity_id: VarInt, -} diff --git a/crates/valence_core/src/packet/c2s/play/recipe_book_data.rs b/crates/valence_core/src/packet/c2s/play/recipe_book_data.rs deleted file mode 100644 index e29170472..000000000 --- a/crates/valence_core/src/packet/c2s/play/recipe_book_data.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct RecipeBookDataC2s<'a> { - pub recipe_id: Ident>, -} diff --git a/crates/valence_core/src/packet/c2s/play/recipe_category_options.rs b/crates/valence_core/src/packet/c2s/play/recipe_category_options.rs deleted file mode 100644 index 2abcebe6c..000000000 --- a/crates/valence_core/src/packet/c2s/play/recipe_category_options.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct RecipeCategoryOptionsC2s { - pub book_id: RecipeBookId, - pub book_open: bool, - pub filter_active: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum RecipeBookId { - Crafting, - Furnace, - BlastFurnace, - Smoker, -} diff --git a/crates/valence_core/src/packet/c2s/play/rename_item.rs b/crates/valence_core/src/packet/c2s/play/rename_item.rs deleted file mode 100644 index 82f2fc665..000000000 --- a/crates/valence_core/src/packet/c2s/play/rename_item.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct RenameItemC2s<'a> { - pub item_name: &'a str, -} diff --git a/crates/valence_core/src/packet/c2s/play/request_command_completions.rs b/crates/valence_core/src/packet/c2s/play/request_command_completions.rs deleted file mode 100644 index efc86e2f6..000000000 --- a/crates/valence_core/src/packet/c2s/play/request_command_completions.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct RequestCommandCompletionsC2s<'a> { - pub transaction_id: VarInt, - pub text: &'a str, -} diff --git a/crates/valence_core/src/packet/c2s/play/resource_pack_status.rs b/crates/valence_core/src/packet/c2s/play/resource_pack_status.rs deleted file mode 100644 index 752c1dbca..000000000 --- a/crates/valence_core/src/packet/c2s/play/resource_pack_status.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub enum ResourcePackStatusC2s { - SuccessfullyLoaded, - Declined, - FailedDownload, - Accepted, -} diff --git a/crates/valence_core/src/packet/c2s/play/select_merchant_trade.rs b/crates/valence_core/src/packet/c2s/play/select_merchant_trade.rs deleted file mode 100644 index 0b7602c18..000000000 --- a/crates/valence_core/src/packet/c2s/play/select_merchant_trade.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct SelectMerchantTradeC2s { - pub selected_slot: VarInt, -} diff --git a/crates/valence_core/src/packet/c2s/play/spectator_teleport.rs b/crates/valence_core/src/packet/c2s/play/spectator_teleport.rs deleted file mode 100644 index 8931c1ea6..000000000 --- a/crates/valence_core/src/packet/c2s/play/spectator_teleport.rs +++ /dev/null @@ -1,8 +0,0 @@ -use uuid::Uuid; - -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct SpectatorTeleportC2s { - pub target: Uuid, -} diff --git a/crates/valence_core/src/packet/c2s/play/teleport_confirm.rs b/crates/valence_core/src/packet/c2s/play/teleport_confirm.rs deleted file mode 100644 index 7c8cd730a..000000000 --- a/crates/valence_core/src/packet/c2s/play/teleport_confirm.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct TeleportConfirmC2s { - pub teleport_id: VarInt, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_beacon.rs b/crates/valence_core/src/packet/c2s/play/update_beacon.rs deleted file mode 100644 index de9688f25..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_beacon.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateBeaconC2s { - pub primary_effect: Option, - pub secondary_effect: Option, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_command_block.rs b/crates/valence_core/src/packet/c2s/play/update_command_block.rs deleted file mode 100644 index defed98f5..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_command_block.rs +++ /dev/null @@ -1,29 +0,0 @@ -use bitfield_struct::bitfield; - -use crate::block_pos::BlockPos; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateCommandBlockC2s<'a> { - pub position: BlockPos, - pub command: &'a str, - pub mode: Mode, - pub flags: Flags, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Mode { - Sequence, - Auto, - Redstone, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct Flags { - pub track_output: bool, - pub conditional: bool, - pub automatic: bool, - #[bits(5)] - _pad: u8, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_command_block_minecart.rs b/crates/valence_core/src/packet/c2s/play/update_command_block_minecart.rs deleted file mode 100644 index 897ad0150..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_command_block_minecart.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateCommandBlockMinecartC2s<'a> { - pub entity_id: VarInt, - pub command: &'a str, - pub track_output: bool, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_difficulty.rs b/crates/valence_core/src/packet/c2s/play/update_difficulty.rs deleted file mode 100644 index f5451c063..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_difficulty.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::difficulty::Difficulty; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub struct UpdateDifficultyC2s { - pub difficulty: Difficulty, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_difficulty_lock.rs b/crates/valence_core/src/packet/c2s/play/update_difficulty_lock.rs deleted file mode 100644 index e40253cd5..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_difficulty_lock.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateDifficultyLockC2s { - pub locked: bool, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_jigsaw.rs b/crates/valence_core/src/packet/c2s/play/update_jigsaw.rs deleted file mode 100644 index 4e1fed1f8..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_jigsaw.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::borrow::Cow; - -use crate::block_pos::BlockPos; -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct UpdateJigsawC2s<'a> { - pub position: BlockPos, - pub name: Ident>, - pub target: Ident>, - pub pool: Ident>, - pub final_state: &'a str, - pub joint_type: &'a str, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_player_abilities.rs b/crates/valence_core/src/packet/c2s/play/update_player_abilities.rs deleted file mode 100644 index 8f96cee19..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_player_abilities.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub enum UpdatePlayerAbilitiesC2s { - #[tag = 0b00] - StopFlying, - #[tag = 0b10] - StartFlying, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_selected_slot.rs b/crates/valence_core/src/packet/c2s/play/update_selected_slot.rs deleted file mode 100644 index 6cb746579..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_selected_slot.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateSelectedSlotC2s { - pub slot: i16, -} diff --git a/crates/valence_core/src/packet/c2s/play/update_sign.rs b/crates/valence_core/src/packet/c2s/play/update_sign.rs deleted file mode 100644 index f832bdc98..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_sign.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateSignC2s<'a> { - pub position: BlockPos, - pub lines: [&'a str; 4], -} diff --git a/crates/valence_core/src/packet/c2s/play/update_structure_block.rs b/crates/valence_core/src/packet/c2s/play/update_structure_block.rs deleted file mode 100644 index 316d9b5a9..000000000 --- a/crates/valence_core/src/packet/c2s/play/update_structure_block.rs +++ /dev/null @@ -1,62 +0,0 @@ -use bitfield_struct::bitfield; - -use crate::block_pos::BlockPos; -use crate::packet::var_long::VarLong; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateStructureBlockC2s<'a> { - pub position: BlockPos, - pub action: Action, - pub mode: Mode, - pub name: &'a str, - pub offset_xyz: [i8; 3], - pub size_xyz: [i8; 3], - pub mirror: Mirror, - pub rotation: Rotation, - pub metadata: &'a str, - pub integrity: f32, - pub seed: VarLong, - pub flags: Flags, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Action { - UpdateData, - SaveStructure, - LoadStructure, - DetectSize, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Mode { - Save, - Load, - Corner, - Data, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Mirror { - None, - LeftRight, - FrontBack, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Rotation { - None, - Clockwise90, - Clockwise180, - Counterclockwise90, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct Flags { - pub ignore_entities: bool, - pub show_air: bool, - pub show_bounding_box: bool, - #[bits(5)] - _pad: u8, -} diff --git a/crates/valence_core/src/packet/c2s/play/vehicle_move.rs b/crates/valence_core/src/packet/c2s/play/vehicle_move.rs deleted file mode 100644 index d349307ee..000000000 --- a/crates/valence_core/src/packet/c2s/play/vehicle_move.rs +++ /dev/null @@ -1,10 +0,0 @@ -use glam::DVec3; - -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct VehicleMoveC2s { - pub position: DVec3, - pub yaw: f32, - pub pitch: f32, -} diff --git a/crates/valence_core/src/packet/c2s/status/query_ping.rs b/crates/valence_core/src/packet/c2s/status/query_ping.rs deleted file mode 100644 index 08b1abe02..000000000 --- a/crates/valence_core/src/packet/c2s/status/query_ping.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct QueryPingC2s { - pub payload: u64, -} diff --git a/crates/valence_core/src/packet/c2s/status/query_request.rs b/crates/valence_core/src/packet/c2s/status/query_request.rs deleted file mode 100644 index d09d00978..000000000 --- a/crates/valence_core/src/packet/c2s/status/query_request.rs +++ /dev/null @@ -1,4 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct QueryRequestC2s; diff --git a/crates/valence_core/src/packet/message_signature.rs b/crates/valence_core/src/packet/message_signature.rs deleted file mode 100644 index bc78225a1..000000000 --- a/crates/valence_core/src/packet/message_signature.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::io::Write; - -use super::var_int::VarInt; -use super::{Decode, Encode}; - -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct MessageSignature<'a> { - pub message_id: i32, - pub signature: Option<&'a [u8; 256]>, -} - -impl<'a> Encode for MessageSignature<'a> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - VarInt(self.message_id + 1).encode(&mut w)?; - - match self.signature { - None => {} - Some(signature) => signature.encode(&mut w)?, - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for MessageSignature<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let message_id = VarInt::decode(r)?.0 - 1; // TODO: this can underflow. - - let signature = if message_id == -1 { - Some(<&[u8; 256]>::decode(r)?) - } else { - None - }; - - Ok(Self { - message_id, - signature, - }) - } -} diff --git a/crates/valence_core/src/packet/s2c.rs b/crates/valence_core/src/packet/s2c.rs deleted file mode 100644 index 69d60f3c9..000000000 --- a/crates/valence_core/src/packet/s2c.rs +++ /dev/null @@ -1,379 +0,0 @@ -pub mod status { - pub use query_pong::QueryPongS2c; - pub use query_response::QueryResponseS2c; - - pub mod query_pong; - pub mod query_response; - - packet_group! { - #[derive(Clone)] - S2cStatusPacket<'a> { - QueryPongS2c, - QueryResponseS2c<'a>, - } - } -} - -pub mod login { - pub use login_compression::LoginCompressionS2c; - pub use login_disconnect::LoginDisconnectS2c; - pub use login_hello::LoginHelloS2c; - pub use login_query_request::LoginQueryRequestS2c; - pub use login_success::LoginSuccessS2c; - - pub mod login_compression; - pub mod login_disconnect; - pub mod login_hello; - pub mod login_query_request; - pub mod login_success; - - packet_group! { - #[derive(Clone)] - S2cLoginPacket<'a> { - LoginCompressionS2c, - LoginDisconnectS2c<'a>, - LoginHelloS2c<'a>, - LoginQueryRequestS2c<'a>, - LoginSuccessS2c<'a>, - } - } -} - -pub mod play { - pub use advancement_update::AdvancementUpdateS2c; - pub use block_breaking_progress::BlockBreakingProgressS2c; - pub use block_entity_update::BlockEntityUpdateS2c; - pub use block_event::BlockEventS2c; - pub use block_update::BlockUpdateS2c; - pub use boss_bar::BossBarS2c; - pub use bundle_splitter::BundleSplitter; - pub use chat_message::ChatMessageS2c; - pub use chat_suggestions::ChatSuggestionsS2c; - pub use chunk_biome_data::ChunkBiomeDataS2c; - pub use chunk_data::ChunkDataS2c; - pub use chunk_delta_update::ChunkDeltaUpdateS2c; - pub use chunk_load_distance::ChunkLoadDistanceS2c; - pub use chunk_render_distance_center::ChunkRenderDistanceCenterS2c; - pub use clear_title::ClearTitleS2c; - pub use close_screen::CloseScreenS2c; - pub use command_suggestions::CommandSuggestionsS2c; - pub use command_tree::CommandTreeS2c; - pub use cooldown_update::CooldownUpdateS2c; - pub use craft_failed_response::CraftFailedResponseS2c; - pub use custom_payload::CustomPayloadS2c; - pub use damage_tilt::DamageTiltS2c; - pub use death_message::DeathMessageS2c; - pub use difficulty::DifficultyS2c; - pub use disconnect::DisconnectS2c; - pub use end_combat::EndCombatS2c; - pub use enter_combat::EnterCombatS2c; - pub use entities_destroy::EntitiesDestroyS2c; - pub use entity_animation::EntityAnimationS2c; - pub use entity_attach::EntityAttachS2c; - pub use entity_attributes::EntityAttributesS2c; - pub use entity_damage::EntityDamageS2c; - pub use entity_equipment_update::EntityEquipmentUpdateS2c; - pub use entity_move::{MoveRelative, Rotate, RotateAndMoveRelative}; - pub use entity_passengers_set::EntityPassengersSetS2c; - pub use entity_position::EntityPositionS2c; - pub use entity_set_head_yaw::EntitySetHeadYawS2c; - pub use entity_spawn::EntitySpawnS2c; - pub use entity_status::EntityStatusS2c; - pub use entity_status_effect::EntityStatusEffectS2c; - pub use entity_tracker_update::EntityTrackerUpdateS2c; - pub use entity_velocity_update::EntityVelocityUpdateS2c; - pub use experience_bar_update::ExperienceBarUpdateS2c; - pub use experience_orb_spawn::ExperienceOrbSpawnS2c; - pub use explosion::ExplosionS2c; - pub use features::FeaturesS2c; - pub use game_join::GameJoinS2c; - pub use game_message::GameMessageS2c; - pub use game_state_change::GameStateChangeS2c; - pub use health_update::HealthUpdateS2c; - pub use inventory::InventoryS2c; - pub use item_pickup_animation::ItemPickupAnimationS2c; - pub use keep_alive::KeepAliveS2c; - pub use light_update::LightUpdateS2c; - pub use look_at::LookAtS2c; - pub use map_update::MapUpdateS2c; - pub use nbt_query_response::NbtQueryResponseS2c; - pub use open_horse_screen::OpenHorseScreenS2c; - pub use open_screen::OpenScreenS2c; - pub use open_written_book::OpenWrittenBookS2c; - pub use overlay_message::OverlayMessageS2c; - pub use particle::ParticleS2c; - pub use play_ping::PlayPingS2c; - pub use play_sound::PlaySoundS2c; - pub use play_sound_from_entity::PlaySoundFromEntityS2c; - pub use player_abilities::PlayerAbilitiesS2c; - pub use player_action_response::PlayerActionResponseS2c; - pub use player_list::PlayerListS2c; - pub use player_list_header::PlayerListHeaderS2c; - pub use player_position_look::PlayerPositionLookS2c; - pub use player_remove::PlayerRemoveS2c; - pub use player_respawn::PlayerRespawnS2c; - pub use player_spawn::PlayerSpawnS2c; - pub use player_spawn_position::PlayerSpawnPositionS2c; - pub use profileless_chat_message::ProfilelessChatMessageS2c; - pub use remove_entity_status_effect::RemoveEntityStatusEffectS2c; - pub use remove_message::RemoveMessageS2c; - pub use resource_pack_send::ResourcePackSendS2c; - pub use scoreboard_display::ScoreboardDisplayS2c; - pub use scoreboard_objective_update::ScoreboardObjectiveUpdateS2c; - pub use scoreboard_player_update::ScoreboardPlayerUpdateS2c; - pub use screen_handler_property_update::ScreenHandlerPropertyUpdateS2c; - pub use screen_handler_slot_update::ScreenHandlerSlotUpdateS2c; - pub use select_advancement_tab::SelectAdvancementTabS2c; - pub use server_metadata::ServerMetadataS2c; - pub use set_camera_entity::SetCameraEntityS2c; - pub use set_trade_offers::SetTradeOffersS2c; - pub use sign_editor_open::SignEditorOpenS2c; - pub use simulation_distance::SimulationDistanceS2c; - pub use statistics::StatisticsS2c; - pub use stop_sound::StopSoundS2c; - pub use subtitle::SubtitleS2c; - pub use synchronize_recipes::SynchronizeRecipesS2c; - pub use synchronize_tags::SynchronizeTagsS2c; - pub use team::TeamS2c; - pub use title::TitleS2c; - pub use title_fade::TitleFadeS2c; - pub use unload_chunk::UnloadChunkS2c; - pub use unlock_recipes::UnlockRecipesS2c; - pub use update_selected_slot::UpdateSelectedSlotS2c; - pub use vehicle_move::VehicleMoveS2c; - pub use world_border_center_changed::WorldBorderCenterChangedS2c; - pub use world_border_initialize::WorldBorderInitializeS2c; - pub use world_border_interpolate_size::WorldBorderInterpolateSizeS2c; - pub use world_border_size_changed::WorldBorderSizeChangedS2c; - pub use world_border_warning_blocks_changed::WorldBorderWarningBlocksChangedS2c; - pub use world_border_warning_time_changed::WorldBorderWarningTimeChangedS2c; - pub use world_event::WorldEventS2c; - pub use world_time_update::WorldTimeUpdateS2c; - - pub mod advancement_update; - pub mod block_breaking_progress; - pub mod block_entity_update; - pub mod block_event; - pub mod block_update; - pub mod boss_bar; - pub mod bundle_splitter; - pub mod chat_message; - pub mod chat_suggestions; - pub mod chunk_biome_data; - pub mod chunk_data; - pub mod chunk_delta_update; - pub mod chunk_load_distance; - pub mod chunk_render_distance_center; - pub mod clear_title; - pub mod close_screen; - pub mod command_suggestions; - pub mod command_tree; - pub mod cooldown_update; - pub mod craft_failed_response; - pub mod custom_payload; - pub mod damage_tilt; - pub mod death_message; - pub mod difficulty; - pub mod disconnect; - pub mod end_combat; - pub mod enter_combat; - pub mod entities_destroy; - pub mod entity_animation; - pub mod entity_attach; - pub mod entity_attributes; - pub mod entity_damage; - pub mod entity_equipment_update; - pub mod entity_move; - pub mod entity_passengers_set; - pub mod entity_position; - pub mod entity_set_head_yaw; - pub mod entity_spawn; - pub mod entity_status; - pub mod entity_status_effect; - pub mod entity_tracker_update; - pub mod entity_velocity_update; - pub mod experience_bar_update; - pub mod experience_orb_spawn; - pub mod explosion; - pub mod features; - pub mod game_join; - pub mod game_message; - pub mod game_state_change; - pub mod health_update; - pub mod inventory; - pub mod item_pickup_animation; - pub mod keep_alive; - pub mod light_update; - pub mod look_at; - pub mod map_update; - pub mod nbt_query_response; - pub mod open_horse_screen; - pub mod open_screen; - pub mod open_written_book; - pub mod overlay_message; - pub mod particle; - pub mod play_ping; - pub mod play_sound; - pub mod play_sound_from_entity; - pub mod player_abilities; - pub mod player_action_response; - pub mod player_list; - pub mod player_list_header; - pub mod player_position_look; - pub mod player_remove; - pub mod player_respawn; - pub mod player_spawn; - pub mod player_spawn_position; - pub mod profileless_chat_message; - pub mod remove_entity_status_effect; - pub mod remove_message; - pub mod resource_pack_send; - pub mod scoreboard_display; - pub mod scoreboard_objective_update; - pub mod scoreboard_player_update; - pub mod screen_handler_property_update; - pub mod screen_handler_slot_update; - pub mod select_advancement_tab; - pub mod server_metadata; - pub mod set_camera_entity; - pub mod set_trade_offers; - pub mod sign_editor_open; - pub mod simulation_distance; - pub mod statistics; - pub mod stop_sound; - pub mod subtitle; - pub mod synchronize_recipes; - pub mod synchronize_tags; - pub mod team; - pub mod title; - pub mod title_fade; - pub mod unload_chunk; - pub mod unlock_recipes; - pub mod update_selected_slot; - pub mod vehicle_move; - pub mod world_border_center_changed; - pub mod world_border_initialize; - pub mod world_border_interpolate_size; - pub mod world_border_size_changed; - pub mod world_border_warning_blocks_changed; - pub mod world_border_warning_time_changed; - pub mod world_event; - pub mod world_time_update; - - packet_group! { - #[derive(Clone)] - S2cPlayPacket<'a> { - AdvancementUpdateS2c<'a>, - BlockBreakingProgressS2c, - BlockEntityUpdateS2c<'a>, - BlockEventS2c, - BlockUpdateS2c, - BossBarS2c, - BundleSplitter, - ChatMessageS2c<'a>, - ChatSuggestionsS2c<'a>, - ChunkBiomeDataS2c<'a>, - ChunkDataS2c<'a>, - ChunkDeltaUpdateS2c<'a>, - ChunkLoadDistanceS2c, - ChunkRenderDistanceCenterS2c, - ClearTitleS2c, - CloseScreenS2c, - CommandSuggestionsS2c<'a>, - CommandTreeS2c<'a>, - CooldownUpdateS2c, - CraftFailedResponseS2c<'a>, - CustomPayloadS2c<'a>, - DamageTiltS2c, - DeathMessageS2c<'a>, - DifficultyS2c, - DisconnectS2c<'a>, - EndCombatS2c, - EnterCombatS2c, - EntitiesDestroyS2c<'a>, - EntityAnimationS2c, - EntityAttachS2c, - EntityAttributesS2c<'a>, - EntityDamageS2c, - EntityEquipmentUpdateS2c, - EntityPassengersSetS2c, - EntityPositionS2c, - EntitySetHeadYawS2c, - EntitySpawnS2c, - EntityStatusEffectS2c, - EntityStatusS2c, - EntityTrackerUpdateS2c<'a>, - EntityVelocityUpdateS2c, - ExperienceBarUpdateS2c, - ExperienceOrbSpawnS2c, - ExplosionS2c<'a>, - FeaturesS2c<'a>, - GameJoinS2c<'a>, - GameMessageS2c<'a>, - GameStateChangeS2c, - HealthUpdateS2c, - InventoryS2c<'a>, - ItemPickupAnimationS2c, - KeepAliveS2c, - LightUpdateS2c, - LookAtS2c, - MapUpdateS2c<'a>, - MoveRelative, - NbtQueryResponseS2c, - OpenHorseScreenS2c, - OpenScreenS2c<'a>, - OpenWrittenBookS2c, - OverlayMessageS2c<'a>, - ParticleS2c<'a>, - PlayerAbilitiesS2c, - PlayerActionResponseS2c, - PlayerListHeaderS2c<'a>, - PlayerListS2c<'a>, - PlayerPositionLookS2c, - PlayerRemoveS2c<'a>, - PlayerRespawnS2c<'a>, - PlayerSpawnPositionS2c, - PlayerSpawnS2c, - PlayPingS2c, - PlaySoundFromEntityS2c, - PlaySoundS2c<'a>, - ProfilelessChatMessageS2c<'a>, - RemoveEntityStatusEffectS2c, - RemoveMessageS2c<'a>, - ResourcePackSendS2c<'a>, - Rotate, - RotateAndMoveRelative, - ScoreboardDisplayS2c<'a>, - ScoreboardObjectiveUpdateS2c<'a>, - ScoreboardPlayerUpdateS2c<'a>, - ScreenHandlerPropertyUpdateS2c, - ScreenHandlerSlotUpdateS2c<'a>, - SelectAdvancementTabS2c<'a>, - ServerMetadataS2c<'a>, - SetCameraEntityS2c, - SetTradeOffersS2c, - SignEditorOpenS2c, - SimulationDistanceS2c, - StatisticsS2c, - StopSoundS2c<'a>, - SubtitleS2c<'a>, - SynchronizeRecipesS2c<'a>, - SynchronizeTagsS2c<'a>, - TeamS2c<'a>, - TitleFadeS2c, - TitleS2c<'a>, - UnloadChunkS2c, - UnlockRecipesS2c<'a>, - UpdateSelectedSlotS2c, - VehicleMoveS2c, - WorldBorderCenterChangedS2c, - WorldBorderInitializeS2c, - WorldBorderInterpolateSizeS2c, - WorldBorderSizeChangedS2c, - WorldBorderWarningBlocksChangedS2c, - WorldBorderWarningTimeChangedS2c, - WorldEventS2c, - WorldTimeUpdateS2c, - } - } -} diff --git a/crates/valence_core/src/packet/s2c/login/login_compression.rs b/crates/valence_core/src/packet/s2c/login/login_compression.rs deleted file mode 100644 index d94dc9298..000000000 --- a/crates/valence_core/src/packet/s2c/login/login_compression.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct LoginCompressionS2c { - pub threshold: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/login/login_disconnect.rs b/crates/valence_core/src/packet/s2c/login/login_disconnect.rs deleted file mode 100644 index 531e3ea5b..000000000 --- a/crates/valence_core/src/packet/s2c/login/login_disconnect.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct LoginDisconnectS2c<'a> { - pub reason: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/login/login_hello.rs b/crates/valence_core/src/packet/s2c/login/login_hello.rs deleted file mode 100644 index 8be5ee5af..000000000 --- a/crates/valence_core/src/packet/s2c/login/login_hello.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct LoginHelloS2c<'a> { - pub server_id: &'a str, - pub public_key: &'a [u8], - pub verify_token: &'a [u8], -} diff --git a/crates/valence_core/src/packet/s2c/login/login_query_request.rs b/crates/valence_core/src/packet/s2c/login/login_query_request.rs deleted file mode 100644 index 83da7ec58..000000000 --- a/crates/valence_core/src/packet/s2c/login/login_query_request.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::raw::RawBytes; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct LoginQueryRequestS2c<'a> { - pub message_id: VarInt, - pub channel: Ident>, - pub data: RawBytes<'a>, -} diff --git a/crates/valence_core/src/packet/s2c/login/login_success.rs b/crates/valence_core/src/packet/s2c/login/login_success.rs deleted file mode 100644 index 8478270e1..000000000 --- a/crates/valence_core/src/packet/s2c/login/login_success.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::borrow::Cow; - -use uuid::Uuid; - -use crate::packet::{Decode, Encode}; -use crate::property::Property; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct LoginSuccessS2c<'a> { - pub uuid: Uuid, - pub username: &'a str, // TODO: bound this. - pub properties: Cow<'a, [Property]>, -} diff --git a/crates/valence_core/src/packet/s2c/play/block_breaking_progress.rs b/crates/valence_core/src/packet/s2c/play/block_breaking_progress.rs deleted file mode 100644 index e07e0f1dd..000000000 --- a/crates/valence_core/src/packet/s2c/play/block_breaking_progress.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct BlockBreakingProgressS2c { - pub entity_id: VarInt, - pub position: BlockPos, - pub destroy_stage: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/block_entity_update.rs b/crates/valence_core/src/packet/s2c/play/block_entity_update.rs deleted file mode 100644 index 93920ec91..000000000 --- a/crates/valence_core/src/packet/s2c/play/block_entity_update.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::borrow::Cow; - -use valence_nbt::Compound; - -use crate::block_pos::BlockPos; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct BlockEntityUpdateS2c<'a> { - pub position: BlockPos, - pub kind: VarInt, - pub data: Cow<'a, Compound>, -} diff --git a/crates/valence_core/src/packet/s2c/play/block_event.rs b/crates/valence_core/src/packet/s2c/play/block_event.rs deleted file mode 100644 index 9e47f15c3..000000000 --- a/crates/valence_core/src/packet/s2c/play/block_event.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct BlockEventS2c { - pub position: BlockPos, - pub action_id: u8, - pub action_parameter: u8, - pub block_type: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/block_update.rs b/crates/valence_core/src/packet/s2c/play/block_update.rs deleted file mode 100644 index b6f3ad646..000000000 --- a/crates/valence_core/src/packet/s2c/play/block_update.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct BlockUpdateS2c { - pub position: BlockPos, - pub block_id: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/boss_bar.rs b/crates/valence_core/src/packet/s2c/play/boss_bar.rs deleted file mode 100644 index 05557c8a3..000000000 --- a/crates/valence_core/src/packet/s2c/play/boss_bar.rs +++ /dev/null @@ -1,57 +0,0 @@ -use bitfield_struct::bitfield; -use uuid::Uuid; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct BossBarS2c { - pub id: Uuid, - pub action: Action, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub enum Action { - Add { - title: Text, - health: f32, - color: Color, - division: Division, - flags: Flags, - }, - Remove, - UpdateHealth(f32), - UpdateTitle(Text), - UpdateStyle(Color, Division), - UpdateFlags(Flags), -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Color { - Pink, - Blue, - Red, - Green, - Yellow, - Purple, - White, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Division { - NoDivision, - SixNotches, - TenNotches, - TwelveNotches, - TwentyNotches, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct Flags { - pub darken_sky: bool, - pub dragon_bar: bool, - pub create_fog: bool, - #[bits(5)] - _pad: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/bundle_splitter.rs b/crates/valence_core/src/packet/s2c/play/bundle_splitter.rs deleted file mode 100644 index fb6d00741..000000000 --- a/crates/valence_core/src/packet/s2c/play/bundle_splitter.rs +++ /dev/null @@ -1,4 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct BundleSplitter; diff --git a/crates/valence_core/src/packet/s2c/play/chat_message.rs b/crates/valence_core/src/packet/s2c/play/chat_message.rs deleted file mode 100644 index c51ef5040..000000000 --- a/crates/valence_core/src/packet/s2c/play/chat_message.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use uuid::Uuid; - -use crate::packet::message_signature::MessageSignature; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, PartialEq, Debug)] -pub struct ChatMessageS2c<'a> { - pub sender: Uuid, - pub index: VarInt, - pub message_signature: Option<&'a [u8; 256]>, - pub message: &'a str, - pub time_stamp: u64, - pub salt: u64, - pub previous_messages: Vec>, - pub unsigned_content: Option>, - pub filter_type: MessageFilterType, - pub filter_type_bits: Option, - pub chat_type: VarInt, - pub network_name: Cow<'a, Text>, - pub network_target_name: Option>, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum MessageFilterType { - PassThrough, - FullyFiltered, - PartiallyFiltered, -} - -impl<'a> Encode for ChatMessageS2c<'a> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - self.sender.encode(&mut w)?; - self.index.encode(&mut w)?; - self.message_signature.encode(&mut w)?; - self.message.encode(&mut w)?; - self.time_stamp.encode(&mut w)?; - self.salt.encode(&mut w)?; - self.previous_messages.encode(&mut w)?; - self.unsigned_content.encode(&mut w)?; - self.filter_type.encode(&mut w)?; - - if self.filter_type == MessageFilterType::PartiallyFiltered { - match self.filter_type_bits { - // Filler data - None => 0u8.encode(&mut w)?, - Some(bits) => bits.encode(&mut w)?, - } - } - - self.chat_type.encode(&mut w)?; - self.network_name.encode(&mut w)?; - self.network_target_name.encode(&mut w)?; - - Ok(()) - } -} - -impl<'a> Decode<'a> for ChatMessageS2c<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let sender = Uuid::decode(r)?; - let index = VarInt::decode(r)?; - let message_signature = Option::<&'a [u8; 256]>::decode(r)?; - let message = <&str>::decode(r)?; - let time_stamp = u64::decode(r)?; - let salt = u64::decode(r)?; - let previous_messages = Vec::::decode(r)?; - let unsigned_content = Option::>::decode(r)?; - let filter_type = MessageFilterType::decode(r)?; - - let filter_type_bits = match filter_type { - MessageFilterType::PartiallyFiltered => Some(u8::decode(r)?), - _ => None, - }; - - let chat_type = VarInt::decode(r)?; - let network_name = >::decode(r)?; - let network_target_name = Option::>::decode(r)?; - - Ok(Self { - sender, - index, - message_signature, - message, - time_stamp, - salt, - previous_messages, - unsigned_content, - filter_type, - filter_type_bits, - chat_type, - network_name, - network_target_name, - }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/chat_suggestions.rs b/crates/valence_core/src/packet/s2c/play/chat_suggestions.rs deleted file mode 100644 index 97005b10c..000000000 --- a/crates/valence_core/src/packet/s2c/play/chat_suggestions.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ChatSuggestionsS2c<'a> { - pub action: Action, - pub entries: Cow<'a, [&'a str]>, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum Action { - Add, - Remove, - Set, -} diff --git a/crates/valence_core/src/packet/s2c/play/chunk_biome_data.rs b/crates/valence_core/src/packet/s2c/play/chunk_biome_data.rs deleted file mode 100644 index 1eeb32abe..000000000 --- a/crates/valence_core/src/packet/s2c/play/chunk_biome_data.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::borrow::Cow; - -use crate::chunk_pos::ChunkPos; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ChunkBiomeDataS2c<'a> { - pub chunks: Cow<'a, [ChunkBiome<'a>]>, -} - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ChunkBiome<'a> { - pub pos: ChunkPos, - /// Chunk data structure, with sections containing only the `Biomes` field. - pub data: &'a [u8], -} diff --git a/crates/valence_core/src/packet/s2c/play/chunk_data.rs b/crates/valence_core/src/packet/s2c/play/chunk_data.rs deleted file mode 100644 index bb873eeb5..000000000 --- a/crates/valence_core/src/packet/s2c/play/chunk_data.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::borrow::Cow; - -use valence_nbt::Compound; - -use crate::chunk_pos::ChunkPos; -use crate::packet::array::LengthPrefixedArray; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ChunkDataS2c<'a> { - pub pos: ChunkPos, - pub heightmaps: Cow<'a, Compound>, - pub blocks_and_biomes: &'a [u8], - pub block_entities: Cow<'a, [ChunkDataBlockEntity<'a>]>, - pub trust_edges: bool, - pub sky_light_mask: Cow<'a, [u64]>, - pub block_light_mask: Cow<'a, [u64]>, - pub empty_sky_light_mask: Cow<'a, [u64]>, - pub empty_block_light_mask: Cow<'a, [u64]>, - pub sky_light_arrays: Cow<'a, [LengthPrefixedArray]>, - pub block_light_arrays: Cow<'a, [LengthPrefixedArray]>, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct ChunkDataBlockEntity<'a> { - pub packed_xz: i8, - pub y: i16, - pub kind: VarInt, - pub data: Cow<'a, Compound>, -} diff --git a/crates/valence_core/src/packet/s2c/play/chunk_delta_update.rs b/crates/valence_core/src/packet/s2c/play/chunk_delta_update.rs deleted file mode 100644 index 779dc4893..000000000 --- a/crates/valence_core/src/packet/s2c/play/chunk_delta_update.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::var_long::VarLong; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ChunkDeltaUpdateS2c<'a> { - pub chunk_section_position: i64, - pub invert_trust_edges: bool, - pub blocks: Cow<'a, [VarLong]>, -} diff --git a/crates/valence_core/src/packet/s2c/play/chunk_load_distance.rs b/crates/valence_core/src/packet/s2c/play/chunk_load_distance.rs deleted file mode 100644 index 33100e583..000000000 --- a/crates/valence_core/src/packet/s2c/play/chunk_load_distance.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ChunkLoadDistanceS2c { - pub view_distance: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/chunk_render_distance_center.rs b/crates/valence_core/src/packet/s2c/play/chunk_render_distance_center.rs deleted file mode 100644 index 9e5c5fa77..000000000 --- a/crates/valence_core/src/packet/s2c/play/chunk_render_distance_center.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ChunkRenderDistanceCenterS2c { - pub chunk_x: VarInt, - pub chunk_z: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/clear_title.rs b/crates/valence_core/src/packet/s2c/play/clear_title.rs deleted file mode 100644 index 2eaeabacf..000000000 --- a/crates/valence_core/src/packet/s2c/play/clear_title.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ClearTitleS2c { - pub reset: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/close_screen.rs b/crates/valence_core/src/packet/s2c/play/close_screen.rs deleted file mode 100644 index 2a1a41610..000000000 --- a/crates/valence_core/src/packet/s2c/play/close_screen.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct CloseScreenS2c { - /// Ignored by notchian clients. - pub window_id: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/command_suggestions.rs b/crates/valence_core/src/packet/s2c/play/command_suggestions.rs deleted file mode 100644 index e33db3d82..000000000 --- a/crates/valence_core/src/packet/s2c/play/command_suggestions.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CommandSuggestionsS2c<'a> { - pub id: VarInt, - pub start: VarInt, - pub length: VarInt, - pub matches: Vec>, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct Match<'a> { - pub suggested_match: &'a str, - pub tooltip: Option>, -} diff --git a/crates/valence_core/src/packet/s2c/play/command_tree.rs b/crates/valence_core/src/packet/s2c/play/command_tree.rs deleted file mode 100644 index 3d74ebcf6..000000000 --- a/crates/valence_core/src/packet/s2c/play/command_tree.rs +++ /dev/null @@ -1,441 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use anyhow::bail; -use byteorder::WriteBytesExt; - -use crate::ident::Ident; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CommandTreeS2c<'a> { - pub commands: Vec>, - pub root_index: VarInt, -} - -#[derive(Clone, Debug)] -pub struct Node<'a> { - pub children: Vec, - pub data: NodeData<'a>, - pub executable: bool, - pub redirect_node: Option, -} - -#[derive(Clone, Debug)] -pub enum NodeData<'a> { - Root, - Literal { - name: &'a str, - }, - Argument { - name: &'a str, - parser: Parser<'a>, - suggestion: Option, - }, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Suggestion { - AskServer, - AllRecipes, - AvailableSounds, - AvailableBiomes, - SummonableEntities, -} - -#[derive(Clone, Debug)] -pub enum Parser<'a> { - Bool, - Float { min: Option, max: Option }, - Double { min: Option, max: Option }, - Integer { min: Option, max: Option }, - Long { min: Option, max: Option }, - String(StringArg), - Entity { single: bool, only_players: bool }, - GameProfile, - BlockPos, - ColumnPos, - Vec3, - Vec2, - BlockState, - BlockPredicate, - ItemStack, - ItemPredicate, - Color, - Component, - Message, - NbtCompoundTag, - NbtTag, - NbtPath, - Objective, - ObjectiveCriteria, - Operation, - Particle, - Angle, - Rotation, - ScoreboardSlot, - ScoreHolder { allow_multiple: bool }, - Swizzle, - Team, - ItemSlot, - ResourceLocation, - Function, - EntityAnchor, - IntRange, - FloatRange, - Dimension, - GameMode, - Time, - ResourceOrTag { registry: Ident> }, - ResourceOrTagKey { registry: Ident> }, - Resource { registry: Ident> }, - ResourceKey { registry: Ident> }, - TemplateMirror, - TemplateRotation, - Uuid, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum StringArg { - SingleWord, - QuotablePhrase, - GreedyPhrase, -} - -impl Encode for Node<'_> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - let node_type = match &self.data { - NodeData::Root => 0, - NodeData::Literal { .. } => 1, - NodeData::Argument { .. } => 2, - }; - - let has_suggestion = matches!( - &self.data, - NodeData::Argument { - suggestion: Some(_), - .. - } - ); - - let flags: u8 = node_type - | (self.executable as u8 * 0x04) - | (self.redirect_node.is_some() as u8 * 0x08) - | (has_suggestion as u8 * 0x10); - - w.write_u8(flags)?; - - self.children.encode(&mut w)?; - - if let Some(redirect_node) = self.redirect_node { - redirect_node.encode(&mut w)?; - } - - match &self.data { - NodeData::Root => {} - NodeData::Literal { name } => { - name.encode(&mut w)?; - } - NodeData::Argument { - name, - parser, - suggestion, - } => { - name.encode(&mut w)?; - parser.encode(&mut w)?; - - if let Some(suggestion) = suggestion { - match suggestion { - Suggestion::AskServer => "ask_server", - Suggestion::AllRecipes => "all_recipes", - Suggestion::AvailableSounds => "available_sounds", - Suggestion::AvailableBiomes => "available_biomes", - Suggestion::SummonableEntities => "summonable_entities", - } - .encode(&mut w)?; - } - } - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for Node<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let flags = u8::decode(r)?; - - let children = Vec::decode(r)?; - - let redirect_node = if flags & 0x08 != 0 { - Some(VarInt::decode(r)?) - } else { - None - }; - - let node_data = match flags & 0x3 { - 0 => NodeData::Root, - 1 => NodeData::Literal { - name: <&str>::decode(r)?, - }, - 2 => NodeData::Argument { - name: <&str>::decode(r)?, - parser: Parser::decode(r)?, - suggestion: if flags & 0x10 != 0 { - Some(match Ident::>::decode(r)?.as_str() { - "minecraft:ask_server" => Suggestion::AskServer, - "minecraft:all_recipes" => Suggestion::AllRecipes, - "minecraft:available_sounds" => Suggestion::AvailableSounds, - "minecraft:available_biomes" => Suggestion::AvailableBiomes, - "minecraft:summonable_entities" => Suggestion::SummonableEntities, - other => bail!("unknown command suggestion type of \"{other}\""), - }) - } else { - None - }, - }, - n => bail!("invalid node type of {n}"), - }; - - Ok(Self { - children, - data: node_data, - executable: flags & 0x04 != 0, - redirect_node, - }) - } -} - -impl Encode for Parser<'_> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - match self { - Parser::Bool => 0u8.encode(&mut w)?, - Parser::Float { min, max } => { - 1u8.encode(&mut w)?; - - (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; - - if let Some(min) = min { - min.encode(&mut w)?; - } - - if let Some(max) = max { - max.encode(&mut w)?; - } - } - Parser::Double { min, max } => { - 2u8.encode(&mut w)?; - - (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; - - if let Some(min) = min { - min.encode(&mut w)?; - } - - if let Some(max) = max { - max.encode(&mut w)?; - } - } - Parser::Integer { min, max } => { - 3u8.encode(&mut w)?; - - (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; - - if let Some(min) = min { - min.encode(&mut w)?; - } - - if let Some(max) = max { - max.encode(&mut w)?; - } - } - Parser::Long { min, max } => { - 4u8.encode(&mut w)?; - - (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; - - if let Some(min) = min { - min.encode(&mut w)?; - } - - if let Some(max) = max { - max.encode(&mut w)?; - } - } - Parser::String(arg) => { - 5u8.encode(&mut w)?; - arg.encode(&mut w)?; - } - Parser::Entity { - single, - only_players, - } => { - 6u8.encode(&mut w)?; - (*single as u8 | (*only_players as u8 * 0x2)).encode(&mut w)?; - } - Parser::GameProfile => 7u8.encode(&mut w)?, - Parser::BlockPos => 8u8.encode(&mut w)?, - Parser::ColumnPos => 9u8.encode(&mut w)?, - Parser::Vec3 => 10u8.encode(&mut w)?, - Parser::Vec2 => 11u8.encode(&mut w)?, - Parser::BlockState => 12u8.encode(&mut w)?, - Parser::BlockPredicate => 13u8.encode(&mut w)?, - Parser::ItemStack => 14u8.encode(&mut w)?, - Parser::ItemPredicate => 15u8.encode(&mut w)?, - Parser::Color => 16u8.encode(&mut w)?, - Parser::Component => 17u8.encode(&mut w)?, - Parser::Message => 18u8.encode(&mut w)?, - Parser::NbtCompoundTag => 19u8.encode(&mut w)?, - Parser::NbtTag => 20u8.encode(&mut w)?, - Parser::NbtPath => 21u8.encode(&mut w)?, - Parser::Objective => 22u8.encode(&mut w)?, - Parser::ObjectiveCriteria => 23u8.encode(&mut w)?, - Parser::Operation => 24u8.encode(&mut w)?, - Parser::Particle => 25u8.encode(&mut w)?, - Parser::Angle => 26u8.encode(&mut w)?, - Parser::Rotation => 27u8.encode(&mut w)?, - Parser::ScoreboardSlot => 28u8.encode(&mut w)?, - Parser::ScoreHolder { allow_multiple } => { - 29u8.encode(&mut w)?; - allow_multiple.encode(&mut w)?; - } - Parser::Swizzle => 30u8.encode(&mut w)?, - Parser::Team => 31u8.encode(&mut w)?, - Parser::ItemSlot => 32u8.encode(&mut w)?, - Parser::ResourceLocation => 33u8.encode(&mut w)?, - Parser::Function => 34u8.encode(&mut w)?, - Parser::EntityAnchor => 35u8.encode(&mut w)?, - Parser::IntRange => 36u8.encode(&mut w)?, - Parser::FloatRange => 37u8.encode(&mut w)?, - Parser::Dimension => 38u8.encode(&mut w)?, - Parser::GameMode => 39u8.encode(&mut w)?, - Parser::Time => 40u8.encode(&mut w)?, - Parser::ResourceOrTag { registry } => { - 41u8.encode(&mut w)?; - registry.encode(&mut w)?; - } - Parser::ResourceOrTagKey { registry } => { - 42u8.encode(&mut w)?; - registry.encode(&mut w)?; - } - Parser::Resource { registry } => { - 43u8.encode(&mut w)?; - registry.encode(&mut w)?; - } - Parser::ResourceKey { registry } => { - 44u8.encode(&mut w)?; - registry.encode(&mut w)?; - } - Parser::TemplateMirror => 45u8.encode(&mut w)?, - Parser::TemplateRotation => 46u8.encode(&mut w)?, - Parser::Uuid => 47u8.encode(&mut w)?, - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for Parser<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - fn decode_min_max<'a, T: Decode<'a>>( - r: &mut &'a [u8], - ) -> anyhow::Result<(Option, Option)> { - let flags = u8::decode(r)?; - - let min = if flags & 0x1 != 0 { - Some(T::decode(r)?) - } else { - None - }; - - let max = if flags & 0x2 != 0 { - Some(T::decode(r)?) - } else { - None - }; - - Ok((min, max)) - } - - Ok(match u8::decode(r)? { - 0 => Self::Bool, - 1 => { - let (min, max) = decode_min_max(r)?; - Self::Float { min, max } - } - 2 => { - let (min, max) = decode_min_max(r)?; - Self::Double { min, max } - } - 3 => { - let (min, max) = decode_min_max(r)?; - Self::Integer { min, max } - } - 4 => { - let (min, max) = decode_min_max(r)?; - Self::Long { min, max } - } - 5 => Self::String(StringArg::decode(r)?), - 6 => { - let flags = u8::decode(r)?; - Self::Entity { - single: flags & 0x1 != 0, - only_players: flags & 0x2 != 0, - } - } - 7 => Self::GameProfile, - 8 => Self::BlockPos, - 9 => Self::ColumnPos, - 10 => Self::Vec3, - 11 => Self::Vec2, - 12 => Self::BlockState, - 13 => Self::BlockPredicate, - 14 => Self::ItemStack, - 15 => Self::ItemPredicate, - 16 => Self::Color, - 17 => Self::Component, - 18 => Self::Message, - 19 => Self::NbtCompoundTag, - 20 => Self::NbtTag, - 21 => Self::NbtPath, - 22 => Self::Objective, - 23 => Self::ObjectiveCriteria, - 24 => Self::Operation, - 25 => Self::Particle, - 26 => Self::Angle, - 27 => Self::Rotation, - 28 => Self::ScoreboardSlot, - 29 => Self::ScoreHolder { - allow_multiple: bool::decode(r)?, - }, - 30 => Self::Swizzle, - 31 => Self::Team, - 32 => Self::ItemSlot, - 33 => Self::ResourceLocation, - 34 => Self::Function, - 35 => Self::EntityAnchor, - 36 => Self::IntRange, - 37 => Self::FloatRange, - 38 => Self::Dimension, - 39 => Self::GameMode, - 40 => Self::Time, - 41 => Self::ResourceOrTag { - registry: Ident::decode(r)?, - }, - 42 => Self::ResourceOrTagKey { - registry: Ident::decode(r)?, - }, - 43 => Self::Resource { - registry: Ident::decode(r)?, - }, - 44 => Self::ResourceKey { - registry: Ident::decode(r)?, - }, - 45 => Self::TemplateMirror, - 46 => Self::TemplateRotation, - 47 => Self::Uuid, - n => bail!("unknown command parser ID of {n}"), - }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/cooldown_update.rs b/crates/valence_core/src/packet/s2c/play/cooldown_update.rs deleted file mode 100644 index d6d78e83a..000000000 --- a/crates/valence_core/src/packet/s2c/play/cooldown_update.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct CooldownUpdateS2c { - pub item_id: VarInt, - pub cooldown_ticks: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/craft_failed_response.rs b/crates/valence_core/src/packet/s2c/play/craft_failed_response.rs deleted file mode 100644 index 5e4af757c..000000000 --- a/crates/valence_core/src/packet/s2c/play/craft_failed_response.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CraftFailedResponseS2c<'a> { - pub window_id: u8, - pub recipe: Ident>, -} diff --git a/crates/valence_core/src/packet/s2c/play/custom_payload.rs b/crates/valence_core/src/packet/s2c/play/custom_payload.rs deleted file mode 100644 index 7dfdc9052..000000000 --- a/crates/valence_core/src/packet/s2c/play/custom_payload.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::raw::RawBytes; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct CustomPayloadS2c<'a> { - pub channel: Ident>, - pub data: RawBytes<'a>, -} diff --git a/crates/valence_core/src/packet/s2c/play/damage_tilt.rs b/crates/valence_core/src/packet/s2c/play/damage_tilt.rs deleted file mode 100644 index 730530530..000000000 --- a/crates/valence_core/src/packet/s2c/play/damage_tilt.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct DamageTiltS2c { - /// The ID of the entity taking damage. - pub entity_id: VarInt, - /// The direction the damage is coming from in relation to the entity. - pub yaw: f32, -} diff --git a/crates/valence_core/src/packet/s2c/play/death_message.rs b/crates/valence_core/src/packet/s2c/play/death_message.rs deleted file mode 100644 index fdf2333eb..000000000 --- a/crates/valence_core/src/packet/s2c/play/death_message.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct DeathMessageS2c<'a> { - pub player_id: VarInt, - /// Killer's entity ID, -1 if no killer - pub entity_id: i32, - pub message: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/play/difficulty.rs b/crates/valence_core/src/packet/s2c/play/difficulty.rs deleted file mode 100644 index ada64a0c7..000000000 --- a/crates/valence_core/src/packet/s2c/play/difficulty.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::difficulty::Difficulty; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct DifficultyS2c { - pub difficulty: Difficulty, - pub locked: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/disconnect.rs b/crates/valence_core/src/packet/s2c/play/disconnect.rs deleted file mode 100644 index bd30e6c75..000000000 --- a/crates/valence_core/src/packet/s2c/play/disconnect.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct DisconnectS2c<'a> { - pub reason: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/play/end_combat.rs b/crates/valence_core/src/packet/s2c/play/end_combat.rs deleted file mode 100644 index 035907a7a..000000000 --- a/crates/valence_core/src/packet/s2c/play/end_combat.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -/// Unused by notchian clients. -#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] -pub struct EndCombatS2c { - pub duration: VarInt, - pub entity_id: i32, -} diff --git a/crates/valence_core/src/packet/s2c/play/enter_combat.rs b/crates/valence_core/src/packet/s2c/play/enter_combat.rs deleted file mode 100644 index 175335547..000000000 --- a/crates/valence_core/src/packet/s2c/play/enter_combat.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::packet::{Decode, Encode}; - -/// Unused by notchian clients. -#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] -pub struct EnterCombatS2c; diff --git a/crates/valence_core/src/packet/s2c/play/entities_destroy.rs b/crates/valence_core/src/packet/s2c/play/entities_destroy.rs deleted file mode 100644 index 0fce6126c..000000000 --- a/crates/valence_core/src/packet/s2c/play/entities_destroy.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct EntitiesDestroyS2c<'a> { - pub entity_ids: Cow<'a, [VarInt]>, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_animation.rs b/crates/valence_core/src/packet/s2c/play/entity_animation.rs deleted file mode 100644 index dcec06d49..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_animation.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntityAnimationS2c { - pub entity_id: VarInt, - pub animation: u8, // TODO: use Animation enum. -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_attach.rs b/crates/valence_core/src/packet/s2c/play/entity_attach.rs deleted file mode 100644 index c3a967942..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_attach.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntityAttachS2c { - pub attached_entity_id: i32, - pub holding_entity_id: i32, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_attributes.rs b/crates/valence_core/src/packet/s2c/play/entity_attributes.rs deleted file mode 100644 index 0cad84ed9..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_attributes.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::borrow::Cow; - -use uuid::Uuid; - -use crate::ident::Ident; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct EntityAttributesS2c<'a> { - pub entity_id: VarInt, - pub properties: Vec>, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct AttributeProperty<'a> { - pub key: Ident>, - pub value: f64, - pub modifiers: Vec, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct AttributeModifier { - pub uuid: Uuid, - pub amount: f64, - pub operation: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_damage.rs b/crates/valence_core/src/packet/s2c/play/entity_damage.rs deleted file mode 100644 index 5440398a6..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_damage.rs +++ /dev/null @@ -1,25 +0,0 @@ -use glam::DVec3; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntityDamageS2c { - /// The ID of the entity taking damage - pub entity_id: VarInt, - /// The ID of the type of damage taken - pub source_type_id: VarInt, - /// The ID + 1 of the entity responsible for the damage, if present. If not - /// present, the value is 0 - pub source_cause_id: VarInt, - /// The ID + 1 of the entity that directly dealt the damage, if present. If - /// not present, the value is 0. If this field is present: - /// * and damage was dealt indirectly, such as by the use of a projectile, - /// this field will contain the ID of such projectile; - /// * and damage was dealt dirctly, such as by manually attacking, this - /// field will contain the same value as Source Cause ID. - pub source_direct_id: VarInt, - /// The Notchian server sends the Source Position when the damage was dealt - /// by the /damage command and a position was specified - pub source_pos: Option, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_equipment_update.rs b/crates/valence_core/src/packet/s2c/play/entity_equipment_update.rs deleted file mode 100644 index 4473d12c9..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_equipment_update.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::io::Write; - -use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, PartialEq, Debug)] -pub struct EntityEquipmentUpdateS2c { - pub entity_id: VarInt, - pub equipment: Vec, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct EquipmentEntry { - pub slot: i8, - pub item: Option, -} - -impl Encode for EntityEquipmentUpdateS2c { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - self.entity_id.encode(&mut w)?; - - for i in 0..self.equipment.len() { - let slot = self.equipment[i].slot; - if i != self.equipment.len() - 1 { - (slot | -128).encode(&mut w)?; - } else { - slot.encode(&mut w)?; - } - self.equipment[i].item.encode(&mut w)?; - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for EntityEquipmentUpdateS2c { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let entity_id = VarInt::decode(r)?; - - let mut equipment = vec![]; - - loop { - let slot = i8::decode(r)?; - let item = Option::::decode(r)?; - equipment.push(EquipmentEntry { - slot: slot & 127, - item, - }); - if slot & -128 == 0 { - break; - } - } - - Ok(Self { - entity_id, - equipment, - }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_move.rs b/crates/valence_core/src/packet/s2c/play/entity_move.rs deleted file mode 100644 index 9777d3813..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_move.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::packet::byte_angle::ByteAngle; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct MoveRelative { - pub entity_id: VarInt, - pub delta: [i16; 3], - pub on_ground: bool, -} - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct RotateAndMoveRelative { - pub entity_id: VarInt, - pub delta: [i16; 3], - pub yaw: ByteAngle, - pub pitch: ByteAngle, - pub on_ground: bool, -} - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct Rotate { - pub entity_id: VarInt, - pub yaw: ByteAngle, - pub pitch: ByteAngle, - pub on_ground: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_passengers_set.rs b/crates/valence_core/src/packet/s2c/play/entity_passengers_set.rs deleted file mode 100644 index f0f2157b5..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_passengers_set.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct EntityPassengersSetS2c { - /// Vehicle's entity id - pub entity_id: VarInt, - pub passengers: Vec, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_position.rs b/crates/valence_core/src/packet/s2c/play/entity_position.rs deleted file mode 100644 index 8024c69c4..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_position.rs +++ /dev/null @@ -1,14 +0,0 @@ -use glam::DVec3; - -use crate::packet::byte_angle::ByteAngle; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntityPositionS2c { - pub entity_id: VarInt, - pub position: DVec3, - pub yaw: ByteAngle, - pub pitch: ByteAngle, - pub on_ground: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_set_head_yaw.rs b/crates/valence_core/src/packet/s2c/play/entity_set_head_yaw.rs deleted file mode 100644 index dc7174b8c..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_set_head_yaw.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::byte_angle::ByteAngle; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntitySetHeadYawS2c { - pub entity_id: VarInt, - pub head_yaw: ByteAngle, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_spawn.rs b/crates/valence_core/src/packet/s2c/play/entity_spawn.rs deleted file mode 100644 index b8b5fef24..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_spawn.rs +++ /dev/null @@ -1,19 +0,0 @@ -use glam::DVec3; -use uuid::Uuid; - -use crate::packet::byte_angle::ByteAngle; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntitySpawnS2c { - pub entity_id: VarInt, - pub object_uuid: Uuid, - pub kind: VarInt, - pub position: DVec3, - pub pitch: ByteAngle, - pub yaw: ByteAngle, - pub head_yaw: ByteAngle, - pub data: VarInt, - pub velocity: [i16; 3], -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_status.rs b/crates/valence_core/src/packet/s2c/play/entity_status.rs deleted file mode 100644 index c876b1a55..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_status.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntityStatusS2c { - pub entity_id: i32, - pub entity_status: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_status_effect.rs b/crates/valence_core/src/packet/s2c/play/entity_status_effect.rs deleted file mode 100644 index aca8a8191..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_status_effect.rs +++ /dev/null @@ -1,25 +0,0 @@ -use bitfield_struct::bitfield; -use valence_nbt::Compound; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct EntityStatusEffectS2c { - pub entity_id: VarInt, - pub effect_id: VarInt, - pub amplifier: u8, - pub duration: VarInt, - pub flags: Flags, - pub factor_codec: Option, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct Flags { - pub is_ambient: bool, - pub show_particles: bool, - pub show_icon: bool, - #[bits(5)] - _pad: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_tracker_update.rs b/crates/valence_core/src/packet/s2c/play/entity_tracker_update.rs deleted file mode 100644 index 55536b07a..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_tracker_update.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::raw::RawBytes; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntityTrackerUpdateS2c<'a> { - pub entity_id: VarInt, - pub metadata: RawBytes<'a>, -} diff --git a/crates/valence_core/src/packet/s2c/play/entity_velocity_update.rs b/crates/valence_core/src/packet/s2c/play/entity_velocity_update.rs deleted file mode 100644 index e4010eef7..000000000 --- a/crates/valence_core/src/packet/s2c/play/entity_velocity_update.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct EntityVelocityUpdateS2c { - pub entity_id: VarInt, - pub velocity: [i16; 3], -} diff --git a/crates/valence_core/src/packet/s2c/play/experience_bar_update.rs b/crates/valence_core/src/packet/s2c/play/experience_bar_update.rs deleted file mode 100644 index 92657b8ac..000000000 --- a/crates/valence_core/src/packet/s2c/play/experience_bar_update.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ExperienceBarUpdateS2c { - pub bar: f32, - pub level: VarInt, - pub total_xp: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/experience_orb_spawn.rs b/crates/valence_core/src/packet/s2c/play/experience_orb_spawn.rs deleted file mode 100644 index c249bf09b..000000000 --- a/crates/valence_core/src/packet/s2c/play/experience_orb_spawn.rs +++ /dev/null @@ -1,11 +0,0 @@ -use glam::DVec3; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ExperienceOrbSpawnS2c { - pub entity_id: VarInt, - pub position: DVec3, - pub count: i16, -} diff --git a/crates/valence_core/src/packet/s2c/play/explosion.rs b/crates/valence_core/src/packet/s2c/play/explosion.rs deleted file mode 100644 index 839244496..000000000 --- a/crates/valence_core/src/packet/s2c/play/explosion.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ExplosionS2c<'a> { - pub window_id: u8, - pub recipe: Ident>, - pub make_all: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/features.rs b/crates/valence_core/src/packet/s2c/play/features.rs deleted file mode 100644 index 10c124936..000000000 --- a/crates/valence_core/src/packet/s2c/play/features.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct FeaturesS2c<'a> { - pub features: Vec>>, -} diff --git a/crates/valence_core/src/packet/s2c/play/game_join.rs b/crates/valence_core/src/packet/s2c/play/game_join.rs deleted file mode 100644 index 91e1ce3bd..000000000 --- a/crates/valence_core/src/packet/s2c/play/game_join.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::borrow::Cow; - -use valence_nbt::Compound; - -use crate::game_mode::GameMode; -use crate::ident::Ident; -use crate::packet::global_pos::GlobalPos; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct GameJoinS2c<'a> { - pub entity_id: i32, - pub is_hardcore: bool, - pub game_mode: GameMode, - /// Same values as `game_mode` but with -1 to indicate no previous. - pub previous_game_mode: i8, - pub dimension_names: Vec>>, - pub registry_codec: Cow<'a, Compound>, - pub dimension_type_name: Ident>, - pub dimension_name: Ident>, - pub hashed_seed: i64, - pub max_players: VarInt, - pub view_distance: VarInt, - pub simulation_distance: VarInt, - pub reduced_debug_info: bool, - pub enable_respawn_screen: bool, - pub is_debug: bool, - pub is_flat: bool, - pub last_death_location: Option>, -} diff --git a/crates/valence_core/src/packet/s2c/play/game_message.rs b/crates/valence_core/src/packet/s2c/play/game_message.rs deleted file mode 100644 index d6f828380..000000000 --- a/crates/valence_core/src/packet/s2c/play/game_message.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct GameMessageS2c<'a> { - pub chat: Cow<'a, Text>, - /// Whether the message is in the actionbar or the chat. - pub overlay: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/game_state_change.rs b/crates/valence_core/src/packet/s2c/play/game_state_change.rs deleted file mode 100644 index 2c90e7983..000000000 --- a/crates/valence_core/src/packet/s2c/play/game_state_change.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct GameStateChangeS2c { - pub kind: GameEventKind, - pub value: f32, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum GameEventKind { - NoRespawnBlockAvailable, - EndRaining, - BeginRaining, - ChangeGameMode, - WinGame, - DemoEvent, - ArrowHitPlayer, - RainLevelChange, - ThunderLevelChange, - PlayPufferfishStingSound, - PlayElderGuardianMobAppearance, - EnableRespawnScreen, -} diff --git a/crates/valence_core/src/packet/s2c/play/health_update.rs b/crates/valence_core/src/packet/s2c/play/health_update.rs deleted file mode 100644 index 71a37a4f3..000000000 --- a/crates/valence_core/src/packet/s2c/play/health_update.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct HealthUpdateS2c { - pub health: f32, - pub food: VarInt, - pub food_saturation: f32, -} diff --git a/crates/valence_core/src/packet/s2c/play/inventory.rs b/crates/valence_core/src/packet/s2c/play/inventory.rs deleted file mode 100644 index c2c516e84..000000000 --- a/crates/valence_core/src/packet/s2c/play/inventory.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::borrow::Cow; - -use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct InventoryS2c<'a> { - pub window_id: u8, - pub state_id: VarInt, - pub slots: Cow<'a, [Option]>, - pub carried_item: Cow<'a, Option>, -} diff --git a/crates/valence_core/src/packet/s2c/play/item_pickup_animation.rs b/crates/valence_core/src/packet/s2c/play/item_pickup_animation.rs deleted file mode 100644 index f81e09683..000000000 --- a/crates/valence_core/src/packet/s2c/play/item_pickup_animation.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ItemPickupAnimationS2c { - pub collected_entity_id: VarInt, - pub collector_entity_id: VarInt, - pub pickup_item_count: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/keep_alive.rs b/crates/valence_core/src/packet/s2c/play/keep_alive.rs deleted file mode 100644 index 7968ef5fc..000000000 --- a/crates/valence_core/src/packet/s2c/play/keep_alive.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct KeepAliveS2c { - pub id: u64, -} diff --git a/crates/valence_core/src/packet/s2c/play/light_update.rs b/crates/valence_core/src/packet/s2c/play/light_update.rs deleted file mode 100644 index b071098ed..000000000 --- a/crates/valence_core/src/packet/s2c/play/light_update.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::packet::array::LengthPrefixedArray; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct LightUpdateS2c { - pub chunk_x: VarInt, - pub chunk_z: VarInt, - pub trust_edges: bool, - pub sky_light_mask: Vec, - pub block_light_mask: Vec, - pub empty_sky_light_mask: Vec, - pub empty_block_light_mask: Vec, - pub sky_light_arrays: Vec>, - pub block_light_arrays: Vec>, -} diff --git a/crates/valence_core/src/packet/s2c/play/look_at.rs b/crates/valence_core/src/packet/s2c/play/look_at.rs deleted file mode 100644 index e1589d854..000000000 --- a/crates/valence_core/src/packet/s2c/play/look_at.rs +++ /dev/null @@ -1,23 +0,0 @@ -use glam::DVec3; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] -pub struct LookAtS2c { - pub feet_or_eyes: FeetOrEyes, - pub target_position: DVec3, - pub entity_to_face: Option, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum FeetOrEyes { - Feet, - Eyes, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub struct LookAtEntity { - pub entity_id: VarInt, - pub feet_or_eyes: FeetOrEyes, -} diff --git a/crates/valence_core/src/packet/s2c/play/map_update.rs b/crates/valence_core/src/packet/s2c/play/map_update.rs deleted file mode 100644 index 381983bcc..000000000 --- a/crates/valence_core/src/packet/s2c/play/map_update.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, PartialEq, Debug)] -pub struct MapUpdateS2c<'a> { - pub map_id: VarInt, - pub scale: i8, - pub locked: bool, - pub icons: Option>>, - pub data: Option>, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct Icon<'a> { - pub icon_type: IconType, - /// In map coordinates; -128 for furthest left, +127 for furthest right - pub position: [i8; 2], - /// 0 is a vertical icon and increments by 22.5° - pub direction: i8, - pub display_name: Option>, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum IconType { - WhiteArrow, - GreenArrow, - RedArrow, - BlueArrow, - WhiteCross, - RedPointer, - WhiteCircle, - SmallWhiteCircle, - Mansion, - Temple, - WhiteBanner, - OrangeBanner, - MagentaBanner, - LightBlueBanner, - YellowBanner, - LimeBanner, - PinkBanner, - GrayBanner, - LightGrayBanner, - CyanBanner, - PurpleBanner, - BlueBanner, - BrownBanner, - GreenBanner, - RedBanner, - BlackBanner, - TreasureMarker, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode)] -pub struct Data<'a> { - pub columns: u8, - pub rows: u8, - pub position: [i8; 2], - pub data: &'a [u8], -} - -impl Encode for MapUpdateS2c<'_> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - self.map_id.encode(&mut w)?; - self.scale.encode(&mut w)?; - self.locked.encode(&mut w)?; - self.icons.encode(&mut w)?; - - match self.data { - None => 0u8.encode(&mut w)?, - Some(data) => data.encode(&mut w)?, - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for MapUpdateS2c<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let map_id = VarInt::decode(r)?; - let scale = i8::decode(r)?; - let locked = bool::decode(r)?; - let icons = >>>::decode(r)?; - let columns = u8::decode(r)?; - - let data = if columns > 0 { - let rows = u8::decode(r)?; - let position = <[i8; 2]>::decode(r)?; - let data = <&'a [u8]>::decode(r)?; - - Some(Data { - columns, - rows, - position, - data, - }) - } else { - None - }; - - Ok(Self { - map_id, - scale, - locked, - icons, - data, - }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/nbt_query_response.rs b/crates/valence_core/src/packet/s2c/play/nbt_query_response.rs deleted file mode 100644 index 884b7b2b0..000000000 --- a/crates/valence_core/src/packet/s2c/play/nbt_query_response.rs +++ /dev/null @@ -1,10 +0,0 @@ -use valence_nbt::Compound; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct NbtQueryResponseS2c { - pub transaction_id: VarInt, - pub nbt: Compound, -} diff --git a/crates/valence_core/src/packet/s2c/play/open_horse_screen.rs b/crates/valence_core/src/packet/s2c/play/open_horse_screen.rs deleted file mode 100644 index 858a5f9c7..000000000 --- a/crates/valence_core/src/packet/s2c/play/open_horse_screen.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct OpenHorseScreenS2c { - pub window_id: u8, - pub slot_count: VarInt, - pub entity_id: i32, -} diff --git a/crates/valence_core/src/packet/s2c/play/open_screen.rs b/crates/valence_core/src/packet/s2c/play/open_screen.rs deleted file mode 100644 index 5fdabd9dc..000000000 --- a/crates/valence_core/src/packet/s2c/play/open_screen.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum WindowType { - Generic9x1, - Generic9x2, - Generic9x3, - Generic9x4, - Generic9x5, - Generic9x6, - Generic3x3, - Anvil, - Beacon, - BlastFurnace, - BrewingStand, - Crafting, - Enchantment, - Furnace, - Grindstone, - Hopper, - Lectern, - Loom, - Merchant, - ShulkerBox, - Smithing, - Smoker, - Cartography, - Stonecutter, -} - -#[derive(Clone, Debug, Encode, Decode)] -pub struct OpenScreenS2c<'a> { - pub window_id: VarInt, - pub window_type: WindowType, - pub window_title: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/play/open_written_book.rs b/crates/valence_core/src/packet/s2c/play/open_written_book.rs deleted file mode 100644 index ea643b843..000000000 --- a/crates/valence_core/src/packet/s2c/play/open_written_book.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::hand::Hand; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct OpenWrittenBookS2c { - pub hand: Hand, -} diff --git a/crates/valence_core/src/packet/s2c/play/overlay_message.rs b/crates/valence_core/src/packet/s2c/play/overlay_message.rs deleted file mode 100644 index ae699266f..000000000 --- a/crates/valence_core/src/packet/s2c/play/overlay_message.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct OverlayMessageS2c<'a> { - pub action_bar_text: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/play/play_ping.rs b/crates/valence_core/src/packet/s2c/play/play_ping.rs deleted file mode 100644 index d541fecb1..000000000 --- a/crates/valence_core/src/packet/s2c/play/play_ping.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayPingS2c { - pub id: i32, -} diff --git a/crates/valence_core/src/packet/s2c/play/play_sound.rs b/crates/valence_core/src/packet/s2c/play/play_sound.rs deleted file mode 100644 index c0ddcba99..000000000 --- a/crates/valence_core/src/packet/s2c/play/play_sound.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use glam::IVec3; - -use crate::ident::Ident; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::sound::SoundCategory; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct PlaySoundS2c<'a> { - pub id: SoundId<'a>, - pub category: SoundCategory, - pub position: IVec3, - pub volume: f32, - pub pitch: f32, - pub seed: i64, -} - -#[derive(Clone, PartialEq, Debug)] -pub enum SoundId<'a> { - Direct { - id: Ident>, - range: Option, - }, - Reference { - id: VarInt, - }, -} - -impl Encode for SoundId<'_> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - match self { - SoundId::Direct { id, range } => { - VarInt(0).encode(&mut w)?; - id.encode(&mut w)?; - range.encode(&mut w)?; - } - SoundId::Reference { id } => VarInt(id.0 + 1).encode(&mut w)?, - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for SoundId<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let i = VarInt::decode(r)?.0; - - if i == 0 { - Ok(SoundId::Direct { - id: Ident::decode(r)?, - range: >::decode(r)?, - }) - } else { - Ok(SoundId::Reference { id: VarInt(i - 1) }) - } - } -} diff --git a/crates/valence_core/src/packet/s2c/play/play_sound_from_entity.rs b/crates/valence_core/src/packet/s2c/play/play_sound_from_entity.rs deleted file mode 100644 index b74c4423f..000000000 --- a/crates/valence_core/src/packet/s2c/play/play_sound_from_entity.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::sound::SoundCategory; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlaySoundFromEntityS2c { - pub id: VarInt, - pub category: SoundCategory, - pub entity_id: VarInt, - pub volume: f32, - pub pitch: f32, - pub seed: i64, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_abilities.rs b/crates/valence_core/src/packet/s2c/play/player_abilities.rs deleted file mode 100644 index a850383f6..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_abilities.rs +++ /dev/null @@ -1,21 +0,0 @@ -use bitfield_struct::bitfield; - -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct PlayerAbilitiesS2c { - pub flags: PlayerAbilitiesFlags, - pub flying_speed: f32, - pub fov_modifier: f32, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct PlayerAbilitiesFlags { - pub invulnerable: bool, - pub flying: bool, - pub allow_flying: bool, - pub instant_break: bool, - #[bits(4)] - _pad: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_action_response.rs b/crates/valence_core/src/packet/s2c/play/player_action_response.rs deleted file mode 100644 index ada903920..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_action_response.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerActionResponseS2c { - pub sequence: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_list_header.rs b/crates/valence_core/src/packet/s2c/play/player_list_header.rs deleted file mode 100644 index 6b24f07fe..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_list_header.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct PlayerListHeaderS2c<'a> { - pub header: Cow<'a, Text>, - pub footer: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_position_look.rs b/crates/valence_core/src/packet/s2c/play/player_position_look.rs deleted file mode 100644 index 15156e321..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_position_look.rs +++ /dev/null @@ -1,26 +0,0 @@ -use bitfield_struct::bitfield; -use glam::DVec3; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] -pub struct PlayerPositionLookS2c { - pub position: DVec3, - pub yaw: f32, - pub pitch: f32, - pub flags: Flags, - pub teleport_id: VarInt, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct Flags { - pub x: bool, - pub y: bool, - pub z: bool, - pub y_rot: bool, - pub x_rot: bool, - #[bits(3)] - _pad: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_remove.rs b/crates/valence_core/src/packet/s2c/play/player_remove.rs deleted file mode 100644 index c86847357..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_remove.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::borrow::Cow; - -use uuid::Uuid; - -use crate::packet::{Decode, Encode}; - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct PlayerRemoveS2c<'a> { - pub uuids: Cow<'a, [Uuid]>, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_respawn.rs b/crates/valence_core/src/packet/s2c/play/player_respawn.rs deleted file mode 100644 index fde8d8764..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_respawn.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::borrow::Cow; - -use crate::game_mode::GameMode; -use crate::ident::Ident; -use crate::packet::global_pos::GlobalPos; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct PlayerRespawnS2c<'a> { - pub dimension_type_name: Ident>, - pub dimension_name: Ident>, - pub hashed_seed: u64, - pub game_mode: GameMode, - pub previous_game_mode: i8, - pub is_debug: bool, - pub is_flat: bool, - pub copy_metadata: bool, - pub last_death_location: Option>, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_spawn.rs b/crates/valence_core/src/packet/s2c/play/player_spawn.rs deleted file mode 100644 index e8788584e..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_spawn.rs +++ /dev/null @@ -1,15 +0,0 @@ -use glam::DVec3; -use uuid::Uuid; - -use crate::packet::byte_angle::ByteAngle; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerSpawnS2c { - pub entity_id: VarInt, - pub player_uuid: Uuid, - pub position: DVec3, - pub yaw: ByteAngle, - pub pitch: ByteAngle, -} diff --git a/crates/valence_core/src/packet/s2c/play/player_spawn_position.rs b/crates/valence_core/src/packet/s2c/play/player_spawn_position.rs deleted file mode 100644 index e84c50dce..000000000 --- a/crates/valence_core/src/packet/s2c/play/player_spawn_position.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct PlayerSpawnPositionS2c { - pub position: BlockPos, - pub angle: f32, -} diff --git a/crates/valence_core/src/packet/s2c/play/profileless_chat_message.rs b/crates/valence_core/src/packet/s2c/play/profileless_chat_message.rs deleted file mode 100644 index 641b6e7b0..000000000 --- a/crates/valence_core/src/packet/s2c/play/profileless_chat_message.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ProfilelessChatMessageS2c<'a> { - pub message: Cow<'a, Text>, - pub chat_type: VarInt, - pub chat_type_name: Cow<'a, Text>, - pub target_name: Option>, -} diff --git a/crates/valence_core/src/packet/s2c/play/remove_entity_status_effect.rs b/crates/valence_core/src/packet/s2c/play/remove_entity_status_effect.rs deleted file mode 100644 index 202a7689e..000000000 --- a/crates/valence_core/src/packet/s2c/play/remove_entity_status_effect.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct RemoveEntityStatusEffectS2c { - pub entity_id: VarInt, - pub effect_id: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/remove_message.rs b/crates/valence_core/src/packet/s2c/play/remove_message.rs deleted file mode 100644 index 17cbf6386..000000000 --- a/crates/valence_core/src/packet/s2c/play/remove_message.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::message_signature::MessageSignature; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct RemoveMessageS2c<'a> { - pub signature: MessageSignature<'a>, -} diff --git a/crates/valence_core/src/packet/s2c/play/resource_pack_send.rs b/crates/valence_core/src/packet/s2c/play/resource_pack_send.rs deleted file mode 100644 index 1fc471e7f..000000000 --- a/crates/valence_core/src/packet/s2c/play/resource_pack_send.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct ResourcePackSendS2c<'a> { - pub url: &'a str, - pub hash: &'a str, - pub forced: bool, - pub prompt_message: Option>, -} diff --git a/crates/valence_core/src/packet/s2c/play/scoreboard_display.rs b/crates/valence_core/src/packet/s2c/play/scoreboard_display.rs deleted file mode 100644 index 11e5436d9..000000000 --- a/crates/valence_core/src/packet/s2c/play/scoreboard_display.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::team::TeamColor; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ScoreboardDisplayS2c<'a> { - pub position: ScoreboardPosition, - pub score_name: &'a str, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum ScoreboardPosition { - List, - Sidebar, - BelowName, - SidebarTeam(TeamColor), -} - -impl Encode for ScoreboardPosition { - fn encode(&self, w: impl std::io::Write) -> anyhow::Result<()> { - match self { - ScoreboardPosition::List => 0u8.encode(w), - ScoreboardPosition::Sidebar => 1u8.encode(w), - ScoreboardPosition::BelowName => 2u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Black) => 3u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::DarkBlue) => 4u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::DarkGreen) => 5u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::DarkCyan) => 6u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::DarkRed) => 7u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Purple) => 8u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Gold) => 9u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Gray) => 10u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::DarkGray) => 11u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Blue) => 12u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::BrightGreen) => 13u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Cyan) => 14u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Red) => 15u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Pink) => 16u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::Yellow) => 17u8.encode(w), - ScoreboardPosition::SidebarTeam(TeamColor::White) => 18u8.encode(w), - ScoreboardPosition::SidebarTeam(_) => { - Err(anyhow::anyhow!("Invalid scoreboard display position")) - } - } - } -} - -impl<'a> Decode<'a> for ScoreboardPosition { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let value = u8::decode(r)?; - match value { - 0 => Ok(ScoreboardPosition::List), - 1 => Ok(ScoreboardPosition::Sidebar), - 2 => Ok(ScoreboardPosition::BelowName), - 3 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Black)), - 4 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkBlue)), - 5 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkGreen)), - 6 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkCyan)), - 7 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkRed)), - 8 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Purple)), - 9 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Gold)), - 10 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Gray)), - 11 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkGray)), - 12 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Blue)), - 13 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::BrightGreen)), - 14 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Cyan)), - 15 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Red)), - 16 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Pink)), - 17 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Yellow)), - 18 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::White)), - _ => Err(anyhow::anyhow!("Invalid scoreboard display position")), - } - } -} diff --git a/crates/valence_core/src/packet/s2c/play/scoreboard_objective_update.rs b/crates/valence_core/src/packet/s2c/play/scoreboard_objective_update.rs deleted file mode 100644 index 719601df0..000000000 --- a/crates/valence_core/src/packet/s2c/play/scoreboard_objective_update.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ScoreboardObjectiveUpdateS2c<'a> { - pub objective_name: &'a str, - pub mode: Mode, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub enum Mode { - Create { - objective_display_name: Text, - render_type: RenderType, - }, - Remove, - Update { - objective_display_name: Text, - render_type: RenderType, - }, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum RenderType { - Integer, - Hearts, -} diff --git a/crates/valence_core/src/packet/s2c/play/scoreboard_player_update.rs b/crates/valence_core/src/packet/s2c/play/scoreboard_player_update.rs deleted file mode 100644 index ba4fa491b..000000000 --- a/crates/valence_core/src/packet/s2c/play/scoreboard_player_update.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ScoreboardPlayerUpdateS2c<'a> { - pub entity_name: &'a str, - pub action: Action<'a>, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub enum Action<'a> { - Update { - objective_name: &'a str, - objective_score: VarInt, - }, - Remove { - objective_name: &'a str, - }, -} diff --git a/crates/valence_core/src/packet/s2c/play/screen_handler_property_update.rs b/crates/valence_core/src/packet/s2c/play/screen_handler_property_update.rs deleted file mode 100644 index 68adf9bed..000000000 --- a/crates/valence_core/src/packet/s2c/play/screen_handler_property_update.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct ScreenHandlerPropertyUpdateS2c { - pub window_id: u8, - pub property: i16, - pub value: i16, -} diff --git a/crates/valence_core/src/packet/s2c/play/screen_handler_slot_update.rs b/crates/valence_core/src/packet/s2c/play/screen_handler_slot_update.rs deleted file mode 100644 index 3fc8ddddd..000000000 --- a/crates/valence_core/src/packet/s2c/play/screen_handler_slot_update.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::borrow::Cow; - -use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ScreenHandlerSlotUpdateS2c<'a> { - pub window_id: i8, - pub state_id: VarInt, - pub slot_idx: i16, - pub slot_data: Cow<'a, Option>, -} diff --git a/crates/valence_core/src/packet/s2c/play/select_advancement_tab.rs b/crates/valence_core/src/packet/s2c/play/select_advancement_tab.rs deleted file mode 100644 index c7bc1f72f..000000000 --- a/crates/valence_core/src/packet/s2c/play/select_advancement_tab.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct SelectAdvancementTabS2c<'a> { - pub identifier: Option>>, -} diff --git a/crates/valence_core/src/packet/s2c/play/server_metadata.rs b/crates/valence_core/src/packet/s2c/play/server_metadata.rs deleted file mode 100644 index 3a6ea1d57..000000000 --- a/crates/valence_core/src/packet/s2c/play/server_metadata.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct ServerMetadataS2c<'a> { - pub motd: Cow<'a, Text>, - pub icon: Option<&'a [u8]>, - pub enforce_secure_chat: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/set_camera_entity.rs b/crates/valence_core/src/packet/s2c/play/set_camera_entity.rs deleted file mode 100644 index 835a55598..000000000 --- a/crates/valence_core/src/packet/s2c/play/set_camera_entity.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct SetCameraEntityS2c { - pub entity_id: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/set_trade_offers.rs b/crates/valence_core/src/packet/s2c/play/set_trade_offers.rs deleted file mode 100644 index 231e1c679..000000000 --- a/crates/valence_core/src/packet/s2c/play/set_trade_offers.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct SetTradeOffersS2c { - pub window_id: VarInt, - pub trades: Vec, - pub villager_level: VarInt, - pub experience: VarInt, - pub is_regular_villager: bool, - pub can_restock: bool, -} - -#[derive(Clone, PartialEq, Debug, Encode, Decode)] -pub struct TradeOffer { - pub input_one: Option, - pub output_item: Option, - pub input_two: Option, - pub trade_disabled: bool, - pub number_of_trade_uses: i32, - pub max_trade_uses: i32, - pub xp: i32, - pub special_price: i32, - pub price_multiplier: f32, - pub demand: i32, -} diff --git a/crates/valence_core/src/packet/s2c/play/sign_editor_open.rs b/crates/valence_core/src/packet/s2c/play/sign_editor_open.rs deleted file mode 100644 index df1192efe..000000000 --- a/crates/valence_core/src/packet/s2c/play/sign_editor_open.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct SignEditorOpenS2c { - pub location: BlockPos, -} diff --git a/crates/valence_core/src/packet/s2c/play/simulation_distance.rs b/crates/valence_core/src/packet/s2c/play/simulation_distance.rs deleted file mode 100644 index 372997674..000000000 --- a/crates/valence_core/src/packet/s2c/play/simulation_distance.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct SimulationDistanceS2c { - pub simulation_distance: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/statistics.rs b/crates/valence_core/src/packet/s2c/play/statistics.rs deleted file mode 100644 index c026ad4de..000000000 --- a/crates/valence_core/src/packet/s2c/play/statistics.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct StatisticsS2c { - pub statistics: Vec, -} - -#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] -pub struct Statistic { - pub category_id: VarInt, - pub statistic_id: VarInt, - pub value: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/stop_sound.rs b/crates/valence_core/src/packet/s2c/play/stop_sound.rs deleted file mode 100644 index af6bf279d..000000000 --- a/crates/valence_core/src/packet/s2c/play/stop_sound.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use crate::ident::Ident; -use crate::packet::{Decode, Encode}; -use crate::sound::SoundCategory; - -#[derive(Clone, PartialEq, Debug)] -pub struct StopSoundS2c<'a> { - pub source: Option, - pub sound: Option>>, -} - -impl Encode for StopSoundS2c<'_> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - match (self.source, self.sound.as_ref()) { - (Some(source), Some(sound)) => { - 3i8.encode(&mut w)?; - source.encode(&mut w)?; - sound.encode(&mut w)?; - } - (None, Some(sound)) => { - 2i8.encode(&mut w)?; - sound.encode(&mut w)?; - } - (Some(source), None) => { - 1i8.encode(&mut w)?; - source.encode(&mut w)?; - } - _ => 0i8.encode(&mut w)?, - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for StopSoundS2c<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let (source, sound) = match i8::decode(r)? { - 3 => ( - Some(SoundCategory::decode(r)?), - Some(>>::decode(r)?), - ), - 2 => (None, Some(>>::decode(r)?)), - 1 => (Some(SoundCategory::decode(r)?), None), - _ => (None, None), - }; - - Ok(Self { source, sound }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/subtitle.rs b/crates/valence_core/src/packet/s2c/play/subtitle.rs deleted file mode 100644 index 575defa6c..000000000 --- a/crates/valence_core/src/packet/s2c/play/subtitle.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct SubtitleS2c<'a> { - pub subtitle_text: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/play/synchronize_recipes.rs b/crates/valence_core/src/packet/s2c/play/synchronize_recipes.rs deleted file mode 100644 index cc3a38f6f..000000000 --- a/crates/valence_core/src/packet/s2c/play/synchronize_recipes.rs +++ /dev/null @@ -1,414 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use anyhow::{bail, ensure}; - -use crate::ident::Ident; -use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct SynchronizeRecipesS2c<'a> { - // TODO: this should be a Vec> - pub recipes: crate::packet::raw::RawBytes<'a>, -} - -#[derive(Clone, PartialEq, Debug)] -pub enum Recipe<'a> { - CraftingShapeless { - recipe_id: Ident>, - group: &'a str, - category: CraftingCategory, - ingredients: Vec, - result: Option, - }, - CraftingShaped { - recipe_id: Ident>, - width: VarInt, - height: VarInt, - group: &'a str, - category: CraftingCategory, - ingredients: Vec, - result: Option, - }, - CraftingSpecial { - kind: SpecialCraftingKind, - recipe_id: Ident>, - category: CraftingCategory, - }, - Smelting { - recipe_id: Ident>, - group: &'a str, - category: SmeltCategory, - ingredient: Ingredient, - result: Option, - experience: f32, - cooking_time: VarInt, - }, - Blasting { - recipe_id: Ident>, - group: &'a str, - category: SmeltCategory, - ingredient: Ingredient, - result: Option, - experience: f32, - cooking_time: VarInt, - }, - Smoking { - recipe_id: Ident>, - group: &'a str, - category: SmeltCategory, - ingredient: Ingredient, - result: Option, - experience: f32, - cooking_time: VarInt, - }, - CampfireCooking { - recipe_id: Ident>, - group: &'a str, - category: SmeltCategory, - ingredient: Ingredient, - result: Option, - experience: f32, - cooking_time: VarInt, - }, - Stonecutting { - recipe_id: Ident>, - group: &'a str, - ingredient: Ingredient, - result: Option, - }, - Smithing { - recipe_id: Ident>, - base: Ingredient, - addition: Ingredient, - result: Option, - }, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum SpecialCraftingKind { - ArmorDye, - BookCloning, - MapCloning, - MapExtending, - FireworkRocket, - FireworkStar, - FireworkStarFade, - RepairItem, - TippedArrow, - BannerDuplicate, - BannerAddPattern, - ShieldDecoration, - ShulkerBoxColoring, - SuspiciousStew, -} - -/// Any item in the Vec may be used for the recipe. -pub type Ingredient = Vec>; - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum CraftingCategory { - Building, - Redstone, - Equipment, - Misc, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum SmeltCategory { - Food, - Blocks, - Misc, -} - -impl<'a> Encode for Recipe<'a> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - match self { - Recipe::CraftingShapeless { - recipe_id, - group, - category, - ingredients, - result, - } => { - "crafting_shapeless".encode(&mut w)?; - recipe_id.encode(&mut w)?; - group.encode(&mut w)?; - category.encode(&mut w)?; - ingredients.encode(&mut w)?; - result.encode(w) - } - Recipe::CraftingShaped { - recipe_id, - width, - height, - group, - category, - ingredients, - result, - } => { - "crafting_shaped".encode(&mut w)?; - recipe_id.encode(&mut w)?; - width.encode(&mut w)?; - height.encode(&mut w)?; - group.encode(&mut w)?; - category.encode(&mut w)?; - - ensure!( - (width.0 as usize).saturating_mul(height.0 as usize) == ingredients.len(), - "width * height must be equal to the number of ingredients" - ); - - for ing in ingredients { - ing.encode(&mut w)?; - } - - result.encode(w) - } - Recipe::CraftingSpecial { - kind, - recipe_id, - category, - } => { - match kind { - SpecialCraftingKind::ArmorDye => "crafting_special_armordye", - SpecialCraftingKind::BookCloning => "crafting_special_bookcloning", - SpecialCraftingKind::MapCloning => "crafting_special_mapcloning", - SpecialCraftingKind::MapExtending => "crafting_special_mapextending", - SpecialCraftingKind::FireworkRocket => "crafting_special_firework_rocket", - SpecialCraftingKind::FireworkStar => "crafting_special_firework_star", - SpecialCraftingKind::FireworkStarFade => "crafting_special_firework_star_fade", - SpecialCraftingKind::RepairItem => "crafting_special_repairitem", - SpecialCraftingKind::TippedArrow => "crafting_special_tippedarrow", - SpecialCraftingKind::BannerDuplicate => "crafting_special_bannerduplicate", - SpecialCraftingKind::BannerAddPattern => "crafting_special_banneraddpattern", - SpecialCraftingKind::ShieldDecoration => "crafting_special_shielddecoration", - SpecialCraftingKind::ShulkerBoxColoring => { - "crafting_special_shulkerboxcoloring" - } - SpecialCraftingKind::SuspiciousStew => "crafting_special_suspiciousstew", - } - .encode(&mut w)?; - recipe_id.encode(&mut w)?; - category.encode(w) - } - Recipe::Smelting { - recipe_id, - group, - category, - ingredient, - result, - experience, - cooking_time, - } => { - "smelting".encode(&mut w)?; - recipe_id.encode(&mut w)?; - group.encode(&mut w)?; - category.encode(&mut w)?; - ingredient.encode(&mut w)?; - result.encode(&mut w)?; - experience.encode(&mut w)?; - cooking_time.encode(w) - } - Recipe::Blasting { - recipe_id, - group, - category, - ingredient, - result, - experience, - cooking_time, - } => { - "blasting".encode(&mut w)?; - recipe_id.encode(&mut w)?; - group.encode(&mut w)?; - category.encode(&mut w)?; - ingredient.encode(&mut w)?; - result.encode(&mut w)?; - experience.encode(&mut w)?; - cooking_time.encode(w) - } - Recipe::Smoking { - recipe_id, - group, - category, - ingredient, - result, - experience, - cooking_time, - } => { - "smoking".encode(&mut w)?; - recipe_id.encode(&mut w)?; - group.encode(&mut w)?; - category.encode(&mut w)?; - ingredient.encode(&mut w)?; - result.encode(&mut w)?; - experience.encode(&mut w)?; - cooking_time.encode(w) - } - Recipe::CampfireCooking { - recipe_id, - group, - category, - ingredient, - result, - experience, - cooking_time, - } => { - "campfire_cooking".encode(&mut w)?; - recipe_id.encode(&mut w)?; - group.encode(&mut w)?; - category.encode(&mut w)?; - ingredient.encode(&mut w)?; - result.encode(&mut w)?; - experience.encode(&mut w)?; - cooking_time.encode(w) - } - Recipe::Stonecutting { - recipe_id, - group, - ingredient, - result, - } => { - "stonecutting".encode(&mut w)?; - recipe_id.encode(&mut w)?; - group.encode(&mut w)?; - ingredient.encode(&mut w)?; - result.encode(w) - } - Recipe::Smithing { - recipe_id, - base, - addition, - result, - } => { - "smithing".encode(&mut w)?; - recipe_id.encode(&mut w)?; - base.encode(&mut w)?; - addition.encode(&mut w)?; - result.encode(w) - } - } - } -} - -impl<'a> Decode<'a> for Recipe<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - Ok(match Ident::>::decode(r)?.as_str() { - "minecraft:crafting_shapeless" => Self::CraftingShapeless { - recipe_id: Decode::decode(r)?, - group: Decode::decode(r)?, - category: Decode::decode(r)?, - ingredients: Decode::decode(r)?, - result: Decode::decode(r)?, - }, - "minecraft:crafting_shaped" => { - let recipe_id = Ident::decode(r)?; - let width = VarInt::decode(r)?.0; - let height = VarInt::decode(r)?.0; - let group = <&str>::decode(r)?; - let category = CraftingCategory::decode(r)?; - - let mut ingredients = Vec::new(); - for _ in 0..width.saturating_mul(height) { - ingredients.push(Ingredient::decode(r)?); - } - - Self::CraftingShaped { - recipe_id, - width: VarInt(width), - height: VarInt(height), - group, - category, - ingredients, - result: Decode::decode(r)?, - } - } - "minecraft:smelting" => Self::Smelting { - recipe_id: Decode::decode(r)?, - group: Decode::decode(r)?, - category: Decode::decode(r)?, - ingredient: Decode::decode(r)?, - result: Decode::decode(r)?, - experience: Decode::decode(r)?, - cooking_time: Decode::decode(r)?, - }, - "minecraft:blasting" => Self::Blasting { - recipe_id: Decode::decode(r)?, - group: Decode::decode(r)?, - category: Decode::decode(r)?, - ingredient: Decode::decode(r)?, - result: Decode::decode(r)?, - experience: Decode::decode(r)?, - cooking_time: Decode::decode(r)?, - }, - "minecraft:smoking" => Self::Smoking { - recipe_id: Decode::decode(r)?, - group: Decode::decode(r)?, - category: Decode::decode(r)?, - ingredient: Decode::decode(r)?, - result: Decode::decode(r)?, - experience: Decode::decode(r)?, - cooking_time: Decode::decode(r)?, - }, - "minecraft:campfire_cooking" => Self::CampfireCooking { - recipe_id: Decode::decode(r)?, - group: Decode::decode(r)?, - category: Decode::decode(r)?, - ingredient: Decode::decode(r)?, - result: Decode::decode(r)?, - experience: Decode::decode(r)?, - cooking_time: Decode::decode(r)?, - }, - "minecraft:stonecutting" => Self::Stonecutting { - recipe_id: Decode::decode(r)?, - group: Decode::decode(r)?, - ingredient: Decode::decode(r)?, - result: Decode::decode(r)?, - }, - "minecraft:smithing" => Self::Smithing { - recipe_id: Decode::decode(r)?, - base: Decode::decode(r)?, - addition: Decode::decode(r)?, - result: Decode::decode(r)?, - }, - other => Self::CraftingSpecial { - kind: match other { - "minecraft:crafting_special_armordye" => SpecialCraftingKind::ArmorDye, - "minecraft:crafting_special_bookcloning" => SpecialCraftingKind::BookCloning, - "minecraft:crafting_special_mapcloning" => SpecialCraftingKind::MapCloning, - "minecraft:crafting_special_mapextending" => SpecialCraftingKind::MapExtending, - "minecraft:crafting_special_firework_rocket" => { - SpecialCraftingKind::FireworkRocket - } - "minecraft:crafting_special_firework_star" => SpecialCraftingKind::FireworkStar, - "minecraft:crafting_special_firework_star_fade" => { - SpecialCraftingKind::FireworkStarFade - } - "minecraft:crafting_special_repairitem" => SpecialCraftingKind::RepairItem, - "minecraft:crafting_special_tippedarrow" => SpecialCraftingKind::TippedArrow, - "minecraft:crafting_special_bannerduplicate" => { - SpecialCraftingKind::BannerDuplicate - } - "minecraft:crafting_special_banneraddpattern" => { - SpecialCraftingKind::BannerAddPattern - } - "minecraft:crafting_special_shielddecoration" => { - SpecialCraftingKind::ShieldDecoration - } - "minecraft:crafting_special_shulkerboxcoloring" => { - SpecialCraftingKind::ShulkerBoxColoring - } - "minecraft:crafting_special_suspiciousstew" => { - SpecialCraftingKind::SuspiciousStew - } - _ => bail!("unknown recipe type \"{other}\""), - }, - recipe_id: Decode::decode(r)?, - category: CraftingCategory::decode(r)?, - }, - }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/synchronize_tags.rs b/crates/valence_core/src/packet/s2c/play/synchronize_tags.rs deleted file mode 100644 index 9502a8cb4..000000000 --- a/crates/valence_core/src/packet/s2c/play/synchronize_tags.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::borrow::Cow; - -use crate::ident::Ident; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct SynchronizeTagsS2c<'a> { - pub tags: Vec>, -} - -#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub struct TagGroup<'a> { - pub kind: Ident>, - pub tags: Vec>, -} - -#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub struct Tag<'a> { - pub name: Ident>, - pub entries: Vec, -} diff --git a/crates/valence_core/src/packet/s2c/play/team.rs b/crates/valence_core/src/packet/s2c/play/team.rs deleted file mode 100644 index 189a44d4b..000000000 --- a/crates/valence_core/src/packet/s2c/play/team.rs +++ /dev/null @@ -1,232 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use anyhow::bail; -use bitfield_struct::bitfield; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct TeamS2c<'a> { - pub team_name: &'a str, - pub mode: Mode<'a>, -} - -#[derive(Clone, PartialEq, Debug)] -pub enum Mode<'a> { - CreateTeam { - team_display_name: Cow<'a, Text>, - friendly_flags: TeamFlags, - name_tag_visibility: NameTagVisibility, - collision_rule: CollisionRule, - team_color: TeamColor, - team_prefix: Cow<'a, Text>, - team_suffix: Cow<'a, Text>, - entities: Vec<&'a str>, - }, - RemoveTeam, - UpdateTeamInfo { - team_display_name: Cow<'a, Text>, - friendly_flags: TeamFlags, - name_tag_visibility: NameTagVisibility, - collision_rule: CollisionRule, - team_color: TeamColor, - team_prefix: Cow<'a, Text>, - team_suffix: Cow<'a, Text>, - }, - AddEntities { - entities: Vec<&'a str>, - }, - RemoveEntities { - entities: Vec<&'a str>, - }, -} - -#[bitfield(u8)] -#[derive(PartialEq, Eq, Encode, Decode)] -pub struct TeamFlags { - pub friendly_fire: bool, - pub see_invisible_teammates: bool, - #[bits(6)] - _pad: u8, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum NameTagVisibility { - Always, - Never, - HideForOtherTeams, - HideForOwnTeam, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum CollisionRule { - Always, - Never, - PushOtherTeams, - PushOwnTeam, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum TeamColor { - Black, - DarkBlue, - DarkGreen, - DarkCyan, - DarkRed, - Purple, - Gold, - Gray, - DarkGray, - Blue, - BrightGreen, - Cyan, - Red, - Pink, - Yellow, - White, - Obfuscated, - Bold, - Strikethrough, - Underlined, - Italic, - Reset, -} - -impl Encode for Mode<'_> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - match self { - Mode::CreateTeam { - team_display_name, - friendly_flags, - name_tag_visibility, - collision_rule, - team_color, - team_prefix, - team_suffix, - entities, - } => { - 0i8.encode(&mut w)?; - team_display_name.encode(&mut w)?; - friendly_flags.encode(&mut w)?; - match name_tag_visibility { - NameTagVisibility::Always => "always", - NameTagVisibility::Never => "never", - NameTagVisibility::HideForOtherTeams => "hideForOtherTeams", - NameTagVisibility::HideForOwnTeam => "hideForOwnTeam", - } - .encode(&mut w)?; - match collision_rule { - CollisionRule::Always => "always", - CollisionRule::Never => "never", - CollisionRule::PushOtherTeams => "pushOtherTeams", - CollisionRule::PushOwnTeam => "pushOwnTeam", - } - .encode(&mut w)?; - team_color.encode(&mut w)?; - team_prefix.encode(&mut w)?; - team_suffix.encode(&mut w)?; - entities.encode(&mut w)?; - } - Mode::RemoveTeam => 1i8.encode(&mut w)?, - Mode::UpdateTeamInfo { - team_display_name, - friendly_flags, - name_tag_visibility, - collision_rule, - team_color, - team_prefix, - team_suffix, - } => { - 2i8.encode(&mut w)?; - team_display_name.encode(&mut w)?; - friendly_flags.encode(&mut w)?; - match name_tag_visibility { - NameTagVisibility::Always => "always", - NameTagVisibility::Never => "never", - NameTagVisibility::HideForOtherTeams => "hideForOtherTeams", - NameTagVisibility::HideForOwnTeam => "hideForOwnTeam", - } - .encode(&mut w)?; - match collision_rule { - CollisionRule::Always => "always", - CollisionRule::Never => "never", - CollisionRule::PushOtherTeams => "pushOtherTeams", - CollisionRule::PushOwnTeam => "pushOwnTeam", - } - .encode(&mut w)?; - team_color.encode(&mut w)?; - team_prefix.encode(&mut w)?; - team_suffix.encode(&mut w)?; - } - Mode::AddEntities { entities } => { - 3i8.encode(&mut w)?; - entities.encode(&mut w)?; - } - Mode::RemoveEntities { entities } => { - 4i8.encode(&mut w)?; - entities.encode(&mut w)?; - } - } - Ok(()) - } -} - -impl<'a> Decode<'a> for Mode<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - Ok(match i8::decode(r)? { - 0 => Self::CreateTeam { - team_display_name: Decode::decode(r)?, - friendly_flags: Decode::decode(r)?, - name_tag_visibility: match <&str>::decode(r)? { - "always" => NameTagVisibility::Always, - "never" => NameTagVisibility::Never, - "hideForOtherTeams" => NameTagVisibility::HideForOtherTeams, - "hideForOwnTeam" => NameTagVisibility::HideForOwnTeam, - other => bail!("unknown name tag visibility type \"{other}\""), - }, - collision_rule: match <&str>::decode(r)? { - "always" => CollisionRule::Always, - "never" => CollisionRule::Never, - "pushOtherTeams" => CollisionRule::PushOtherTeams, - "pushOwnTeam" => CollisionRule::PushOwnTeam, - other => bail!("unknown collision rule type \"{other}\""), - }, - team_color: Decode::decode(r)?, - team_prefix: Decode::decode(r)?, - team_suffix: Decode::decode(r)?, - entities: Decode::decode(r)?, - }, - 1 => Self::RemoveTeam, - 2 => Self::UpdateTeamInfo { - team_display_name: Decode::decode(r)?, - friendly_flags: Decode::decode(r)?, - name_tag_visibility: match <&str>::decode(r)? { - "always" => NameTagVisibility::Always, - "never" => NameTagVisibility::Never, - "hideForOtherTeams" => NameTagVisibility::HideForOtherTeams, - "hideForOwnTeam" => NameTagVisibility::HideForOwnTeam, - other => bail!("unknown name tag visibility type \"{other}\""), - }, - collision_rule: match <&str>::decode(r)? { - "always" => CollisionRule::Always, - "never" => CollisionRule::Never, - "pushOtherTeams" => CollisionRule::PushOtherTeams, - "pushOwnTeam" => CollisionRule::PushOwnTeam, - other => bail!("unknown collision rule type \"{other}\""), - }, - team_color: Decode::decode(r)?, - team_prefix: Decode::decode(r)?, - team_suffix: Decode::decode(r)?, - }, - 3 => Self::AddEntities { - entities: Decode::decode(r)?, - }, - 4 => Self::RemoveEntities { - entities: Decode::decode(r)?, - }, - n => bail!("unknown update teams action of {n}"), - }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/title.rs b/crates/valence_core/src/packet/s2c/play/title.rs deleted file mode 100644 index 446214f82..000000000 --- a/crates/valence_core/src/packet/s2c/play/title.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::borrow::Cow; - -use crate::packet::{Decode, Encode}; -use crate::text::Text; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct TitleS2c<'a> { - pub title_text: Cow<'a, Text>, -} diff --git a/crates/valence_core/src/packet/s2c/play/title_fade.rs b/crates/valence_core/src/packet/s2c/play/title_fade.rs deleted file mode 100644 index 1266795dc..000000000 --- a/crates/valence_core/src/packet/s2c/play/title_fade.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct TitleFadeS2c { - /// Ticks to spend fading in. - pub fade_in: i32, - /// Ticks to keep the title displayed. - pub stay: i32, - /// Ticks to spend fading out. - pub fade_out: i32, -} diff --git a/crates/valence_core/src/packet/s2c/play/unload_chunk.rs b/crates/valence_core/src/packet/s2c/play/unload_chunk.rs deleted file mode 100644 index c6167093c..000000000 --- a/crates/valence_core/src/packet/s2c/play/unload_chunk.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::chunk_pos::ChunkPos; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UnloadChunkS2c { - pub pos: ChunkPos, -} diff --git a/crates/valence_core/src/packet/s2c/play/unlock_recipes.rs b/crates/valence_core/src/packet/s2c/play/unlock_recipes.rs deleted file mode 100644 index 77d6333f6..000000000 --- a/crates/valence_core/src/packet/s2c/play/unlock_recipes.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::borrow::Cow; -use std::io::Write; - -use anyhow::bail; - -use crate::ident::Ident; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct UnlockRecipesS2c<'a> { - pub action: UpdateRecipeBookAction<'a>, - pub crafting_recipe_book_open: bool, - pub crafting_recipe_book_filter_active: bool, - pub smelting_recipe_book_open: bool, - pub smelting_recipe_book_filter_active: bool, - pub blast_furnace_recipe_book_open: bool, - pub blast_furnace_recipe_book_filter_active: bool, - pub smoker_recipe_book_open: bool, - pub smoker_recipe_book_filter_active: bool, - pub recipe_ids: Vec>>, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum UpdateRecipeBookAction<'a> { - Init { - recipe_ids: Vec>>, - }, - Add, - Remove, -} - -impl Encode for UnlockRecipesS2c<'_> { - fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { - VarInt(match &self.action { - UpdateRecipeBookAction::Init { .. } => 0, - UpdateRecipeBookAction::Add => 1, - UpdateRecipeBookAction::Remove => 2, - }) - .encode(&mut w)?; - - self.crafting_recipe_book_open.encode(&mut w)?; - self.crafting_recipe_book_filter_active.encode(&mut w)?; - self.smelting_recipe_book_open.encode(&mut w)?; - self.smelting_recipe_book_filter_active.encode(&mut w)?; - self.blast_furnace_recipe_book_open.encode(&mut w)?; - self.blast_furnace_recipe_book_filter_active - .encode(&mut w)?; - self.smoker_recipe_book_open.encode(&mut w)?; - self.smoker_recipe_book_filter_active.encode(&mut w)?; - self.recipe_ids.encode(&mut w)?; - if let UpdateRecipeBookAction::Init { recipe_ids } = &self.action { - recipe_ids.encode(&mut w)?; - } - - Ok(()) - } -} - -impl<'a> Decode<'a> for UnlockRecipesS2c<'a> { - fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let action_id = VarInt::decode(r)?.0; - - let crafting_recipe_book_open = bool::decode(r)?; - let crafting_recipe_book_filter_active = bool::decode(r)?; - let smelting_recipe_book_open = bool::decode(r)?; - let smelting_recipe_book_filter_active = bool::decode(r)?; - let blast_furnace_recipe_book_open = bool::decode(r)?; - let blast_furnace_recipe_book_filter_active = bool::decode(r)?; - let smoker_recipe_book_open = bool::decode(r)?; - let smoker_recipe_book_filter_active = bool::decode(r)?; - let recipe_ids = Vec::decode(r)?; - - Ok(Self { - action: match action_id { - 0 => UpdateRecipeBookAction::Init { - recipe_ids: Vec::decode(r)?, - }, - 1 => UpdateRecipeBookAction::Add, - 2 => UpdateRecipeBookAction::Remove, - n => bail!("unknown recipe book action of {n}"), - }, - crafting_recipe_book_open, - crafting_recipe_book_filter_active, - smelting_recipe_book_open, - smelting_recipe_book_filter_active, - blast_furnace_recipe_book_open, - blast_furnace_recipe_book_filter_active, - smoker_recipe_book_open, - smoker_recipe_book_filter_active, - recipe_ids, - }) - } -} diff --git a/crates/valence_core/src/packet/s2c/play/update_selected_slot.rs b/crates/valence_core/src/packet/s2c/play/update_selected_slot.rs deleted file mode 100644 index 76d9f17e2..000000000 --- a/crates/valence_core/src/packet/s2c/play/update_selected_slot.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct UpdateSelectedSlotS2c { - pub slot: u8, -} diff --git a/crates/valence_core/src/packet/s2c/play/vehicle_move.rs b/crates/valence_core/src/packet/s2c/play/vehicle_move.rs deleted file mode 100644 index 6e15f0610..000000000 --- a/crates/valence_core/src/packet/s2c/play/vehicle_move.rs +++ /dev/null @@ -1,10 +0,0 @@ -use glam::DVec3; - -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct VehicleMoveS2c { - pub position: DVec3, - pub yaw: f32, - pub pitch: f32, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_border_center_changed.rs b/crates/valence_core/src/packet/s2c/play/world_border_center_changed.rs deleted file mode 100644 index 5ea01ba7e..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_border_center_changed.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct WorldBorderCenterChangedS2c { - pub x_pos: f64, - pub z_pos: f64, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_border_initialize.rs b/crates/valence_core/src/packet/s2c/play/world_border_initialize.rs deleted file mode 100644 index ac515b4dc..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_border_initialize.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::var_long::VarLong; -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct WorldBorderInitializeS2c { - pub x: f64, - pub z: f64, - pub old_diameter: f64, - pub new_diameter: f64, - pub speed: VarLong, - pub portal_teleport_boundary: VarInt, - pub warning_blocks: VarInt, - pub warning_time: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_border_interpolate_size.rs b/crates/valence_core/src/packet/s2c/play/world_border_interpolate_size.rs deleted file mode 100644 index f0d07e3fd..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_border_interpolate_size.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::packet::var_long::VarLong; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct WorldBorderInterpolateSizeS2c { - pub old_diameter: f64, - pub new_diameter: f64, - pub speed: VarLong, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_border_size_changed.rs b/crates/valence_core/src/packet/s2c/play/world_border_size_changed.rs deleted file mode 100644 index e2903eb48..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_border_size_changed.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct WorldBorderSizeChangedS2c { - pub diameter: f64, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_border_warning_blocks_changed.rs b/crates/valence_core/src/packet/s2c/play/world_border_warning_blocks_changed.rs deleted file mode 100644 index 70433f2d6..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_border_warning_blocks_changed.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct WorldBorderWarningBlocksChangedS2c { - pub warning_blocks: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_border_warning_time_changed.rs b/crates/valence_core/src/packet/s2c/play/world_border_warning_time_changed.rs deleted file mode 100644 index e1df49417..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_border_warning_time_changed.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct WorldBorderWarningTimeChangedS2c { - pub warning_time: VarInt, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_event.rs b/crates/valence_core/src/packet/s2c/play/world_event.rs deleted file mode 100644 index 23b7adfae..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_event.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::block_pos::BlockPos; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug, Encode, Decode)] -pub struct WorldEventS2c { - pub event: i32, - pub location: BlockPos, - pub data: i32, - pub disable_relative_volume: bool, -} diff --git a/crates/valence_core/src/packet/s2c/play/world_time_update.rs b/crates/valence_core/src/packet/s2c/play/world_time_update.rs deleted file mode 100644 index e74032ef9..000000000 --- a/crates/valence_core/src/packet/s2c/play/world_time_update.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct WorldTimeUpdateS2c { - /// The age of the world in 1/20ths of a second. - pub world_age: i64, - /// The current time of day in 1/20ths of a second. - /// The value should be in the range \[0, 24000]. - /// 6000 is noon, 12000 is sunset, and 18000 is midnight. - pub time_of_day: i64, -} diff --git a/crates/valence_core/src/packet/s2c/status/query_pong.rs b/crates/valence_core/src/packet/s2c/status/query_pong.rs deleted file mode 100644 index 889a48b23..000000000 --- a/crates/valence_core/src/packet/s2c/status/query_pong.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct QueryPongS2c { - pub payload: u64, -} diff --git a/crates/valence_core/src/packet/s2c/status/query_response.rs b/crates/valence_core/src/packet/s2c/status/query_response.rs deleted file mode 100644 index 3bb4c0aac..000000000 --- a/crates/valence_core/src/packet/s2c/status/query_response.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::packet::{Decode, Encode}; - -#[derive(Copy, Clone, Debug, Encode, Decode)] -pub struct QueryResponseS2c<'a> { - pub json: &'a str, -} diff --git a/crates/valence_core/src/packet/s2c/play/particle.rs b/crates/valence_core/src/particle.rs similarity index 98% rename from crates/valence_core/src/packet/s2c/play/particle.rs rename to crates/valence_core/src/particle.rs index 245393eaa..d75d4ae27 100644 --- a/crates/valence_core/src/packet/s2c/play/particle.rs +++ b/crates/valence_core/src/particle.rs @@ -6,18 +6,8 @@ use glam::{DVec3, Vec3}; use crate::block_pos::BlockPos; use crate::item::ItemStack; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; - -#[derive(Clone, Debug)] -pub struct ParticleS2c<'a> { - pub particle: Cow<'a, Particle>, - pub long_distance: bool, - pub position: DVec3, - pub offset: Vec3, - pub max_speed: f32, - pub count: i32, -} +use crate::protocol::var_int::VarInt; +use crate::protocol::{packet_id, Decode, Encode, Packet}; #[derive(Clone, PartialEq, Debug)] pub enum Particle { @@ -369,6 +359,17 @@ impl Particle { } } +#[derive(Clone, Debug, Packet)] +#[packet(id = packet_id::PARTICLE_S2C)] +pub struct ParticleS2c<'a> { + pub particle: Cow<'a, Particle>, + pub long_distance: bool, + pub position: DVec3, + pub offset: Vec3, + pub max_speed: f32, + pub count: i32, +} + impl Encode for ParticleS2c<'_> { fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { VarInt(self.particle.id()).encode(&mut w)?; diff --git a/crates/valence_core/src/property.rs b/crates/valence_core/src/property.rs index 96dfae9f1..634d44162 100644 --- a/crates/valence_core/src/property.rs +++ b/crates/valence_core/src/property.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Serialize, Deserialize)] pub struct Property { diff --git a/crates/valence_core/src/protocol.rs b/crates/valence_core/src/protocol.rs new file mode 100644 index 000000000..ce2a9fd2f --- /dev/null +++ b/crates/valence_core/src/protocol.rs @@ -0,0 +1,351 @@ +//! Minecraft's protocol. + +pub mod array; +pub mod byte_angle; +pub mod decode; +pub mod encode; +pub mod global_pos; +pub mod impls; +pub mod packet; +pub mod raw; +pub mod var_int; +pub mod var_long; + +use std::io::Write; + +use anyhow::Context; +pub use valence_core_macros::{Decode, Encode, Packet}; +use var_int::VarInt; + +/// The maximum number of bytes in a single Minecraft packet. +pub const MAX_PACKET_SIZE: i32 = 2097152; + +/// The `Encode` trait allows objects to be written to the Minecraft protocol. +/// It is the inverse of [`Decode`]. +/// +/// # Deriving +/// +/// This trait can be implemented automatically for structs and enums by using +/// the [`Encode`][macro] derive macro. All components of the type must +/// implement `Encode`. Components are encoded in the order they appear in the +/// type definition. +/// +/// For enums, the variant to encode is marked by a leading [`VarInt`] +/// discriminant (tag). The discriminant value can be changed using the `#[tag = +/// ...]` attribute on the variant in question. Discriminant values are assigned +/// to variants using rules similar to regular enum discriminants. +/// +/// ``` +/// use valence_core::protocol::Encode; +/// +/// #[derive(Encode)] +/// struct MyStruct<'a> { +/// first: i32, +/// second: &'a str, +/// third: [f64; 3], +/// } +/// +/// #[derive(Encode)] +/// enum MyEnum { +/// First, // tag = 0 +/// Second, // tag = 1 +/// #[packet(tag = 25)] +/// Third, // tag = 25 +/// Fourth, // tag = 26 +/// } +/// +/// let value = MyStruct { +/// first: 10, +/// second: "hello", +/// third: [1.5, 3.14, 2.718], +/// }; +/// +/// let mut buf = vec![]; +/// value.encode(&mut buf).unwrap(); +/// +/// println!("{buf:?}"); +/// ``` +/// +/// [macro]: valence_core_macros::Encode +/// [`VarInt`]: var_int::VarInt +pub trait Encode { + /// Writes this object to the provided writer. + /// + /// If this type also implements [`Decode`] then successful calls to this + /// function returning `Ok(())` must always successfully [`decode`] using + /// the data that was written to the writer. The exact number of bytes + /// that were originally written must be consumed during the decoding. + /// + /// [`decode`]: Decode::decode + fn encode(&self, w: impl Write) -> anyhow::Result<()>; + + /// Like [`Encode::encode`], except that a whole slice of values is encoded. + /// + /// This method must be semantically equivalent to encoding every element of + /// the slice in sequence with no leading length prefix (which is exactly + /// what the default implementation does), but a more efficient + /// implementation may be used. + /// + /// This optimization is very important for some types like `u8` where + /// [`write_all`] is used. Because impl specialization is unavailable in + /// stable Rust, we must make the slice specialization part of this trait. + /// + /// [`write_all`]: Write::write_all + fn encode_slice(slice: &[Self], mut w: impl Write) -> anyhow::Result<()> + where + Self: Sized, + { + for value in slice { + value.encode(&mut w)?; + } + + Ok(()) + } +} + +/// The `Decode` trait allows objects to be read from the Minecraft protocol. It +/// is the inverse of [`Encode`]. +/// +/// `Decode` is parameterized by a lifetime. This allows the decoded value to +/// borrow data from the byte slice it was read from. +/// +/// # Deriving +/// +/// This trait can be implemented automatically for structs and enums by using +/// the [`Decode`][macro] derive macro. All components of the type must +/// implement `Decode`. Components are decoded in the order they appear in the +/// type definition. +/// +/// For enums, the variant to decode is determined by a leading [`VarInt`] +/// discriminant (tag). The discriminant value can be changed using the `#[tag = +/// ...]` attribute on the variant in question. Discriminant values are assigned +/// to variants using rules similar to regular enum discriminants. +/// +/// ``` +/// use valence_core::protocol::Decode; +/// +/// #[derive(PartialEq, Debug, Decode)] +/// struct MyStruct { +/// first: i32, +/// second: MyEnum, +/// } +/// +/// #[derive(PartialEq, Debug, Decode)] +/// enum MyEnum { +/// First, // tag = 0 +/// Second, // tag = 1 +/// #[packet(tag = 25)] +/// Third, // tag = 25 +/// Fourth, // tag = 26 +/// } +/// +/// let mut r: &[u8] = &[0, 0, 0, 0, 26]; +/// +/// let value = MyStruct::decode(&mut r).unwrap(); +/// let expected = MyStruct { +/// first: 0, +/// second: MyEnum::Fourth, +/// }; +/// +/// assert_eq!(value, expected); +/// assert!(r.is_empty()); +/// ``` +/// +/// [macro]: valence_core_macros::Decode +/// [`VarInt`]: var_int::VarInt +pub trait Decode<'a>: Sized { + /// Reads this object from the provided byte slice. + /// + /// Implementations of `Decode` are expected to shrink the slice from the + /// front as bytes are read. + fn decode(r: &mut &'a [u8]) -> anyhow::Result; +} + +/// Types considered to be Minecraft packets. +/// +/// In serialized form, a packet begins with a [`VarInt`] packet ID followed by +/// the body of the packet. If present, the implementations of [`Encode`] and +/// [`Decode`] on `Self` are expected to only encode/decode the _body_ of this +/// packet without the leading ID. +pub trait Packet: std::fmt::Debug { + /// The leading VarInt ID of this packet. + const ID: i32; + /// The name of this packet for debugging purposes. + const NAME: &'static str; + + /// Encodes this packet's VarInt ID first, followed by the packet's body. + fn encode_with_id(&self, mut w: impl Write) -> anyhow::Result<()> + where + Self: Encode, + { + VarInt(Self::ID) + .encode(&mut w) + .context("failed to encode packet ID")?; + self.encode(w) + } +} + +/// Contains constants for every vanilla packet ID. +pub mod packet_id { + include!(concat!(env!("OUT_DIR"), "/packet_id.rs")); +} + +#[allow(dead_code)] +#[cfg(test)] +mod tests { + use std::borrow::Cow; + + use bytes::BytesMut; + + use super::*; + use crate::protocol::decode::PacketDecoder; + use crate::protocol::encode::PacketEncoder; + + #[derive(Encode, Decode, Packet, Debug)] + #[packet(id = 1)] + struct RegularStruct { + foo: i32, + bar: bool, + baz: f64, + } + + #[derive(Encode, Decode, Packet, Debug)] + #[packet(id = 2)] + struct UnitStruct; + + #[derive(Encode, Decode, Packet, Debug)] + #[packet(id = 3)] + struct EmptyStruct {} + + #[derive(Encode, Decode, Packet, Debug)] + #[packet(id = 4)] + struct TupleStruct(i32, bool, f64); + + #[derive(Encode, Decode, Packet, Debug)] + #[packet(id = 5)] + struct StructWithGenerics<'z, T = ()> { + foo: &'z str, + bar: T, + } + + #[derive(Encode, Decode, Packet, Debug)] + #[packet(id = 6)] + struct TupleStructWithGenerics<'z, T = ()>(&'z str, i32, T); + + #[allow(unconditional_recursion, clippy::extra_unused_type_parameters)] + fn assert_has_impls<'a, T>() + where + T: Encode + Decode<'a> + Packet, + { + assert_has_impls::(); + assert_has_impls::(); + assert_has_impls::(); + assert_has_impls::(); + assert_has_impls::(); + assert_has_impls::(); + } + + #[test] + fn packet_name() { + assert_eq!(RegularStruct::NAME, "RegularStruct"); + assert_eq!(UnitStruct::NAME, "UnitStruct"); + assert_eq!(StructWithGenerics::<()>::NAME, "StructWithGenerics"); + } + + use crate::block_pos::BlockPos; + use crate::hand::Hand; + use crate::ident::Ident; + use crate::item::{ItemKind, ItemStack}; + use crate::protocol::var_int::VarInt; + use crate::protocol::var_long::VarLong; + use crate::text::{Text, TextFormat}; + + #[cfg(feature = "encryption")] + const CRYPT_KEY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + + #[derive(PartialEq, Debug, Encode, Decode, Packet)] + #[packet(id = 42)] + struct TestPacket<'a> { + a: bool, + b: u8, + c: i32, + d: f32, + e: f64, + f: BlockPos, + g: Hand, + h: Ident>, + i: Option, + j: Text, + k: VarInt, + l: VarLong, + m: &'a str, + n: &'a [u8; 10], + o: [u128; 3], + } + + impl<'a> TestPacket<'a> { + fn new(string: &'a str) -> Self { + Self { + a: true, + b: 12, + c: -999, + d: 5.001, + e: 1e10, + f: BlockPos::new(1, 2, 3), + g: Hand::Off, + h: Ident::new("minecraft:whatever").unwrap(), + i: Some(ItemStack::new(ItemKind::WoodenSword, 12, None)), + j: "my ".into_text() + "fancy".italic() + " text", + k: VarInt(123), + l: VarLong(456), + m: string, + n: &[7; 10], + o: [123456789; 3], + } + } + } + + fn check_test_packet(dec: &mut PacketDecoder, string: &str) { + let frame = dec.try_next_packet().unwrap().unwrap(); + + let pkt = frame.decode::().unwrap(); + + assert_eq!(&pkt, &TestPacket::new(string)); + } + + #[test] + fn packets_round_trip() { + let mut buf = BytesMut::new(); + + let mut enc = PacketEncoder::new(); + + enc.append_packet(&TestPacket::new("first")).unwrap(); + #[cfg(feature = "compression")] + enc.set_compression(Some(0)); + enc.append_packet(&TestPacket::new("second")).unwrap(); + buf.unsplit(enc.take()); + #[cfg(feature = "encryption")] + enc.enable_encryption(&CRYPT_KEY); + enc.append_packet(&TestPacket::new("third")).unwrap(); + enc.prepend_packet(&TestPacket::new("fourth")).unwrap(); + + buf.unsplit(enc.take()); + + let mut dec = PacketDecoder::new(); + + dec.queue_bytes(buf); + + check_test_packet(&mut dec, "first"); + + #[cfg(feature = "compression")] + dec.set_compression(Some(0)); + + check_test_packet(&mut dec, "second"); + + #[cfg(feature = "encryption")] + dec.enable_encryption(&CRYPT_KEY); + + check_test_packet(&mut dec, "fourth"); + check_test_packet(&mut dec, "third"); + } +} diff --git a/crates/valence_core/src/packet/array.rs b/crates/valence_core/src/protocol/array.rs similarity index 93% rename from crates/valence_core/src/packet/array.rs rename to crates/valence_core/src/protocol/array.rs index d8c1b0d84..957eee997 100644 --- a/crates/valence_core/src/packet/array.rs +++ b/crates/valence_core/src/protocol/array.rs @@ -2,8 +2,8 @@ use std::io::Write; use anyhow::ensure; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; +use crate::protocol::var_int::VarInt; +use crate::protocol::{Decode, Encode}; /// A fixed-size array encoded and decoded with a [`VarInt`] length prefix. /// diff --git a/crates/valence_core/src/packet/byte_angle.rs b/crates/valence_core/src/protocol/byte_angle.rs similarity index 95% rename from crates/valence_core/src/packet/byte_angle.rs rename to crates/valence_core/src/protocol/byte_angle.rs index c6a213dd0..c65b3c675 100644 --- a/crates/valence_core/src/packet/byte_angle.rs +++ b/crates/valence_core/src/protocol/byte_angle.rs @@ -1,7 +1,7 @@ use std::f32::consts::TAU; use std::io::Write; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; /// Represents an angle in steps of 1/256 of a full turn. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] diff --git a/crates/valence_core/src/protocol/c2s.rs b/crates/valence_core/src/protocol/c2s.rs new file mode 100644 index 000000000..06b4dff30 --- /dev/null +++ b/crates/valence_core/src/protocol/c2s.rs @@ -0,0 +1,156 @@ +/* +pub use advancement_tab::AdvancementTabC2s; +pub use boat_paddle::BoatPaddleStateC2s; +pub use book_update::BookUpdateC2s; +pub use button_click::ButtonClickC2s; +pub use chat_message::ChatMessageC2s; +pub use click_slot::ClickSlotC2s; +pub use client_command::ClientCommandC2s; +pub use client_settings::ClientSettingsC2s; +pub use client_status::ClientStatusC2s; +pub use close_handled_screen::CloseHandledScreenC2s; +pub use command_execution::CommandExecutionC2s; +pub use craft_request::CraftRequestC2s; +pub use creative_inventory_action::CreativeInventoryActionC2s; +pub use custom_payload::CustomPayloadC2s; +pub use hand_swing::HandSwingC2s; +pub use jigsaw_generating::JigsawGeneratingC2s; +pub use keep_alive::KeepAliveC2s; +pub use message_acknowledgment::MessageAcknowledgmentC2s; +pub use pick_from_inventory::PickFromInventoryC2s; +pub use play_pong::PlayPongC2s; +pub use player_action::PlayerActionC2s; +pub use player_input::PlayerInputC2s; +pub use player_interact_block::PlayerInteractBlockC2s; +pub use player_interact_entity::PlayerInteractEntityC2s; +pub use player_interact_item::PlayerInteractItemC2s; +pub use player_move::{Full, LookAndOnGround, OnGroundOnly, PositionAndOnGround}; +pub use player_session::PlayerSessionC2s; +pub use query_block_nbt::QueryBlockNbtC2s; +pub use query_entity_nbt::QueryEntityNbtC2s; +pub use recipe_book_data::RecipeBookDataC2s; +pub use recipe_category_options::RecipeCategoryOptionsC2s; +pub use rename_item::RenameItemC2s; +pub use request_command_completions::RequestCommandCompletionsC2s; +pub use resource_pack_status::ResourcePackStatusC2s; +pub use select_merchant_trade::SelectMerchantTradeC2s; +pub use spectator_teleport::SpectatorTeleportC2s; +pub use teleport_confirm::TeleportConfirmC2s; +pub use update_beacon::UpdateBeaconC2s; +pub use update_command_block::UpdateCommandBlockC2s; +pub use update_command_block_minecart::UpdateCommandBlockMinecartC2s; +pub use update_difficulty::UpdateDifficultyC2s; +pub use update_difficulty_lock::UpdateDifficultyLockC2s; +pub use update_jigsaw::UpdateJigsawC2s; +pub use update_player_abilities::UpdatePlayerAbilitiesC2s; +pub use update_selected_slot::UpdateSelectedSlotC2s; +pub use update_sign::UpdateSignC2s; +pub use update_structure_block::UpdateStructureBlockC2s; +pub use vehicle_move::VehicleMoveC2s; + +pub mod advancement_tab; +pub mod boat_paddle; +pub mod book_update; +pub mod button_click; +pub mod chat_message; +pub mod click_slot; +pub mod client_command; +pub mod client_settings; +pub mod client_status; +pub mod close_handled_screen; +pub mod command_execution; +pub mod craft_request; +pub mod creative_inventory_action; +pub mod custom_payload; +pub mod hand_swing; +pub mod jigsaw_generating; +pub mod keep_alive; +pub mod message_acknowledgment; +pub mod pick_from_inventory; +pub mod play_pong; +pub mod player_action; +pub mod player_input; +pub mod player_interact_block; +pub mod player_interact_entity; +pub mod player_interact_item; +pub mod player_move; +pub mod player_session; +pub mod query_block_nbt; +pub mod query_entity_nbt; +pub mod recipe_book_data; +pub mod recipe_category_options; +pub mod rename_item; +pub mod request_command_completions; +pub mod resource_pack_status; +pub mod select_merchant_trade; +pub mod spectator_teleport; +pub mod teleport_confirm; +pub mod update_beacon; +pub mod update_command_block; +pub mod update_command_block_minecart; +pub mod update_difficulty; +pub mod update_difficulty_lock; +pub mod update_jigsaw; +pub mod update_player_abilities; +pub mod update_selected_slot; +pub mod update_sign; +pub mod update_structure_block; +pub mod vehicle_move; + +packet_group! { + #[derive(Clone)] + C2sPlayPacket<'a> { + AdvancementTabC2s<'a>, + BoatPaddleStateC2s, + BookUpdateC2s<'a>, + ButtonClickC2s, + ChatMessageC2s<'a>, + ClickSlotC2s, + ClientCommandC2s, + ClientSettingsC2s<'a>, + ClientStatusC2s, + CloseHandledScreenC2s, + CommandExecutionC2s<'a>, + CraftRequestC2s<'a>, + CreativeInventoryActionC2s, + CustomPayloadC2s<'a>, + Full, + HandSwingC2s, + JigsawGeneratingC2s, + KeepAliveC2s, + LookAndOnGround, + MessageAcknowledgmentC2s, + OnGroundOnly, + PickFromInventoryC2s, + PlayerActionC2s, + PlayerInputC2s, + PlayerInteractBlockC2s, + PlayerInteractEntityC2s, + PlayerInteractItemC2s, + PlayerSessionC2s<'a>, + PlayPongC2s, + PositionAndOnGround, + QueryBlockNbtC2s, + QueryEntityNbtC2s, + RecipeBookDataC2s<'a>, + RecipeCategoryOptionsC2s, + RenameItemC2s<'a>, + RequestCommandCompletionsC2s<'a>, + ResourcePackStatusC2s, + SelectMerchantTradeC2s, + SpectatorTeleportC2s, + TeleportConfirmC2s, + UpdateBeaconC2s, + UpdateCommandBlockC2s<'a>, + UpdateCommandBlockMinecartC2s<'a>, + UpdateDifficultyC2s, + UpdateDifficultyLockC2s, + UpdateJigsawC2s<'a>, + UpdatePlayerAbilitiesC2s, + UpdateSelectedSlotC2s, + UpdateSignC2s<'a>, + UpdateStructureBlockC2s<'a>, + VehicleMoveC2s, + } +} +*/ diff --git a/crates/valence_core/src/packet/decode.rs b/crates/valence_core/src/protocol/decode.rs similarity index 60% rename from crates/valence_core/src/packet/decode.rs rename to crates/valence_core/src/protocol/decode.rs index 174a8457f..6a383d265 100644 --- a/crates/valence_core/src/packet/decode.rs +++ b/crates/valence_core/src/protocol/decode.rs @@ -1,15 +1,18 @@ #[cfg(feature = "encryption")] -use aes::cipher::{AsyncStreamCipher, NewCipher}; -use anyhow::{bail, ensure}; +use aes::cipher::generic_array::GenericArray; +#[cfg(feature = "encryption")] +use aes::cipher::{BlockDecryptMut, BlockSizeUser, KeyIvInit}; +use anyhow::{bail, ensure, Context}; use bytes::{Buf, BytesMut}; -use crate::packet::var_int::{VarInt, VarIntDecodeError}; -use crate::packet::{Packet, MAX_PACKET_SIZE}; +use super::Decode; +use crate::protocol::var_int::{VarInt, VarIntDecodeError}; +use crate::protocol::{Packet, MAX_PACKET_SIZE}; /// The AES block cipher with a 128 bit key, using the CFB-8 mode of /// operation. #[cfg(feature = "encryption")] -type Cipher = cfb8::Cfb8; +type Cipher = cfb8::Decryptor; #[derive(Default)] pub struct PacketDecoder { @@ -27,7 +30,7 @@ impl PacketDecoder { Self::default() } - pub fn try_next_packet(&mut self) -> anyhow::Result> { + pub fn try_next_packet(&mut self) -> anyhow::Result> { let mut r = &self.buf[..]; let packet_len = match VarInt::decode_partial(&mut r) { @@ -48,6 +51,8 @@ impl PacketDecoder { let packet_len_len = VarInt(packet_len).written_size(); + let mut data; + #[cfg(feature = "compression")] if let Some(threshold) = self.compression_threshold { use std::io::Write; @@ -55,8 +60,6 @@ impl PacketDecoder { use bytes::BufMut; use flate2::write::ZlibDecoder; - use crate::packet::Decode; - r = &r[..packet_len as usize]; let data_len = VarInt::decode(&mut r)?.0; @@ -92,7 +95,7 @@ impl PacketDecoder { self.buf.advance(total_packet_len); - return Ok(Some(self.decompress_buf.split())); + data = self.decompress_buf.split(); } else { debug_assert_eq!(data_len, 0); @@ -106,12 +109,32 @@ impl PacketDecoder { let remaining_len = r.len(); self.buf.advance(packet_len_len + 1); - return Ok(Some(self.buf.split_to(remaining_len))); + + data = self.buf.split_to(remaining_len); } + } else { + self.buf.advance(packet_len_len); + data = self.buf.split_to(packet_len as usize); + } + + #[cfg(not(feature = "compression"))] + { + self.buf.advance(packet_len_len); + data = self.buf.split_to(packet_len as usize); } - self.buf.advance(packet_len_len); - Ok(Some(self.buf.split_to(packet_len as usize))) + // Decode the leading packet ID. + r = &data[..]; + let packet_id = VarInt::decode(&mut r) + .context("failed to decode packet ID")? + .0; + + data.advance(data.len() - r.len()); + + Ok(Some(PacketFrame { + id: packet_id, + body: data, + })) } #[cfg(feature = "compression")] @@ -128,20 +151,30 @@ impl PacketDecoder { pub fn enable_encryption(&mut self, key: &[u8; 16]) { assert!(self.cipher.is_none(), "encryption is already enabled"); - let mut cipher = Cipher::new(key.into(), key.into()); + let mut cipher = Cipher::new_from_slices(key, key).expect("invalid key"); // Don't forget to decrypt the data we already have. - cipher.decrypt(&mut self.buf); + Self::decrypt_bytes(&mut cipher, &mut self.buf); self.cipher = Some(cipher); } + /// Decrypts the provided byte slice in place using the cipher, without + /// consuming the cipher. + #[cfg(feature = "encryption")] + fn decrypt_bytes(cipher: &mut Cipher, bytes: &mut [u8]) { + for chunk in bytes.chunks_mut(Cipher::block_size()) { + let gen_arr = GenericArray::from_mut_slice(chunk); + cipher.decrypt_block_mut(gen_arr); + } + } + pub fn queue_bytes(&mut self, mut bytes: BytesMut) { #![allow(unused_mut)] #[cfg(feature = "encryption")] if let Some(cipher) = &mut self.cipher { - cipher.decrypt(&mut bytes); + Self::decrypt_bytes(cipher, &mut bytes); } self.buf.unsplit(bytes); @@ -155,7 +188,8 @@ impl PacketDecoder { #[cfg(feature = "encryption")] if let Some(cipher) = &mut self.cipher { - cipher.decrypt(&mut self.buf[len..]); + let slice = &mut self.buf[len..]; + Self::decrypt_bytes(cipher, slice); } } @@ -168,17 +202,40 @@ impl PacketDecoder { } } -/// Decodes a (packet ID + data) packet frame. An error is returned if the input -/// is not read to the end. -pub fn decode_packet<'a, P: Packet<'a>>(mut bytes: &'a [u8]) -> anyhow::Result

{ - let pkt = P::decode_packet(&mut bytes)?; +#[derive(Clone, Debug)] +pub struct PacketFrame { + /// The ID of the decoded packet. + pub id: i32, + /// The contents of the packet after the leading VarInt ID. + pub body: BytesMut, +} + +impl PacketFrame { + /// Attempts to decode this packet as type `P`. An error is returned if the + /// packet ID does not match, the body of the packet failed to decode, or + /// some input was missed. + pub fn decode<'a, P>(&'a self) -> anyhow::Result

+ where + P: Packet + Decode<'a>, + { + ensure!( + P::ID == self.id, + "packet ID mismatch: expected {}, got {}", + P::ID, + self.id + ); + + let mut r = &self.body[..]; - ensure!( - bytes.is_empty(), - "missed {} bytes while decoding {}", - bytes.len(), - pkt.packet_name() - ); + let pkt = P::decode(&mut r)?; - Ok(pkt) + ensure!( + r.is_empty(), + "missed {} bytes while decoding packet {}", + r.len(), + P::NAME + ); + + Ok(pkt) + } } diff --git a/crates/valence_core/src/packet/encode.rs b/crates/valence_core/src/protocol/encode.rs similarity index 83% rename from crates/valence_core/src/packet/encode.rs rename to crates/valence_core/src/protocol/encode.rs index 62bc4a1e0..6f9c576c9 100644 --- a/crates/valence_core/src/packet/encode.rs +++ b/crates/valence_core/src/protocol/encode.rs @@ -1,16 +1,21 @@ use std::io::Write; +#[cfg(feature = "encryption")] +use aes::cipher::generic_array::GenericArray; +#[cfg(feature = "encryption")] +use aes::cipher::{BlockEncryptMut, BlockSizeUser, KeyIvInit}; use anyhow::ensure; +use bevy_ecs::world::Mut; use bytes::{BufMut, BytesMut}; use tracing::warn; -use crate::packet::var_int::VarInt; -use crate::packet::{Encode, Packet, MAX_PACKET_SIZE}; +use crate::protocol::var_int::VarInt; +use crate::protocol::{Encode, Packet, MAX_PACKET_SIZE}; /// The AES block cipher with a 128 bit key, using the CFB-8 mode of /// operation. #[cfg(feature = "encryption")] -type Cipher = cfb8::Cfb8; +type Cipher = cfb8::Encryptor; #[derive(Default)] pub struct PacketEncoder { @@ -33,9 +38,9 @@ impl PacketEncoder { self.buf.extend_from_slice(bytes) } - pub fn prepend_packet<'a, P>(&mut self, pkt: &P) -> anyhow::Result<()> + pub fn prepend_packet

(&mut self, pkt: &P) -> anyhow::Result<()> where - P: Packet<'a>, + P: Packet + Encode, { let start_len = self.buf.len(); self.append_packet(pkt)?; @@ -54,13 +59,13 @@ impl PacketEncoder { Ok(()) } - pub fn append_packet<'a, P>(&mut self, pkt: &P) -> anyhow::Result<()> + pub fn append_packet

(&mut self, pkt: &P) -> anyhow::Result<()> where - P: Packet<'a>, + P: Packet + Encode, { let start_len = self.buf.len(); - pkt.encode_packet((&mut self.buf).writer())?; + pkt.encode_with_id((&mut self.buf).writer())?; let data_len = self.buf.len() - start_len; @@ -145,9 +150,10 @@ impl PacketEncoder { pub fn take(&mut self) -> BytesMut { #[cfg(feature = "encryption")] if let Some(cipher) = &mut self.cipher { - use aes::cipher::AsyncStreamCipher; - - cipher.encrypt(&mut self.buf); + for chunk in self.buf.chunks_mut(Cipher::block_size()) { + let gen_arr = GenericArray::from_mut_slice(chunk); + cipher.encrypt_block_mut(gen_arr); + } } self.buf.split() @@ -168,10 +174,8 @@ impl PacketEncoder { /// [taken]: Self::take #[cfg(feature = "encryption")] pub fn enable_encryption(&mut self, key: &[u8; 16]) { - use aes::cipher::NewCipher; - assert!(self.cipher.is_none(), "encryption is already enabled"); - self.cipher = Some(NewCipher::new(key.into(), key.into())); + self.cipher = Some(Cipher::new_from_slices(key, key).expect("invalid key")); } } @@ -179,14 +183,20 @@ impl PacketEncoder { pub trait WritePacket { /// Writes a packet to this object. Encoding errors are typically logged and /// discarded. - fn write_packet<'a>(&mut self, packet: &impl Packet<'a>); + fn write_packet

(&mut self, packet: &P) + where + P: Packet + Encode; + /// Copies raw packet data directly into this object. Don't use this unless /// you know what you're doing. fn write_packet_bytes(&mut self, bytes: &[u8]); } impl WritePacket for &mut W { - fn write_packet<'a>(&mut self, packet: &impl Packet<'a>) { + fn write_packet

(&mut self, packet: &P) + where + P: Packet + Encode, + { (*self).write_packet(packet) } @@ -195,6 +205,19 @@ impl WritePacket for &mut W { } } +impl WritePacket for Mut<'_, T> { + fn write_packet

(&mut self, packet: &P) + where + P: Packet + Encode, + { + self.as_mut().write_packet(packet) + } + + fn write_packet_bytes(&mut self, bytes: &[u8]) { + self.as_mut().write_packet_bytes(bytes) + } +} + /// An implementor of [`WritePacket`] backed by a `Vec` reference. pub struct PacketWriter<'a> { pub buf: &'a mut Vec, @@ -213,7 +236,10 @@ impl<'a> PacketWriter<'a> { } impl WritePacket for PacketWriter<'_> { - fn write_packet<'a>(&mut self, pkt: &impl Packet<'a>) { + fn write_packet

(&mut self, pkt: &P) + where + P: Packet + Encode, + { #[cfg(feature = "compression")] let res = if let Some(threshold) = self.threshold { encode_packet_compressed(self.buf, pkt, threshold, self.scratch) @@ -237,7 +263,10 @@ impl WritePacket for PacketWriter<'_> { } impl WritePacket for PacketEncoder { - fn write_packet<'a>(&mut self, packet: &impl Packet<'a>) { + fn write_packet

(&mut self, packet: &P) + where + P: Packet + Encode, + { if let Err(e) = self.append_packet(packet) { warn!("failed to write packet: {e:#}"); } @@ -248,13 +277,13 @@ impl WritePacket for PacketEncoder { } } -pub fn encode_packet<'a, P>(buf: &mut Vec, pkt: &P) -> anyhow::Result<()> +pub fn encode_packet

(buf: &mut Vec, pkt: &P) -> anyhow::Result<()> where - P: Packet<'a>, + P: Packet + Encode, { let start_len = buf.len(); - pkt.encode_packet(&mut *buf)?; + pkt.encode_with_id(&mut *buf)?; let packet_len = buf.len() - start_len; @@ -278,14 +307,14 @@ where } #[cfg(feature = "compression")] -pub fn encode_packet_compressed<'a, P>( +pub fn encode_packet_compressed

( buf: &mut Vec, pkt: &P, threshold: u32, scratch: &mut Vec, ) -> anyhow::Result<()> where - P: Packet<'a>, + P: Packet + Encode, { use std::io::Read; @@ -294,7 +323,7 @@ where let start_len = buf.len(); - pkt.encode_packet(&mut *buf)?; + pkt.encode_with_id(&mut *buf)?; let data_len = buf.len() - start_len; diff --git a/crates/valence_core/src/packet/global_pos.rs b/crates/valence_core/src/protocol/global_pos.rs similarity index 85% rename from crates/valence_core/src/packet/global_pos.rs rename to crates/valence_core/src/protocol/global_pos.rs index d0667a3b2..2136b941b 100644 --- a/crates/valence_core/src/packet/global_pos.rs +++ b/crates/valence_core/src/protocol/global_pos.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use crate::block_pos::BlockPos; use crate::ident::Ident; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] pub struct GlobalPos<'a> { diff --git a/crates/valence_core/src/packet/impls.rs b/crates/valence_core/src/protocol/impls.rs similarity index 97% rename from crates/valence_core/src/packet/impls.rs rename to crates/valence_core/src/protocol/impls.rs index 81aa9b459..e14bb2132 100644 --- a/crates/valence_core/src/packet/impls.rs +++ b/crates/valence_core/src/protocol/impls.rs @@ -1,5 +1,6 @@ //! [`Encode`] and [`Decode`] impls on foreign types. +use core::slice; use std::borrow::Cow; use std::collections::{BTreeSet, HashSet}; use std::hash::{BuildHasher, Hash}; @@ -27,8 +28,8 @@ impl Encode for bool { fn encode_slice(slice: &[bool], mut w: impl Write) -> Result<()> { // SAFETY: Bools have the same layout as u8. + let bytes = unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len()) }; // Bools are guaranteed to have the correct bit pattern. - let bytes: &[u8] = unsafe { mem::transmute(slice) }; Ok(w.write_all(bytes)?) } } @@ -64,7 +65,7 @@ impl Encode for i8 { fn encode_slice(slice: &[i8], mut w: impl Write) -> Result<()> { // SAFETY: i8 has the same layout as u8. - let bytes: &[u8] = unsafe { mem::transmute(slice) }; + let bytes = unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len()) }; Ok(w.write_all(bytes)?) } } @@ -471,7 +472,6 @@ impl Encode for [T; N] { impl<'a, const N: usize, T: Decode<'a>> Decode<'a> for [T; N] { fn decode(r: &mut &'a [u8]) -> Result { // TODO: rewrite using std::array::try_from_fn when stabilized? - // TODO: specialization for [f64; 3] improved performance. let mut data: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; @@ -539,9 +539,12 @@ impl<'a> Decode<'a> for &'a [u8] { impl<'a> Decode<'a> for &'a [i8] { fn decode(r: &mut &'a [u8]) -> Result { - let unsigned_bytes = <&[u8]>::decode(r)?; - let signed_bytes: &[i8] = unsafe { mem::transmute(unsigned_bytes) }; - Ok(signed_bytes) + let bytes = <&[u8]>::decode(r)?; + + // SAFETY: i8 and u8 have the same layout. + let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *const i8, bytes.len()) }; + + Ok(bytes) } } @@ -765,12 +768,12 @@ impl<'a> Decode<'a> for Uuid { impl Encode for Compound { fn encode(&self, w: impl Write) -> Result<()> { - Ok(valence_nbt::to_binary_writer(w, self, "")?) + Ok(self.to_binary(w, "")?) } } impl Decode<'_> for Compound { fn decode(r: &mut &[u8]) -> Result { - Ok(valence_nbt::from_binary_slice(r)?.0) + Ok(Self::from_binary(r)?.0) } } diff --git a/crates/valence_core/src/protocol/packet.rs b/crates/valence_core/src/protocol/packet.rs new file mode 100644 index 000000000..655ff28e6 --- /dev/null +++ b/crates/valence_core/src/protocol/packet.rs @@ -0,0 +1,1374 @@ +//! A temporary module for packets that do not yet have a home outside of +//! valence_core. +//! +//! All packets should be moved out of this module eventually. + +use std::borrow::Cow; +use std::io::Write; + +use anyhow::bail; +use bitfield_struct::bitfield; +use byteorder::WriteBytesExt; +use glam::IVec3; +use uuid::Uuid; + +use crate::ident; +use crate::ident::Ident; +use crate::protocol::var_int::VarInt; +use crate::protocol::{packet_id, Decode, Encode, Packet}; +use crate::text::Text; + +// TODO: move module contents to valence_chat. +pub mod chat { + pub use super::*; + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::GAME_MESSAGE_S2C)] + pub struct GameMessageS2c<'a> { + pub chat: Cow<'a, Text>, + /// Whether the message is in the actionbar or the chat. + pub overlay: bool, + } + + #[derive(Copy, Clone, PartialEq, Debug)] + pub struct MessageSignature<'a> { + pub message_id: i32, + pub signature: Option<&'a [u8; 256]>, + } + + impl<'a> Encode for MessageSignature<'a> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + VarInt(self.message_id + 1).encode(&mut w)?; + + match self.signature { + None => {} + Some(signature) => signature.encode(&mut w)?, + } + + Ok(()) + } + } + + impl<'a> Decode<'a> for MessageSignature<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let message_id = VarInt::decode(r)?.0 - 1; // TODO: this can underflow. + + let signature = if message_id == -1 { + Some(<&[u8; 256]>::decode(r)?) + } else { + None + }; + + Ok(Self { + message_id, + signature, + }) + } + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::CHAT_MESSAGE_C2S)] + pub struct ChatMessageC2s<'a> { + pub message: &'a str, + pub timestamp: u64, + pub salt: u64, + pub signature: Option<&'a [u8; 256]>, + pub message_count: VarInt, + // This is a bitset of 20; each bit represents one + // of the last 20 messages received and whether or not + // the message was acknowledged by the client + pub acknowledgement: [u8; 3], + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::COMMAND_EXECUTION_C2S)] + pub struct CommandExecutionC2s<'a> { + pub command: &'a str, + pub timestamp: u64, + pub salt: u64, + pub argument_signatures: Vec>, + pub message_count: VarInt, + //// This is a bitset of 20; each bit represents one + //// of the last 20 messages received and whether or not + //// the message was acknowledged by the client + pub acknowledgement: [u8; 3], + } + + #[derive(Copy, Clone, Debug, Encode, Decode)] + pub struct CommandArgumentSignature<'a> { + pub argument_name: &'a str, + pub signature: &'a [u8; 256], + } + + #[derive(Copy, Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::MESSAGE_ACKNOWLEDGMENT_C2S)] + + pub struct MessageAcknowledgmentC2s { + pub message_count: VarInt, + } + + #[derive(Copy, Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::PLAYER_SESSION_C2S)] + pub struct PlayerSessionC2s<'a> { + pub session_id: Uuid, + // Public key + pub expires_at: i64, + pub public_key_data: &'a [u8], + pub key_signature: &'a [u8], + } + + #[derive(Copy, Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::REQUEST_COMMAND_COMPLETIONS_C2S)] + pub struct RequestCommandCompletionsC2s<'a> { + pub transaction_id: VarInt, + pub text: &'a str, + } + + #[derive(Clone, PartialEq, Debug, Packet)] + #[packet(id = packet_id::CHAT_MESSAGE_S2C)] + pub struct ChatMessageS2c<'a> { + pub sender: Uuid, + pub index: VarInt, + pub message_signature: Option<&'a [u8; 256]>, + pub message: &'a str, + pub time_stamp: u64, + pub salt: u64, + pub previous_messages: Vec>, + pub unsigned_content: Option>, + pub filter_type: MessageFilterType, + pub filter_type_bits: Option, + pub chat_type: VarInt, + pub network_name: Cow<'a, Text>, + pub network_target_name: Option>, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum MessageFilterType { + PassThrough, + FullyFiltered, + PartiallyFiltered, + } + + impl<'a> Encode for ChatMessageS2c<'a> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + self.sender.encode(&mut w)?; + self.index.encode(&mut w)?; + self.message_signature.encode(&mut w)?; + self.message.encode(&mut w)?; + self.time_stamp.encode(&mut w)?; + self.salt.encode(&mut w)?; + self.previous_messages.encode(&mut w)?; + self.unsigned_content.encode(&mut w)?; + self.filter_type.encode(&mut w)?; + + if self.filter_type == MessageFilterType::PartiallyFiltered { + match self.filter_type_bits { + // Filler data + None => 0u8.encode(&mut w)?, + Some(bits) => bits.encode(&mut w)?, + } + } + + self.chat_type.encode(&mut w)?; + self.network_name.encode(&mut w)?; + self.network_target_name.encode(&mut w)?; + + Ok(()) + } + } + + impl<'a> Decode<'a> for ChatMessageS2c<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let sender = Uuid::decode(r)?; + let index = VarInt::decode(r)?; + let message_signature = Option::<&'a [u8; 256]>::decode(r)?; + let message = <&str>::decode(r)?; + let time_stamp = u64::decode(r)?; + let salt = u64::decode(r)?; + let previous_messages = Vec::::decode(r)?; + let unsigned_content = Option::>::decode(r)?; + let filter_type = MessageFilterType::decode(r)?; + + let filter_type_bits = match filter_type { + MessageFilterType::PartiallyFiltered => Some(u8::decode(r)?), + _ => None, + }; + + let chat_type = VarInt::decode(r)?; + let network_name = >::decode(r)?; + let network_target_name = Option::>::decode(r)?; + + Ok(Self { + sender, + index, + message_signature, + message, + time_stamp, + salt, + previous_messages, + unsigned_content, + filter_type, + filter_type_bits, + chat_type, + network_name, + network_target_name, + }) + } + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::CHAT_SUGGESTIONS_S2C)] + pub struct ChatSuggestionsS2c<'a> { + pub action: ChatSuggestionsAction, + pub entries: Cow<'a, [&'a str]>, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum ChatSuggestionsAction { + Add, + Remove, + Set, + } + + #[derive(Copy, Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::REMOVE_MESSAGE_S2C)] + pub struct RemoveMessageS2c<'a> { + pub signature: MessageSignature<'a>, + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::COMMAND_SUGGESTIONS_S2C)] + pub struct CommandSuggestionsS2c<'a> { + pub id: VarInt, + pub start: VarInt, + pub length: VarInt, + pub matches: Vec>, + } + + #[derive(Clone, PartialEq, Debug, Encode, Decode)] + pub struct CommandSuggestionsMatch<'a> { + pub suggested_match: &'a str, + pub tooltip: Option>, + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::PROFILELESS_CHAT_MESSAGE_S2C)] + pub struct ProfilelessChatMessageS2c<'a> { + pub message: Cow<'a, Text>, + pub chat_type: VarInt, + pub chat_type_name: Cow<'a, Text>, + pub target_name: Option>, + } +} + +// TODO: move to valence_scoreboard? +pub mod scoreboard { + + use super::*; + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::TEAM_S2C)] + pub struct TeamS2c<'a> { + pub team_name: &'a str, + pub mode: Mode<'a>, + } + + #[derive(Clone, PartialEq, Debug)] + pub enum Mode<'a> { + CreateTeam { + team_display_name: Cow<'a, Text>, + friendly_flags: TeamFlags, + name_tag_visibility: NameTagVisibility, + collision_rule: CollisionRule, + team_color: TeamColor, + team_prefix: Cow<'a, Text>, + team_suffix: Cow<'a, Text>, + entities: Vec<&'a str>, + }, + RemoveTeam, + UpdateTeamInfo { + team_display_name: Cow<'a, Text>, + friendly_flags: TeamFlags, + name_tag_visibility: NameTagVisibility, + collision_rule: CollisionRule, + team_color: TeamColor, + team_prefix: Cow<'a, Text>, + team_suffix: Cow<'a, Text>, + }, + AddEntities { + entities: Vec<&'a str>, + }, + RemoveEntities { + entities: Vec<&'a str>, + }, + } + + #[bitfield(u8)] + #[derive(PartialEq, Eq, Encode, Decode)] + pub struct TeamFlags { + pub friendly_fire: bool, + pub see_invisible_teammates: bool, + #[bits(6)] + _pad: u8, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub enum NameTagVisibility { + Always, + Never, + HideForOtherTeams, + HideForOwnTeam, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub enum CollisionRule { + Always, + Never, + PushOtherTeams, + PushOwnTeam, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum TeamColor { + Black, + DarkBlue, + DarkGreen, + DarkCyan, + DarkRed, + Purple, + Gold, + Gray, + DarkGray, + Blue, + BrightGreen, + Cyan, + Red, + Pink, + Yellow, + White, + Obfuscated, + Bold, + Strikethrough, + Underlined, + Italic, + Reset, + } + + impl Encode for Mode<'_> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + match self { + Mode::CreateTeam { + team_display_name, + friendly_flags, + name_tag_visibility, + collision_rule, + team_color, + team_prefix, + team_suffix, + entities, + } => { + 0i8.encode(&mut w)?; + team_display_name.encode(&mut w)?; + friendly_flags.encode(&mut w)?; + match name_tag_visibility { + NameTagVisibility::Always => "always", + NameTagVisibility::Never => "never", + NameTagVisibility::HideForOtherTeams => "hideForOtherTeams", + NameTagVisibility::HideForOwnTeam => "hideForOwnTeam", + } + .encode(&mut w)?; + match collision_rule { + CollisionRule::Always => "always", + CollisionRule::Never => "never", + CollisionRule::PushOtherTeams => "pushOtherTeams", + CollisionRule::PushOwnTeam => "pushOwnTeam", + } + .encode(&mut w)?; + team_color.encode(&mut w)?; + team_prefix.encode(&mut w)?; + team_suffix.encode(&mut w)?; + entities.encode(&mut w)?; + } + Mode::RemoveTeam => 1i8.encode(&mut w)?, + Mode::UpdateTeamInfo { + team_display_name, + friendly_flags, + name_tag_visibility, + collision_rule, + team_color, + team_prefix, + team_suffix, + } => { + 2i8.encode(&mut w)?; + team_display_name.encode(&mut w)?; + friendly_flags.encode(&mut w)?; + match name_tag_visibility { + NameTagVisibility::Always => "always", + NameTagVisibility::Never => "never", + NameTagVisibility::HideForOtherTeams => "hideForOtherTeams", + NameTagVisibility::HideForOwnTeam => "hideForOwnTeam", + } + .encode(&mut w)?; + match collision_rule { + CollisionRule::Always => "always", + CollisionRule::Never => "never", + CollisionRule::PushOtherTeams => "pushOtherTeams", + CollisionRule::PushOwnTeam => "pushOwnTeam", + } + .encode(&mut w)?; + team_color.encode(&mut w)?; + team_prefix.encode(&mut w)?; + team_suffix.encode(&mut w)?; + } + Mode::AddEntities { entities } => { + 3i8.encode(&mut w)?; + entities.encode(&mut w)?; + } + Mode::RemoveEntities { entities } => { + 4i8.encode(&mut w)?; + entities.encode(&mut w)?; + } + } + Ok(()) + } + } + + impl<'a> Decode<'a> for Mode<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + Ok(match i8::decode(r)? { + 0 => Self::CreateTeam { + team_display_name: Decode::decode(r)?, + friendly_flags: Decode::decode(r)?, + name_tag_visibility: match <&str>::decode(r)? { + "always" => NameTagVisibility::Always, + "never" => NameTagVisibility::Never, + "hideForOtherTeams" => NameTagVisibility::HideForOtherTeams, + "hideForOwnTeam" => NameTagVisibility::HideForOwnTeam, + other => bail!("unknown name tag visibility type \"{other}\""), + }, + collision_rule: match <&str>::decode(r)? { + "always" => CollisionRule::Always, + "never" => CollisionRule::Never, + "pushOtherTeams" => CollisionRule::PushOtherTeams, + "pushOwnTeam" => CollisionRule::PushOwnTeam, + other => bail!("unknown collision rule type \"{other}\""), + }, + team_color: Decode::decode(r)?, + team_prefix: Decode::decode(r)?, + team_suffix: Decode::decode(r)?, + entities: Decode::decode(r)?, + }, + 1 => Self::RemoveTeam, + 2 => Self::UpdateTeamInfo { + team_display_name: Decode::decode(r)?, + friendly_flags: Decode::decode(r)?, + name_tag_visibility: match <&str>::decode(r)? { + "always" => NameTagVisibility::Always, + "never" => NameTagVisibility::Never, + "hideForOtherTeams" => NameTagVisibility::HideForOtherTeams, + "hideForOwnTeam" => NameTagVisibility::HideForOwnTeam, + other => bail!("unknown name tag visibility type \"{other}\""), + }, + collision_rule: match <&str>::decode(r)? { + "always" => CollisionRule::Always, + "never" => CollisionRule::Never, + "pushOtherTeams" => CollisionRule::PushOtherTeams, + "pushOwnTeam" => CollisionRule::PushOwnTeam, + other => bail!("unknown collision rule type \"{other}\""), + }, + team_color: Decode::decode(r)?, + team_prefix: Decode::decode(r)?, + team_suffix: Decode::decode(r)?, + }, + 3 => Self::AddEntities { + entities: Decode::decode(r)?, + }, + 4 => Self::RemoveEntities { + entities: Decode::decode(r)?, + }, + n => bail!("unknown update teams action of {n}"), + }) + } + } + + #[derive(Copy, Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::SCOREBOARD_DISPLAY_S2C)] + pub struct ScoreboardDisplayS2c<'a> { + pub position: ScoreboardPosition, + pub score_name: &'a str, + } + + #[derive(Copy, Clone, PartialEq, Debug)] + pub enum ScoreboardPosition { + List, + Sidebar, + BelowName, + SidebarTeam(TeamColor), + } + + impl Encode for ScoreboardPosition { + fn encode(&self, w: impl std::io::Write) -> anyhow::Result<()> { + match self { + ScoreboardPosition::List => 0u8.encode(w), + ScoreboardPosition::Sidebar => 1u8.encode(w), + ScoreboardPosition::BelowName => 2u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Black) => 3u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::DarkBlue) => 4u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::DarkGreen) => 5u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::DarkCyan) => 6u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::DarkRed) => 7u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Purple) => 8u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Gold) => 9u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Gray) => 10u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::DarkGray) => 11u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Blue) => 12u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::BrightGreen) => 13u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Cyan) => 14u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Red) => 15u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Pink) => 16u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::Yellow) => 17u8.encode(w), + ScoreboardPosition::SidebarTeam(TeamColor::White) => 18u8.encode(w), + ScoreboardPosition::SidebarTeam(_) => { + Err(anyhow::anyhow!("Invalid scoreboard display position")) + } + } + } + } + + impl<'a> Decode<'a> for ScoreboardPosition { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let value = u8::decode(r)?; + match value { + 0 => Ok(ScoreboardPosition::List), + 1 => Ok(ScoreboardPosition::Sidebar), + 2 => Ok(ScoreboardPosition::BelowName), + 3 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Black)), + 4 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkBlue)), + 5 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkGreen)), + 6 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkCyan)), + 7 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkRed)), + 8 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Purple)), + 9 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Gold)), + 10 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Gray)), + 11 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkGray)), + 12 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Blue)), + 13 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::BrightGreen)), + 14 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Cyan)), + 15 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Red)), + 16 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Pink)), + 17 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Yellow)), + 18 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::White)), + _ => Err(anyhow::anyhow!("Invalid scoreboard display position")), + } + } + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::SCOREBOARD_OBJECTIVE_UPDATE_S2C)] + pub struct ScoreboardObjectiveUpdateS2c<'a> { + pub objective_name: &'a str, + pub mode: ObjectiveMode, + } + + #[derive(Clone, PartialEq, Debug, Encode, Decode)] + pub enum ObjectiveMode { + Create { + objective_display_name: Text, + render_type: ObjectiveRenderType, + }, + Remove, + Update { + objective_display_name: Text, + render_type: ObjectiveRenderType, + }, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum ObjectiveRenderType { + Integer, + Hearts, + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::SCOREBOARD_PLAYER_UPDATE_S2C)] + pub struct ScoreboardPlayerUpdateS2c<'a> { + pub entity_name: &'a str, + pub action: ScoreboardPlayerUpdateAction<'a>, + } + + #[derive(Clone, PartialEq, Debug, Encode, Decode)] + pub enum ScoreboardPlayerUpdateAction<'a> { + Update { + objective_name: &'a str, + objective_score: VarInt, + }, + Remove { + objective_name: &'a str, + }, + } +} + +// TODO: move to valence_boss_bar? +pub mod boss_bar { + use super::*; + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::BOSS_BAR_S2C)] + pub struct BossBarS2c { + pub id: Uuid, + pub action: BossBarAction, + } + + #[derive(Clone, PartialEq, Debug, Encode, Decode)] + pub enum BossBarAction { + Add { + title: Text, + health: f32, + color: BossBarColor, + division: BossBarDivision, + flags: BossBarFlags, + }, + Remove, + UpdateHealth(f32), + UpdateTitle(Text), + UpdateStyle(BossBarColor, BossBarDivision), + UpdateFlags(BossBarFlags), + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum BossBarColor { + Pink, + Blue, + Red, + Green, + Yellow, + Purple, + White, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum BossBarDivision { + NoDivision, + SixNotches, + TenNotches, + TwelveNotches, + TwentyNotches, + } + + #[bitfield(u8)] + #[derive(PartialEq, Eq, Encode, Decode)] + pub struct BossBarFlags { + pub darken_sky: bool, + pub dragon_bar: bool, + pub create_fog: bool, + #[bits(5)] + _pad: u8, + } +} + +// TODO: move to valence_sound? +pub mod sound { + use super::*; + + include!(concat!(env!("OUT_DIR"), "/sound.rs")); + + impl Sound { + pub fn to_id(self) -> SoundId<'static> { + SoundId::Direct { + id: self.to_ident().into(), + range: None, + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum SoundCategory { + Master, + Music, + Record, + Weather, + Block, + Hostile, + Neutral, + Player, + Ambient, + Voice, + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn sound_to_soundid() { + assert_eq!( + Sound::BlockBellUse.to_id(), + SoundId::Direct { + id: ident!("block.bell.use").into(), + range: None + }, + ); + } + } + + #[derive(Copy, Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::PLAY_SOUND_FROM_ENTITY_S2C)] + pub struct PlaySoundFromEntityS2c { + pub id: VarInt, + pub category: SoundCategory, + pub entity_id: VarInt, + pub volume: f32, + pub pitch: f32, + pub seed: i64, + } + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::PLAY_SOUND_S2C)] + pub struct PlaySoundS2c<'a> { + pub id: SoundId<'a>, + pub category: SoundCategory, + pub position: IVec3, + pub volume: f32, + pub pitch: f32, + pub seed: i64, + } + + #[derive(Clone, PartialEq, Debug)] + pub enum SoundId<'a> { + Direct { + id: Ident>, + range: Option, + }, + Reference { + id: VarInt, + }, + } + + impl Encode for SoundId<'_> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + match self { + SoundId::Direct { id, range } => { + VarInt(0).encode(&mut w)?; + id.encode(&mut w)?; + range.encode(&mut w)?; + } + SoundId::Reference { id } => VarInt(id.0 + 1).encode(&mut w)?, + } + + Ok(()) + } + } + + impl<'a> Decode<'a> for SoundId<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let i = VarInt::decode(r)?.0; + + if i == 0 { + Ok(SoundId::Direct { + id: Ident::decode(r)?, + range: >::decode(r)?, + }) + } else { + Ok(SoundId::Reference { id: VarInt(i - 1) }) + } + } + } + + #[derive(Clone, PartialEq, Debug, Packet)] + #[packet(id = packet_id::STOP_SOUND_S2C)] + pub struct StopSoundS2c<'a> { + pub source: Option, + pub sound: Option>>, + } + + impl Encode for StopSoundS2c<'_> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + match (self.source, self.sound.as_ref()) { + (Some(source), Some(sound)) => { + 3i8.encode(&mut w)?; + source.encode(&mut w)?; + sound.encode(&mut w)?; + } + (None, Some(sound)) => { + 2i8.encode(&mut w)?; + sound.encode(&mut w)?; + } + (Some(source), None) => { + 1i8.encode(&mut w)?; + source.encode(&mut w)?; + } + _ => 0i8.encode(&mut w)?, + } + + Ok(()) + } + } + + impl<'a> Decode<'a> for StopSoundS2c<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let (source, sound) = match i8::decode(r)? { + 3 => ( + Some(SoundCategory::decode(r)?), + Some(>>::decode(r)?), + ), + 2 => (None, Some(>>::decode(r)?)), + 1 => (Some(SoundCategory::decode(r)?), None), + _ => (None, None), + }; + + Ok(Self { source, sound }) + } + } +} + +// TODO: move to valence_command +pub mod command { + + use super::*; + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::COMMAND_TREE_S2C)] + pub struct CommandTreeS2c<'a> { + pub commands: Vec>, + pub root_index: VarInt, + } + + #[derive(Clone, Debug)] + pub struct Node<'a> { + pub children: Vec, + pub data: NodeData<'a>, + pub executable: bool, + pub redirect_node: Option, + } + + #[derive(Clone, Debug)] + pub enum NodeData<'a> { + Root, + Literal { + name: &'a str, + }, + Argument { + name: &'a str, + parser: Parser<'a>, + suggestion: Option, + }, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub enum Suggestion { + AskServer, + AllRecipes, + AvailableSounds, + AvailableBiomes, + SummonableEntities, + } + + #[derive(Clone, Debug)] + pub enum Parser<'a> { + Bool, + Float { min: Option, max: Option }, + Double { min: Option, max: Option }, + Integer { min: Option, max: Option }, + Long { min: Option, max: Option }, + String(StringArg), + Entity { single: bool, only_players: bool }, + GameProfile, + BlockPos, + ColumnPos, + Vec3, + Vec2, + BlockState, + BlockPredicate, + ItemStack, + ItemPredicate, + Color, + Component, + Message, + NbtCompoundTag, + NbtTag, + NbtPath, + Objective, + ObjectiveCriteria, + Operation, + Particle, + Angle, + Rotation, + ScoreboardSlot, + ScoreHolder { allow_multiple: bool }, + Swizzle, + Team, + ItemSlot, + ResourceLocation, + Function, + EntityAnchor, + IntRange, + FloatRange, + Dimension, + GameMode, + Time, + ResourceOrTag { registry: Ident> }, + ResourceOrTagKey { registry: Ident> }, + Resource { registry: Ident> }, + ResourceKey { registry: Ident> }, + TemplateMirror, + TemplateRotation, + Uuid, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum StringArg { + SingleWord, + QuotablePhrase, + GreedyPhrase, + } + + impl Encode for Node<'_> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + let node_type = match &self.data { + NodeData::Root => 0, + NodeData::Literal { .. } => 1, + NodeData::Argument { .. } => 2, + }; + + let has_suggestion = matches!( + &self.data, + NodeData::Argument { + suggestion: Some(_), + .. + } + ); + + let flags: u8 = node_type + | (self.executable as u8 * 0x04) + | (self.redirect_node.is_some() as u8 * 0x08) + | (has_suggestion as u8 * 0x10); + + w.write_u8(flags)?; + + self.children.encode(&mut w)?; + + if let Some(redirect_node) = self.redirect_node { + redirect_node.encode(&mut w)?; + } + + match &self.data { + NodeData::Root => {} + NodeData::Literal { name } => { + name.encode(&mut w)?; + } + NodeData::Argument { + name, + parser, + suggestion, + } => { + name.encode(&mut w)?; + parser.encode(&mut w)?; + + if let Some(suggestion) = suggestion { + match suggestion { + Suggestion::AskServer => "ask_server", + Suggestion::AllRecipes => "all_recipes", + Suggestion::AvailableSounds => "available_sounds", + Suggestion::AvailableBiomes => "available_biomes", + Suggestion::SummonableEntities => "summonable_entities", + } + .encode(&mut w)?; + } + } + } + + Ok(()) + } + } + + impl<'a> Decode<'a> for Node<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let flags = u8::decode(r)?; + + let children = Vec::decode(r)?; + + let redirect_node = if flags & 0x08 != 0 { + Some(VarInt::decode(r)?) + } else { + None + }; + + let node_data = match flags & 0x3 { + 0 => NodeData::Root, + 1 => NodeData::Literal { + name: <&str>::decode(r)?, + }, + 2 => NodeData::Argument { + name: <&str>::decode(r)?, + parser: Parser::decode(r)?, + suggestion: if flags & 0x10 != 0 { + Some(match Ident::>::decode(r)?.as_str() { + "minecraft:ask_server" => Suggestion::AskServer, + "minecraft:all_recipes" => Suggestion::AllRecipes, + "minecraft:available_sounds" => Suggestion::AvailableSounds, + "minecraft:available_biomes" => Suggestion::AvailableBiomes, + "minecraft:summonable_entities" => Suggestion::SummonableEntities, + other => bail!("unknown command suggestion type of \"{other}\""), + }) + } else { + None + }, + }, + n => bail!("invalid node type of {n}"), + }; + + Ok(Self { + children, + data: node_data, + executable: flags & 0x04 != 0, + redirect_node, + }) + } + } + + impl Encode for Parser<'_> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + match self { + Parser::Bool => 0u8.encode(&mut w)?, + Parser::Float { min, max } => { + 1u8.encode(&mut w)?; + + (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; + + if let Some(min) = min { + min.encode(&mut w)?; + } + + if let Some(max) = max { + max.encode(&mut w)?; + } + } + Parser::Double { min, max } => { + 2u8.encode(&mut w)?; + + (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; + + if let Some(min) = min { + min.encode(&mut w)?; + } + + if let Some(max) = max { + max.encode(&mut w)?; + } + } + Parser::Integer { min, max } => { + 3u8.encode(&mut w)?; + + (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; + + if let Some(min) = min { + min.encode(&mut w)?; + } + + if let Some(max) = max { + max.encode(&mut w)?; + } + } + Parser::Long { min, max } => { + 4u8.encode(&mut w)?; + + (min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?; + + if let Some(min) = min { + min.encode(&mut w)?; + } + + if let Some(max) = max { + max.encode(&mut w)?; + } + } + Parser::String(arg) => { + 5u8.encode(&mut w)?; + arg.encode(&mut w)?; + } + Parser::Entity { + single, + only_players, + } => { + 6u8.encode(&mut w)?; + (*single as u8 | (*only_players as u8 * 0x2)).encode(&mut w)?; + } + Parser::GameProfile => 7u8.encode(&mut w)?, + Parser::BlockPos => 8u8.encode(&mut w)?, + Parser::ColumnPos => 9u8.encode(&mut w)?, + Parser::Vec3 => 10u8.encode(&mut w)?, + Parser::Vec2 => 11u8.encode(&mut w)?, + Parser::BlockState => 12u8.encode(&mut w)?, + Parser::BlockPredicate => 13u8.encode(&mut w)?, + Parser::ItemStack => 14u8.encode(&mut w)?, + Parser::ItemPredicate => 15u8.encode(&mut w)?, + Parser::Color => 16u8.encode(&mut w)?, + Parser::Component => 17u8.encode(&mut w)?, + Parser::Message => 18u8.encode(&mut w)?, + Parser::NbtCompoundTag => 19u8.encode(&mut w)?, + Parser::NbtTag => 20u8.encode(&mut w)?, + Parser::NbtPath => 21u8.encode(&mut w)?, + Parser::Objective => 22u8.encode(&mut w)?, + Parser::ObjectiveCriteria => 23u8.encode(&mut w)?, + Parser::Operation => 24u8.encode(&mut w)?, + Parser::Particle => 25u8.encode(&mut w)?, + Parser::Angle => 26u8.encode(&mut w)?, + Parser::Rotation => 27u8.encode(&mut w)?, + Parser::ScoreboardSlot => 28u8.encode(&mut w)?, + Parser::ScoreHolder { allow_multiple } => { + 29u8.encode(&mut w)?; + allow_multiple.encode(&mut w)?; + } + Parser::Swizzle => 30u8.encode(&mut w)?, + Parser::Team => 31u8.encode(&mut w)?, + Parser::ItemSlot => 32u8.encode(&mut w)?, + Parser::ResourceLocation => 33u8.encode(&mut w)?, + Parser::Function => 34u8.encode(&mut w)?, + Parser::EntityAnchor => 35u8.encode(&mut w)?, + Parser::IntRange => 36u8.encode(&mut w)?, + Parser::FloatRange => 37u8.encode(&mut w)?, + Parser::Dimension => 38u8.encode(&mut w)?, + Parser::GameMode => 39u8.encode(&mut w)?, + Parser::Time => 40u8.encode(&mut w)?, + Parser::ResourceOrTag { registry } => { + 41u8.encode(&mut w)?; + registry.encode(&mut w)?; + } + Parser::ResourceOrTagKey { registry } => { + 42u8.encode(&mut w)?; + registry.encode(&mut w)?; + } + Parser::Resource { registry } => { + 43u8.encode(&mut w)?; + registry.encode(&mut w)?; + } + Parser::ResourceKey { registry } => { + 44u8.encode(&mut w)?; + registry.encode(&mut w)?; + } + Parser::TemplateMirror => 45u8.encode(&mut w)?, + Parser::TemplateRotation => 46u8.encode(&mut w)?, + Parser::Uuid => 47u8.encode(&mut w)?, + } + + Ok(()) + } + } + + impl<'a> Decode<'a> for Parser<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + fn decode_min_max<'a, T: Decode<'a>>( + r: &mut &'a [u8], + ) -> anyhow::Result<(Option, Option)> { + let flags = u8::decode(r)?; + + let min = if flags & 0x1 != 0 { + Some(T::decode(r)?) + } else { + None + }; + + let max = if flags & 0x2 != 0 { + Some(T::decode(r)?) + } else { + None + }; + + Ok((min, max)) + } + + Ok(match u8::decode(r)? { + 0 => Self::Bool, + 1 => { + let (min, max) = decode_min_max(r)?; + Self::Float { min, max } + } + 2 => { + let (min, max) = decode_min_max(r)?; + Self::Double { min, max } + } + 3 => { + let (min, max) = decode_min_max(r)?; + Self::Integer { min, max } + } + 4 => { + let (min, max) = decode_min_max(r)?; + Self::Long { min, max } + } + 5 => Self::String(StringArg::decode(r)?), + 6 => { + let flags = u8::decode(r)?; + Self::Entity { + single: flags & 0x1 != 0, + only_players: flags & 0x2 != 0, + } + } + 7 => Self::GameProfile, + 8 => Self::BlockPos, + 9 => Self::ColumnPos, + 10 => Self::Vec3, + 11 => Self::Vec2, + 12 => Self::BlockState, + 13 => Self::BlockPredicate, + 14 => Self::ItemStack, + 15 => Self::ItemPredicate, + 16 => Self::Color, + 17 => Self::Component, + 18 => Self::Message, + 19 => Self::NbtCompoundTag, + 20 => Self::NbtTag, + 21 => Self::NbtPath, + 22 => Self::Objective, + 23 => Self::ObjectiveCriteria, + 24 => Self::Operation, + 25 => Self::Particle, + 26 => Self::Angle, + 27 => Self::Rotation, + 28 => Self::ScoreboardSlot, + 29 => Self::ScoreHolder { + allow_multiple: bool::decode(r)?, + }, + 30 => Self::Swizzle, + 31 => Self::Team, + 32 => Self::ItemSlot, + 33 => Self::ResourceLocation, + 34 => Self::Function, + 35 => Self::EntityAnchor, + 36 => Self::IntRange, + 37 => Self::FloatRange, + 38 => Self::Dimension, + 39 => Self::GameMode, + 40 => Self::Time, + 41 => Self::ResourceOrTag { + registry: Ident::decode(r)?, + }, + 42 => Self::ResourceOrTagKey { + registry: Ident::decode(r)?, + }, + 43 => Self::Resource { + registry: Ident::decode(r)?, + }, + 44 => Self::ResourceKey { + registry: Ident::decode(r)?, + }, + 45 => Self::TemplateMirror, + 46 => Self::TemplateRotation, + 47 => Self::Uuid, + n => bail!("unknown command parser ID of {n}"), + }) + } + } +} + +/// Move to valence_map? +pub mod map { + use super::*; + + #[derive(Clone, PartialEq, Debug, Packet)] + #[packet(id = packet_id::MAP_UPDATE_S2C)] + pub struct MapUpdateS2c<'a> { + pub map_id: VarInt, + pub scale: i8, + pub locked: bool, + pub icons: Option>>, + pub data: Option>, + } + + #[derive(Clone, PartialEq, Debug, Encode, Decode)] + pub struct Icon<'a> { + pub icon_type: IconType, + /// In map coordinates; -128 for furthest left, +127 for furthest right + pub position: [i8; 2], + /// 0 is a vertical icon and increments by 22.5° + pub direction: i8, + pub display_name: Option>, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum IconType { + WhiteArrow, + GreenArrow, + RedArrow, + BlueArrow, + WhiteCross, + RedPointer, + WhiteCircle, + SmallWhiteCircle, + Mansion, + Temple, + WhiteBanner, + OrangeBanner, + MagentaBanner, + LightBlueBanner, + YellowBanner, + LimeBanner, + PinkBanner, + GrayBanner, + LightGrayBanner, + CyanBanner, + PurpleBanner, + BlueBanner, + BrownBanner, + GreenBanner, + RedBanner, + BlackBanner, + TreasureMarker, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode)] + pub struct Data<'a> { + pub columns: u8, + pub rows: u8, + pub position: [i8; 2], + pub data: &'a [u8], + } + + impl Encode for MapUpdateS2c<'_> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + self.map_id.encode(&mut w)?; + self.scale.encode(&mut w)?; + self.locked.encode(&mut w)?; + self.icons.encode(&mut w)?; + + match self.data { + None => 0u8.encode(&mut w)?, + Some(data) => data.encode(&mut w)?, + } + + Ok(()) + } + } + + impl<'a> Decode<'a> for MapUpdateS2c<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let map_id = VarInt::decode(r)?; + let scale = i8::decode(r)?; + let locked = bool::decode(r)?; + let icons = >>>::decode(r)?; + let columns = u8::decode(r)?; + + let data = if columns > 0 { + let rows = u8::decode(r)?; + let position = <[i8; 2]>::decode(r)?; + let data = <&'a [u8]>::decode(r)?; + + Some(Data { + columns, + rows, + position, + data, + }) + } else { + None + }; + + Ok(Self { + map_id, + scale, + locked, + icons, + data, + }) + } + } +} diff --git a/crates/valence_core/src/packet/raw.rs b/crates/valence_core/src/protocol/raw.rs similarity index 58% rename from crates/valence_core/src/packet/raw.rs rename to crates/valence_core/src/protocol/raw.rs index 93fc0016d..e49e8c680 100644 --- a/crates/valence_core/src/packet/raw.rs +++ b/crates/valence_core/src/protocol/raw.rs @@ -1,7 +1,7 @@ use std::io::Write; use std::mem; -use crate::packet::{Decode, Encode, Packet}; +use crate::protocol::{Decode, Encode}; /// While [encoding], the contained slice is written directly to the output /// without any length prefix or metadata. @@ -37,26 +37,3 @@ impl<'a> From> for &'a [u8] { value.0 } } - -/// A fake [`Packet`] which simply reads all data into a slice, or writes all -/// data from a slice. The packet ID is included in the slice. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] -pub struct RawPacket<'a>(pub &'a [u8]); - -impl<'a> Packet<'a> for RawPacket<'a> { - fn packet_id(&self) -> i32 { - -1 - } - - fn packet_name(&self) -> &str { - "RawPacket" - } - - fn encode_packet(&self, mut w: impl Write) -> anyhow::Result<()> { - Ok(w.write_all(self.0)?) - } - - fn decode_packet(r: &mut &'a [u8]) -> anyhow::Result { - Ok(Self(mem::take(r))) - } -} diff --git a/crates/valence_core/src/protocol/s2c.rs b/crates/valence_core/src/protocol/s2c.rs new file mode 100644 index 000000000..567bc0cc4 --- /dev/null +++ b/crates/valence_core/src/protocol/s2c.rs @@ -0,0 +1,338 @@ +/* +pub use advancement_update::AdvancementUpdateS2c; +pub use block_breaking_progress::BlockBreakingProgressS2c; +pub use block_entity_update::BlockEntityUpdateS2c; +pub use block_event::BlockEventS2c; +pub use block_update::BlockUpdateS2c; +pub use boss_bar::BossBarS2c; +pub use bundle_splitter::BundleSplitter; +pub use chat_message::ChatMessageS2c; +pub use chat_suggestions::ChatSuggestionsS2c; +pub use chunk_biome_data::ChunkBiomeDataS2c; +pub use chunk_data::ChunkDataS2c; +pub use chunk_delta_update::ChunkDeltaUpdateS2c; +pub use chunk_load_distance::ChunkLoadDistanceS2c; +pub use chunk_render_distance_center::ChunkRenderDistanceCenterS2c; +pub use clear_title::ClearTitleS2c; +pub use close_screen::CloseScreenS2c; +pub use command_suggestions::CommandSuggestionsS2c; +pub use command_tree::CommandTreeS2c; +pub use cooldown_update::CooldownUpdateS2c; +pub use craft_failed_response::CraftFailedResponseS2c; +pub use custom_payload::CustomPayloadS2c; +pub use damage_tilt::DamageTiltS2c; +pub use death_message::DeathMessageS2c; +pub use difficulty::DifficultyS2c; +pub use disconnect::DisconnectS2c; +pub use end_combat::EndCombatS2c; +pub use enter_combat::EnterCombatS2c; +pub use entities_destroy::EntitiesDestroyS2c; +pub use entity_animation::EntityAnimationS2c; +pub use entity_attach::EntityAttachS2c; +pub use entity_attributes::EntityAttributesS2c; +pub use entity_damage::EntityDamageS2c; +pub use entity_equipment_update::EntityEquipmentUpdateS2c; +pub use entity_move::{MoveRelative, Rotate, RotateAndMoveRelative}; +pub use entity_passengers_set::EntityPassengersSetS2c; +pub use entity_position::EntityPositionS2c; +pub use entity_set_head_yaw::EntitySetHeadYawS2c; +pub use entity_spawn::EntitySpawnS2c; +pub use entity_status::EntityStatusS2c; +pub use entity_status_effect::EntityStatusEffectS2c; +pub use entity_tracker_update::EntityTrackerUpdateS2c; +pub use entity_velocity_update::EntityVelocityUpdateS2c; +pub use experience_bar_update::ExperienceBarUpdateS2c; +pub use experience_orb_spawn::ExperienceOrbSpawnS2c; +pub use explosion::ExplosionS2c; +pub use features::FeaturesS2c; +pub use game_join::GameJoinS2c; +pub use game_message::GameMessageS2c; +pub use game_state_change::GameStateChangeS2c; +pub use health_update::HealthUpdateS2c; +pub use inventory::InventoryS2c; +pub use item_pickup_animation::ItemPickupAnimationS2c; +pub use keep_alive::KeepAliveS2c; +pub use light_update::LightUpdateS2c; +pub use look_at::LookAtS2c; +pub use map_update::MapUpdateS2c; +pub use nbt_query_response::NbtQueryResponseS2c; +pub use open_horse_screen::OpenHorseScreenS2c; +pub use open_screen::OpenScreenS2c; +pub use open_written_book::OpenWrittenBookS2c; +pub use overlay_message::OverlayMessageS2c; +pub use particle::ParticleS2c; +pub use play_ping::PlayPingS2c; +pub use play_sound::PlaySoundS2c; +pub use play_sound_from_entity::PlaySoundFromEntityS2c; +pub use player_abilities::PlayerAbilitiesS2c; +pub use player_action_response::PlayerActionResponseS2c; +pub use player_list::PlayerListS2c; +pub use player_list_header::PlayerListHeaderS2c; +pub use player_position_look::PlayerPositionLookS2c; +pub use player_remove::PlayerRemoveS2c; +pub use player_respawn::PlayerRespawnS2c; +pub use player_spawn::PlayerSpawnS2c; +pub use player_spawn_position::PlayerSpawnPositionS2c; +pub use profileless_chat_message::ProfilelessChatMessageS2c; +pub use remove_entity_status_effect::RemoveEntityStatusEffectS2c; +pub use remove_message::RemoveMessageS2c; +pub use resource_pack_send::ResourcePackSendS2c; +pub use scoreboard_display::ScoreboardDisplayS2c; +pub use scoreboard_objective_update::ScoreboardObjectiveUpdateS2c; +pub use scoreboard_player_update::ScoreboardPlayerUpdateS2c; +pub use screen_handler_property_update::ScreenHandlerPropertyUpdateS2c; +pub use screen_handler_slot_update::ScreenHandlerSlotUpdateS2c; +pub use select_advancement_tab::SelectAdvancementTabS2c; +pub use server_metadata::ServerMetadataS2c; +pub use set_camera_entity::SetCameraEntityS2c; +pub use set_trade_offers::SetTradeOffersS2c; +pub use sign_editor_open::SignEditorOpenS2c; +pub use simulation_distance::SimulationDistanceS2c; +pub use statistics::StatisticsS2c; +pub use stop_sound::StopSoundS2c; +pub use subtitle::SubtitleS2c; +pub use synchronize_recipes::SynchronizeRecipesS2c; +pub use synchronize_tags::SynchronizeTagsS2c; +pub use team::TeamS2c; +pub use title::TitleS2c; +pub use title_fade::TitleFadeS2c; +pub use unload_chunk::UnloadChunkS2c; +pub use unlock_recipes::UnlockRecipesS2c; +pub use update_selected_slot::UpdateSelectedSlotS2c; +pub use vehicle_move::VehicleMoveS2c; +pub use world_border_center_changed::WorldBorderCenterChangedS2c; +pub use world_border_initialize::WorldBorderInitializeS2c; +pub use world_border_interpolate_size::WorldBorderInterpolateSizeS2c; +pub use world_border_size_changed::WorldBorderSizeChangedS2c; +pub use world_border_warning_blocks_changed::WorldBorderWarningBlocksChangedS2c; +pub use world_border_warning_time_changed::WorldBorderWarningTimeChangedS2c; +pub use world_event::WorldEventS2c; +pub use world_time_update::WorldTimeUpdateS2c; + +pub mod advancement_update; +pub mod block_breaking_progress; +pub mod block_entity_update; +pub mod block_event; +pub mod block_update; +pub mod boss_bar; +pub mod bundle_splitter; +pub mod chat_message; +pub mod chat_suggestions; +pub mod chunk_biome_data; +pub mod chunk_data; +pub mod chunk_delta_update; +pub mod chunk_load_distance; +pub mod chunk_render_distance_center; +pub mod clear_title; +pub mod close_screen; +pub mod command_suggestions; +pub mod command_tree; +pub mod cooldown_update; +pub mod craft_failed_response; +pub mod custom_payload; +pub mod damage_tilt; +pub mod death_message; +pub mod difficulty; +pub mod disconnect; +pub mod end_combat; +pub mod enter_combat; +pub mod entities_destroy; +pub mod entity_animation; +pub mod entity_attach; +pub mod entity_attributes; +pub mod entity_damage; +pub mod entity_equipment_update; +pub mod entity_move; +pub mod entity_passengers_set; +pub mod entity_position; +pub mod entity_set_head_yaw; +pub mod entity_spawn; +pub mod entity_status; +pub mod entity_status_effect; +pub mod entity_tracker_update; +pub mod entity_velocity_update; +pub mod experience_bar_update; +pub mod experience_orb_spawn; +pub mod explosion; +pub mod features; +pub mod game_join; +pub mod game_message; +pub mod game_state_change; +pub mod health_update; +pub mod inventory; +pub mod item_pickup_animation; +pub mod keep_alive; +pub mod light_update; +pub mod look_at; +pub mod map_update; +pub mod nbt_query_response; +pub mod open_horse_screen; +pub mod open_screen; +pub mod open_written_book; +pub mod overlay_message; +pub mod particle; +pub mod play_ping; +pub mod play_sound; +pub mod play_sound_from_entity; +pub mod player_abilities; +pub mod player_action_response; +pub mod player_list; +pub mod player_list_header; +pub mod player_position_look; +pub mod player_remove; +pub mod player_respawn; +pub mod player_spawn; +pub mod player_spawn_position; +pub mod profileless_chat_message; +pub mod remove_entity_status_effect; +pub mod remove_message; +pub mod resource_pack_send; +pub mod scoreboard_display; +pub mod scoreboard_objective_update; +pub mod scoreboard_player_update; +pub mod screen_handler_property_update; +pub mod screen_handler_slot_update; +pub mod select_advancement_tab; +pub mod server_metadata; +pub mod set_camera_entity; +pub mod set_trade_offers; +pub mod sign_editor_open; +pub mod simulation_distance; +pub mod statistics; +pub mod stop_sound; +pub mod subtitle; +pub mod synchronize_recipes; +pub mod synchronize_tags; +pub mod team; +pub mod title; +pub mod title_fade; +pub mod unload_chunk; +pub mod unlock_recipes; +pub mod update_selected_slot; +pub mod vehicle_move; +pub mod world_border_center_changed; +pub mod world_border_initialize; +pub mod world_border_interpolate_size; +pub mod world_border_size_changed; +pub mod world_border_warning_blocks_changed; +pub mod world_border_warning_time_changed; +pub mod world_event; +pub mod world_time_update; + +packet_group! { + #[derive(Clone)] + S2cPlayPacket<'a> { + AdvancementUpdateS2c<'a>, + BlockBreakingProgressS2c, + BlockEntityUpdateS2c<'a>, + BlockEventS2c, + BlockUpdateS2c, + BossBarS2c, + BundleSplitter, + ChatMessageS2c<'a>, + ChatSuggestionsS2c<'a>, + ChunkBiomeDataS2c<'a>, + ChunkDataS2c<'a>, + ChunkDeltaUpdateS2c<'a>, + ChunkLoadDistanceS2c, + ChunkRenderDistanceCenterS2c, + ClearTitleS2c, + CloseScreenS2c, + CommandSuggestionsS2c<'a>, + CommandTreeS2c<'a>, + CooldownUpdateS2c, + CraftFailedResponseS2c<'a>, + CustomPayloadS2c<'a>, + DamageTiltS2c, + DeathMessageS2c<'a>, + DifficultyS2c, + DisconnectS2c<'a>, + EndCombatS2c, + EnterCombatS2c, + EntitiesDestroyS2c<'a>, + EntityAnimationS2c, + EntityAttachS2c, + EntityAttributesS2c<'a>, + EntityDamageS2c, + EntityEquipmentUpdateS2c, + EntityPassengersSetS2c, + EntityPositionS2c, + EntitySetHeadYawS2c, + EntitySpawnS2c, + EntityStatusEffectS2c, + EntityStatusS2c, + EntityTrackerUpdateS2c<'a>, + EntityVelocityUpdateS2c, + ExperienceBarUpdateS2c, + ExperienceOrbSpawnS2c, + ExplosionS2c<'a>, + FeaturesS2c<'a>, + GameJoinS2c<'a>, + GameMessageS2c<'a>, + GameStateChangeS2c, + HealthUpdateS2c, + InventoryS2c<'a>, + ItemPickupAnimationS2c, + KeepAliveS2c, + LightUpdateS2c, + LookAtS2c, + MapUpdateS2c<'a>, + MoveRelative, + NbtQueryResponseS2c, + OpenHorseScreenS2c, + OpenScreenS2c<'a>, + OpenWrittenBookS2c, + OverlayMessageS2c<'a>, + ParticleS2c<'a>, + PlayerAbilitiesS2c, + PlayerActionResponseS2c, + PlayerListHeaderS2c<'a>, + PlayerListS2c<'a>, + PlayerPositionLookS2c, + PlayerRemoveS2c<'a>, + PlayerRespawnS2c<'a>, + PlayerSpawnPositionS2c, + PlayerSpawnS2c, + PlayPingS2c, + PlaySoundFromEntityS2c, + PlaySoundS2c<'a>, + ProfilelessChatMessageS2c<'a>, + RemoveEntityStatusEffectS2c, + RemoveMessageS2c<'a>, + ResourcePackSendS2c<'a>, + Rotate, + RotateAndMoveRelative, + ScoreboardDisplayS2c<'a>, + ScoreboardObjectiveUpdateS2c<'a>, + ScoreboardPlayerUpdateS2c<'a>, + ScreenHandlerPropertyUpdateS2c, + ScreenHandlerSlotUpdateS2c<'a>, + SelectAdvancementTabS2c<'a>, + ServerMetadataS2c<'a>, + SetCameraEntityS2c, + SetTradeOffersS2c, + SignEditorOpenS2c, + SimulationDistanceS2c, + StatisticsS2c, + StopSoundS2c<'a>, + SubtitleS2c<'a>, + SynchronizeRecipesS2c<'a>, + SynchronizeTagsS2c<'a>, + TeamS2c<'a>, + TitleFadeS2c, + TitleS2c<'a>, + UnloadChunkS2c, + UnlockRecipesS2c<'a>, + UpdateSelectedSlotS2c, + VehicleMoveS2c, + WorldBorderCenterChangedS2c, + WorldBorderInitializeS2c, + WorldBorderInterpolateSizeS2c, + WorldBorderSizeChangedS2c, + WorldBorderWarningBlocksChangedS2c, + WorldBorderWarningTimeChangedS2c, + WorldEventS2c, + WorldTimeUpdateS2c, + } +} +*/ \ No newline at end of file diff --git a/crates/valence_core/src/packet/var_int.rs b/crates/valence_core/src/protocol/var_int.rs similarity index 97% rename from crates/valence_core/src/packet/var_int.rs rename to crates/valence_core/src/protocol/var_int.rs index 52dcdf91a..68a7442d2 100644 --- a/crates/valence_core/src/packet/var_int.rs +++ b/crates/valence_core/src/protocol/var_int.rs @@ -2,12 +2,13 @@ use std::io::{Read, Write}; use anyhow::bail; use byteorder::ReadBytesExt; +use serde::Deserialize; use thiserror::Error; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; /// An `i32` encoded with variable length. -#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Deserialize)] #[repr(transparent)] pub struct VarInt(pub i32); diff --git a/crates/valence_core/src/packet/var_long.rs b/crates/valence_core/src/protocol/var_long.rs similarity index 99% rename from crates/valence_core/src/packet/var_long.rs rename to crates/valence_core/src/protocol/var_long.rs index 13a831fce..4146e5190 100644 --- a/crates/valence_core/src/packet/var_long.rs +++ b/crates/valence_core/src/protocol/var_long.rs @@ -3,7 +3,7 @@ use std::io::Write; use anyhow::bail; use byteorder::ReadBytesExt; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; /// An `i64` encoded with variable length. #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] diff --git a/crates/valence_core/src/sound.rs b/crates/valence_core/src/sound.rs deleted file mode 100644 index bc754522a..000000000 --- a/crates/valence_core/src/sound.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::ident; -use crate::ident::Ident; -use crate::packet::s2c::play::play_sound::SoundId; -use crate::packet::{Decode, Encode}; - -include!(concat!(env!("OUT_DIR"), "/sound.rs")); - -impl Sound { - pub fn to_id(self) -> SoundId<'static> { - SoundId::Direct { - id: self.to_ident().into(), - range: None, - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] -pub enum SoundCategory { - Master, - Music, - Record, - Weather, - Block, - Hostile, - Neutral, - Player, - Ambient, - Voice, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sound_to_soundid() { - assert_eq!( - Sound::BlockBellUse.to_id(), - SoundId::Direct { - id: ident!("block.bell.use").into(), - range: None - }, - ); - } -} diff --git a/crates/valence_core/src/text.rs b/crates/valence_core/src/text.rs index b17b071af..eb13f87d6 100644 --- a/crates/valence_core/src/text.rs +++ b/crates/valence_core/src/text.rs @@ -11,7 +11,7 @@ use uuid::Uuid; use valence_nbt::Value; use crate::ident::Ident; -use crate::packet::{Decode, Encode}; +use crate::protocol::{Decode, Encode}; /// Represents formatted text in Minecraft's JSON text format. /// diff --git a/crates/valence_core_macros/src/lib.rs b/crates/valence_core_macros/src/lib.rs index 803702f75..1c7f59905 100644 --- a/crates/valence_core_macros/src/lib.rs +++ b/crates/valence_core_macros/src/lib.rs @@ -21,8 +21,8 @@ use proc_macro::TokenStream as StdTokenStream; use proc_macro2::TokenStream; use quote::ToTokens; use syn::{ - parse_quote, Attribute, Error, GenericParam, Generics, Lifetime, LifetimeDef, Lit, Meta, - Result, Variant, + parse_quote, Attribute, GenericParam, Generics, Lifetime, LifetimeParam, LitInt, Result, + Variant, }; mod decode; @@ -30,7 +30,7 @@ mod encode; mod ident; mod packet; -#[proc_macro_derive(Encode, attributes(tag))] +#[proc_macro_derive(Encode, attributes(packet))] pub fn derive_encode(item: StdTokenStream) -> StdTokenStream { match encode::derive_encode(item.into()) { Ok(tokens) => tokens.into(), @@ -38,7 +38,7 @@ pub fn derive_encode(item: StdTokenStream) -> StdTokenStream { } } -#[proc_macro_derive(Decode, attributes(tag))] +#[proc_macro_derive(Decode, attributes(packet))] pub fn derive_decode(item: StdTokenStream) -> StdTokenStream { match decode::derive_decode(item.into()) { Ok(tokens) => tokens.into(), @@ -46,7 +46,7 @@ pub fn derive_decode(item: StdTokenStream) -> StdTokenStream { } } -#[proc_macro_derive(Packet, attributes(packet_id))] +#[proc_macro_derive(Packet, attributes(packet))] pub fn derive_packet(item: StdTokenStream) -> StdTokenStream { match packet::derive_packet(item.into()) { Ok(tokens) => tokens.into(), @@ -69,7 +69,7 @@ fn pair_variants_with_discriminants( variants .into_iter() .map(|v| { - if let Some(i) = find_tag_attr(&v.attrs)? { + if let Some(i) = parse_tag_attr(&v.attrs)? { discriminant = i; } @@ -80,19 +80,21 @@ fn pair_variants_with_discriminants( .collect::>() } -fn find_tag_attr(attrs: &[Attribute]) -> Result> { +fn parse_tag_attr(attrs: &[Attribute]) -> Result> { for attr in attrs { - if let Meta::NameValue(nv) = attr.parse_meta()? { - if nv.path.is_ident("tag") { - let span = nv.lit.span(); - return match nv.lit { - Lit::Int(lit) => Ok(Some(lit.base10_parse::()?)), - _ => Err(Error::new( - span, - "discriminant value must be an integer literal", - )), - }; - } + if attr.path().is_ident("packet") { + let mut res = 0; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("tag") { + res = meta.value()?.parse::()?.base10_parse::()?; + Ok(()) + } else { + Err(meta.error("unrecognized argument")) + } + })?; + + return Ok(Some(res)); } } @@ -115,7 +117,7 @@ fn decode_split_for_impl( if generics.lifetimes().next().is_none() { generics .params - .push(GenericParam::Lifetime(LifetimeDef::new(lifetime))); + .push(GenericParam::Lifetime(LifetimeParam::new(lifetime))); impl_generics = generics.split_for_impl().0.to_token_stream(); } diff --git a/crates/valence_core_macros/src/packet.rs b/crates/valence_core_macros/src/packet.rs index 169d6c948..6f96d517b 100644 --- a/crates/valence_core_macros/src/packet.rs +++ b/crates/valence_core_macros/src/packet.rs @@ -1,92 +1,66 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::{parse2, parse_quote, Attribute, DeriveInput, Error, Lit, LitInt, Meta, Result}; +use syn::spanned::Spanned; +use syn::{parse2, Attribute, DeriveInput, Error, Expr, LitInt, Result}; -use crate::{add_trait_bounds, decode_split_for_impl}; +use crate::add_trait_bounds; pub(super) fn derive_packet(item: TokenStream) -> Result { let mut input = parse2::(item)?; - let Some(packet_id) = find_packet_id_attr(&input.attrs)? else { - return Err(Error::new( - input.ident.span(), - "cannot derive `Packet` without `#[packet_id = ...]` helper attribute", - )) + let Some(packet_attr) = parse_packet_helper_attr(&input.attrs)? else { + return Err(Error::new(input.span(), "missing `packet` attribute")); }; - let lifetime = input - .generics - .lifetimes() - .next() - .map(|l| l.lifetime.clone()) - .unwrap_or_else(|| parse_quote!('a)); - - add_trait_bounds( - &mut input.generics, - quote!(::valence_core::__private::Encode), - ); - - add_trait_bounds( - &mut input.generics, - quote!(::valence_core::__private::Decode<#lifetime>), - ); + let Some(packet_id) = packet_attr.id else { + return Err(Error::new(packet_attr.span, "missing `id = ...` value from packet attribute")); + }; add_trait_bounds(&mut input.generics, quote!(::std::fmt::Debug)); - let (impl_generics, ty_generics, where_clause) = - decode_split_for_impl(input.generics, lifetime.clone()); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let name_str = input.ident.to_string(); let name = input.ident; Ok(quote! { - impl #impl_generics ::valence_core::__private::Packet<#lifetime> for #name #ty_generics + impl #impl_generics ::valence_core::__private::Packet for #name #ty_generics #where_clause { - const PACKET_ID: i32 = #packet_id; - - fn packet_id(&self) -> i32 { - #packet_id - } - - fn packet_name(&self) -> &str { - #name_str - } - - fn encode_packet(&self, mut w: impl ::std::io::Write) -> ::valence_core::__private::Result<()> { - use ::valence_core::__private::{Encode, Context, VarInt}; - - VarInt(#packet_id) - .encode(&mut w) - .context("failed to encode packet ID")?; - - Encode::encode(self, w) - } - - fn decode_packet(r: &mut &#lifetime [u8]) -> ::valence_core::__private::Result { - use ::valence_core::__private::{Decode, Context, VarInt}; - - let id = VarInt::decode(r).context("failed to decode packet ID")?.0; - ::valence_core::__private::ensure!( - id == #packet_id, "unexpected packet ID {} (expected {})", id, #packet_id - ); - - Decode::decode(r) - } + const ID: i32 = #packet_id; + const NAME: &'static str = #name_str; } }) } -fn find_packet_id_attr(attrs: &[Attribute]) -> Result> { +struct PacketAttr { + span: Span, + id: Option, + tag: Option, +} + +fn parse_packet_helper_attr(attrs: &[Attribute]) -> Result> { for attr in attrs { - if let Meta::NameValue(nv) = attr.parse_meta()? { - if nv.path.is_ident("packet_id") { - let span = nv.lit.span(); - return match nv.lit { - Lit::Int(i) => Ok(Some(i)), - _ => Err(Error::new(span, "packet ID must be an integer literal")), - }; - } + if attr.path().is_ident("packet") { + let mut res = PacketAttr { + span: attr.span(), + id: None, + tag: None, + }; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("id") { + res.id = Some(meta.value()?.parse::()?); + Ok(()) + } else if meta.path.is_ident("tag") { + res.tag = Some(meta.value()?.parse::()?.base10_parse::()?); + Ok(()) + } else { + Err(meta.error("unrecognized packet argument")) + } + })?; + + return Ok(Some(res)); } } diff --git a/crates/valence_entity/Cargo.toml b/crates/valence_entity/Cargo.toml index 0d91012c4..1e6d564b9 100644 --- a/crates/valence_entity/Cargo.toml +++ b/crates/valence_entity/Cargo.toml @@ -5,15 +5,16 @@ edition.workspace = true [dependencies] anyhow.workspace = true -uuid.workspace = true -tracing.workspace = true -rustc-hash.workspace = true -paste.workspace = true -glam.workspace = true bevy_app.workspace = true bevy_ecs.workspace = true -valence_core.workspace = true +bitfield-struct.workspace = true +glam.workspace = true +paste.workspace = true +rustc-hash.workspace = true +tracing.workspace = true +uuid.workspace = true valence_block.workspace = true +valence_core.workspace = true valence_nbt.workspace = true [build-dependencies] @@ -24,4 +25,4 @@ syn.workspace = true serde_json.workspace = true heck.workspace = true serde.workspace = true -valence_build_utils.workspace = true +valence_build_utils.workspace = true \ No newline at end of file diff --git a/crates/valence_entity/build.rs b/crates/valence_entity/build.rs index 1c21e854b..09ac95cb4 100644 --- a/crates/valence_entity/build.rs +++ b/crates/valence_entity/build.rs @@ -140,7 +140,7 @@ impl Value { Value::BlockState(_) => quote!(valence_block::BlockState), Value::OptionalBlockState(_) => quote!(valence_block::BlockState), Value::NbtCompound(_) => quote!(valence_nbt::Compound), - Value::Particle(_) => quote!(valence_core::packet::s2c::play::particle::Particle), + Value::Particle(_) => quote!(valence_core::particle::Particle), Value::VillagerData { .. } => quote!(crate::VillagerData), Value::OptionalInt(_) => quote!(Option), Value::EntityPose(_) => quote!(crate::Pose), @@ -189,7 +189,7 @@ impl Value { quote!(None) } Value::Facing(f) => { - let variant = ident(f.to_pascal_case()); + let variant = ident(f.replace('.', "_").to_pascal_case()); quote!(valence_core::direction::Direction::#variant) } Value::OptionalUuid(uuid) => { @@ -208,16 +208,16 @@ impl Value { quote!(valence_nbt::Compound::default()) } Value::Particle(p) => { - let variant = ident(p.to_pascal_case()); - quote!(valence_core::packet::s2c::play::particle::Particle::#variant) + let variant = ident(p.replace('.', "_").to_pascal_case()); + quote!(valence_core::particle::Particle::#variant) } Value::VillagerData { typ, profession, level, } => { - let typ = ident(typ.to_pascal_case()); - let profession = ident(profession.to_pascal_case()); + let typ = ident(typ.replace('.', "_").to_pascal_case()); + let profession = ident(profession.replace('.', "_").to_pascal_case()); quote! { crate::VillagerData { kind: crate::VillagerKind::#typ, @@ -231,24 +231,24 @@ impl Value { quote!(None) } Value::EntityPose(p) => { - let variant = ident(p.to_pascal_case()); + let variant = ident(p.replace('.', "_").to_pascal_case()); quote!(crate::Pose::#variant) } Value::CatVariant(c) => { - let variant = ident(c.to_pascal_case()); + let variant = ident(c.replace('.', "_").to_pascal_case()); quote!(crate::CatKind::#variant) } Value::FrogVariant(f) => { - let variant = ident(f.to_pascal_case()); + let variant = ident(f.replace('.', "_").to_pascal_case()); quote!(crate::FrogKind::#variant) } Value::OptionalGlobalPos(_) => quote!(()), Value::PaintingVariant(p) => { - let variant = ident(p.to_pascal_case()); + let variant = ident(p.replace('.', "_").to_pascal_case()); quote!(crate::PaintingKind::#variant) } Value::SnifferState(s) => { - let state = ident(s.to_pascal_case()); + let state = ident(s.replace('.', "_").to_pascal_case()); quote!(crate::SnifferState::#state) } Value::Vector3f { x, y, z } => quote!(glam::f32::Vec3::new(#x, #y, #z)), @@ -297,7 +297,9 @@ fn build() -> anyhow::Result { for (entity_name, entity) in entities.clone() { let entity_name_ident = ident(&entity_name); - let stripped_shouty_entity_name = strip_entity_suffix(&entity_name).to_shouty_snake_case(); + let stripped_shouty_entity_name = strip_entity_suffix(&entity_name) + .replace('.', "_") + .to_shouty_snake_case(); let stripped_shouty_entity_name_ident = ident(&stripped_shouty_entity_name); let stripped_snake_entity_name = strip_entity_suffix(&entity_name).to_snake_case(); let stripped_snake_entity_name_ident = ident(&stripped_snake_entity_name); @@ -351,7 +353,8 @@ fn build() -> anyhow::Result { let snake_entity_name_ident = ident(entity_name.to_snake_case()); let stripped_snake_entity_name_ident = ident(stripped_entity_name.to_snake_case()); - let pascal_entity_name_ident = ident(entity_name.to_pascal_case()); + let pascal_entity_name_ident = + ident(entity_name.replace('.', "_").to_pascal_case()); bundle_fields.extend([quote! { pub #snake_entity_name_ident: super::#stripped_snake_entity_name_ident::#pascal_entity_name_ident, @@ -363,7 +366,7 @@ fn build() -> anyhow::Result { } MarkerOrField::Field { entity_name, field } => { let snake_field_name = field.name.to_snake_case(); - let pascal_field_name = field.name.to_pascal_case(); + let pascal_field_name = field.name.replace('.', "_").to_pascal_case(); let pascal_field_name_ident = ident(&pascal_field_name); let stripped_entity_name = strip_entity_suffix(entity_name); let stripped_snake_entity_name = stripped_entity_name.to_snake_case(); @@ -444,7 +447,7 @@ fn build() -> anyhow::Result { } for field in &entity.fields { - let pascal_field_name_ident = ident(field.name.to_pascal_case()); + let pascal_field_name_ident = ident(field.name.replace('.', "_").to_pascal_case()); let snake_field_name = field.name.to_snake_case(); let inner_type = field.default_value.field_type(); let default_expr = field.default_value.default_expr(); @@ -522,7 +525,7 @@ fn build() -> anyhow::Result { .entity_status .into_iter() .map(|(name, code)| { - let name = ident(name.to_pascal_case()); + let name = ident(name.replace('.', "_").to_pascal_case()); let code = code as isize; quote! { @@ -535,7 +538,7 @@ fn build() -> anyhow::Result { .entity_animation .into_iter() .map(|(name, code)| { - let name = ident(name.to_pascal_case()); + let name = ident(name.replace('.', "_").to_pascal_case()); let code = code as isize; quote! { diff --git a/crates/valence_entity/src/hitbox.rs b/crates/valence_entity/src/hitbox.rs index dab61ae57..ae6fc5f7b 100644 --- a/crates/valence_entity/src/hitbox.rs +++ b/crates/valence_entity/src/hitbox.rs @@ -1,294 +1,562 @@ -// TODO: Make a `Hitbox` component and plugin. - -/* -/// Returns the hitbox of this entity. -/// -/// The hitbox describes the space that an entity occupies. Clients interact -/// with this space to create an [interact event]. -/// -/// The hitbox of an entity is determined by its position, entity type, and -/// other state specific to that type. -/// -/// [interact event]: crate::client::event::PlayerInteract -pub fn hitbox(&self) -> Aabb { - fn baby(is_baby: bool, adult_hitbox: [f64; 3]) -> [f64; 3] { - if is_baby { - adult_hitbox.map(|a| a / 2.0) - } else { - adult_hitbox +#![allow(clippy::type_complexity)] + +use bevy_app::{App, CoreSet, Plugin}; +use bevy_ecs::prelude::{Component, Entity, SystemSet}; +use bevy_ecs::query::{Added, Changed, Or, With}; +use bevy_ecs::schedule::{IntoSystemConfig, IntoSystemConfigs, IntoSystemSetConfig}; +use bevy_ecs::system::{Commands, Query}; +use glam::{DVec3, UVec3, Vec3Swizzles}; +use valence_core::aabb::Aabb; +use valence_core::direction::Direction; + +use crate::*; + +#[derive(SystemSet, Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub struct HitboxShapeUpdateSet; + +#[derive(SystemSet, Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub struct HitboxComponentsAddSet; + +#[derive(SystemSet, Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub struct HitboxUpdateSet; + +pub struct HitboxPlugin; + +#[derive(Resource)] +/// Settings for hitbox plugin +pub struct EntityHitboxSettings { + /// Controls if a plugin should add hitbox component on each created entity. + /// Otherwise you should add hitbox component by yourself in order to use + /// it. + pub add_hitbox_component: bool, +} + +impl Default for EntityHitboxSettings { + fn default() -> Self { + Self { + add_hitbox_component: true, } } +} - fn item_frame(pos: DVec3, rotation: i32) -> Aabb { - let mut center_pos = pos + 0.5; - - match rotation { - 0 => center_pos.y += 0.46875, - 1 => center_pos.y -= 0.46875, - 2 => center_pos.z += 0.46875, - 3 => center_pos.z -= 0.46875, - 4 => center_pos.x += 0.46875, - 5 => center_pos.x -= 0.46875, - _ => center_pos.y -= 0.46875, - }; +impl Plugin for HitboxPlugin { + fn build(&self, app: &mut App) { + app.init_resource::() + .configure_set(HitboxShapeUpdateSet.in_base_set(CoreSet::PreUpdate)) + .add_systems( + ( + update_constant_hitbox, + update_warden_hitbox, + update_area_effect_cloud_hitbox, + update_armor_stand_hitbox, + update_passive_child_hitbox, + update_zombie_hitbox, + update_piglin_hitbox, + update_zoglin_hitbox, + update_player_hitbox, + update_item_frame_hitbox, + update_slime_hitbox, + update_painting_hitbox, + update_shulker_hitbox, + ) + .in_set(HitboxShapeUpdateSet), + ) + .configure_set(HitboxComponentsAddSet.in_base_set(CoreSet::PostUpdate)) + .add_system(add_hitbox_component.in_set(HitboxComponentsAddSet)) + .configure_set( + HitboxUpdateSet + .in_base_set(CoreSet::PreUpdate) + .after(HitboxShapeUpdateSet), + ) + .add_system(update_hitbox.in_set(HitboxUpdateSet)); + } +} - let bounds = DVec3::from(match rotation { - 0 | 1 => [0.75, 0.0625, 0.75], - 2 | 3 => [0.75, 0.75, 0.0625], - 4 | 5 => [0.0625, 0.75, 0.75], - _ => [0.75, 0.0625, 0.75], - }); +/// Size of hitbox. The only way to manipulate it without losing it on the next +/// tick is using a marker entity. Marker entity's hitbox is never updated. +#[derive(Component, Debug, PartialEq)] +pub struct HitboxShape(pub Aabb); - Aabb { - min: center_pos - bounds / 2.0, - max: center_pos + bounds / 2.0, +#[derive(Component, Debug)] +/// Hitbox, aabb of which is calculated each tick using it's position and +/// [`Hitbox`]. In order to change size of this hitbox you need to change +/// [`Hitbox`] +pub struct Hitbox(Aabb); + +impl HitboxShape { + pub const ZERO: HitboxShape = HitboxShape(Aabb { + min: DVec3::ZERO, + max: DVec3::ZERO, + }); + + pub fn get(&self) -> Aabb { + self.0 + } + + pub(crate) fn centered(&mut self, size: DVec3) { + self.0 = Aabb::from_bottom_size(DVec3::ZERO, size); + } + + pub(crate) fn in_world(&self, pos: DVec3) -> Aabb { + self.0 + pos + } +} + +impl Hitbox { + pub fn get(&self) -> Aabb { + self.0 + } +} + +fn add_hitbox_component( + settings: Res, + mut commands: Commands, + query: Query<(Entity, &Position), Added>, + alt_query: Query<(Entity, &Position, &HitboxShape), Added>, +) { + if settings.add_hitbox_component { + for (entity, pos) in query.iter() { + commands + .entity(entity) + .insert(HitboxShape::ZERO) + .insert(Hitbox(HitboxShape::ZERO.in_world(pos.0))); + } + } else { + for (entity, pos, hitbox) in alt_query.iter() { + commands + .entity(entity) + .insert(Hitbox(hitbox.in_world(pos.0))); } } +} - let dimensions = match &self.data { - TrackedData::Allay(_) => [0.6, 0.35, 0.6], - TrackedData::ChestBoat(_) => [1.375, 0.5625, 1.375], - TrackedData::Frog(_) => [0.5, 0.5, 0.5], - TrackedData::Tadpole(_) => [0.4, 0.3, 0.4], - TrackedData::Warden(e) => match e.get_pose() { - Pose::Emerging | Pose::Digging => [0.9, 1.0, 0.9], - _ => [0.9, 2.9, 0.9], - }, - TrackedData::AreaEffectCloud(e) => [ - e.get_radius() as f64 * 2.0, - 0.5, - e.get_radius() as f64 * 2.0, - ], - TrackedData::ArmorStand(e) => { - if e.get_marker() { - [0.0, 0.0, 0.0] - } else if e.get_small() { +fn update_hitbox( + mut hitbox_query: Query< + (&mut Hitbox, &HitboxShape, &Position), + Or<(Changed, Changed)>, + >, +) { + for (mut in_world, hitbox, pos) in hitbox_query.iter_mut() { + in_world.0 = hitbox.in_world(pos.0); + } +} + +fn update_constant_hitbox( + mut hitbox_query: Query< + (&mut HitboxShape, &EntityKind), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, entity_kind) in hitbox_query.iter_mut() { + let size = match *entity_kind { + EntityKind::ALLAY => [0.6, 0.35, 0.6], + EntityKind::CHEST_BOAT | EntityKind::BOAT => [1.375, 0.5625, 1.375], + EntityKind::FROG => [0.5, 0.5, 0.5], + EntityKind::TADPOLE => [0.4, 0.3, 0.4], + EntityKind::SPECTRAL_ARROW | EntityKind::ARROW => [0.5, 0.5, 0.5], + EntityKind::AXOLOTL => [1.3, 0.6, 1.3], + EntityKind::BAT => [0.5, 0.9, 0.5], + EntityKind::BLAZE => [0.6, 1.8, 0.6], + EntityKind::CAT => [0.6, 0.7, 0.6], + EntityKind::CAVE_SPIDER => [0.7, 0.5, 0.7], + EntityKind::COD => [0.5, 0.3, 0.5], + EntityKind::CREEPER => [0.6, 1.7, 0.6], + EntityKind::DOLPHIN => [0.9, 0.6, 0.9], + EntityKind::DRAGON_FIREBALL => [1.0, 1.0, 1.0], + EntityKind::ELDER_GUARDIAN => [1.9975, 1.9975, 1.9975], + EntityKind::END_CRYSTAL => [2.0, 2.0, 2.0], + EntityKind::ENDER_DRAGON => [16.0, 8.0, 16.0], + EntityKind::ENDERMAN => [0.6, 2.9, 0.6], + EntityKind::ENDERMITE => [0.4, 0.3, 0.4], + EntityKind::EVOKER => [0.6, 1.95, 0.6], + EntityKind::EVOKER_FANGS => [0.5, 0.8, 0.5], + EntityKind::EXPERIENCE_ORB => [0.5, 0.5, 0.5], + EntityKind::EYE_OF_ENDER => [0.25, 0.25, 0.25], + EntityKind::FALLING_BLOCK => [0.98, 0.98, 0.98], + EntityKind::FIREWORK_ROCKET => [0.25, 0.25, 0.25], + EntityKind::GHAST => [4.0, 4.0, 4.0], + EntityKind::GIANT => [3.6, 12.0, 3.6], + EntityKind::GLOW_SQUID | EntityKind::SQUID => [0.8, 0.8, 0.8], + EntityKind::GUARDIAN => [0.85, 0.85, 0.85], + EntityKind::ILLUSIONER => [0.6, 1.95, 0.6], + EntityKind::IRON_GOLEM => [1.4, 2.7, 1.4], + EntityKind::ITEM => [0.25, 0.25, 0.25], + EntityKind::FIREBALL => [1.0, 1.0, 1.0], + EntityKind::LEASH_KNOT => [0.375, 0.5, 0.375], + EntityKind::LIGHTNING /* | EntityKind::MARKER - marker hitbox */ => [0.0; 3], + EntityKind::LLAMA_SPIT => [0.25, 0.25, 0.25], + EntityKind::MINECART + | EntityKind::CHEST_MINECART + | EntityKind::TNT_MINECART + | EntityKind::HOPPER_MINECART + | EntityKind::FURNACE_MINECART + | EntityKind::SPAWNER_MINECART + | EntityKind::COMMAND_BLOCK_MINECART => [0.98, 0.7, 0.98], + EntityKind::PARROT => [0.5, 0.9, 0.5], + EntityKind::PHANTOM => [0.9, 0.5, 0.9], + EntityKind::PIGLIN_BRUTE => [0.6, 1.95, 0.6], + EntityKind::PILLAGER => [0.6, 1.95, 0.6], + EntityKind::TNT => [0.98, 0.98, 0.98], + EntityKind::PUFFERFISH => [0.7, 0.7, 0.7], + EntityKind::RAVAGER => [1.95, 2.2, 1.95], + EntityKind::SALMON => [0.7, 0.4, 0.7], + EntityKind::SHULKER_BULLET => [0.3125, 0.3125, 0.3125], + EntityKind::SILVERFISH => [0.4, 0.3, 0.4], + EntityKind::SMALL_FIREBALL => [0.3125, 0.3125, 0.3125], + EntityKind::SNOW_GOLEM => [0.7, 1.9, 0.7], + EntityKind::SPIDER => [1.4, 0.9, 1.4], + EntityKind::STRAY => [0.6, 1.99, 0.6], + EntityKind::EGG => [0.25, 0.25, 0.25], + EntityKind::ENDER_PEARL => [0.25, 0.25, 0.25], + EntityKind::EXPERIENCE_BOTTLE => [0.25, 0.25, 0.25], + EntityKind::POTION => [0.25, 0.25, 0.25], + EntityKind::TRIDENT => [0.5, 0.5, 0.5], + EntityKind::TRADER_LLAMA => [0.9, 1.87, 0.9], + EntityKind::TROPICAL_FISH => [0.5, 0.4, 0.5], + EntityKind::VEX => [0.4, 0.8, 0.4], + EntityKind::VINDICATOR => [0.6, 1.95, 0.6], + EntityKind::WITHER => [0.9, 3.5, 0.9], + EntityKind::WITHER_SKELETON => [0.7, 2.4, 0.7], + EntityKind::WITHER_SKULL => [0.3125, 0.3125, 0.3125], + EntityKind::FISHING_BOBBER => [0.25, 0.25, 0.25], + _ => { + continue; + } + } + .into(); + hitbox.centered(size); + } +} + +fn update_warden_hitbox( + mut query: Query< + (&mut HitboxShape, &entity::Pose), + ( + Or<(Changed, Added)>, + With, + ), + >, +) { + for (mut hitbox, entity_pose) in query.iter_mut() { + hitbox.centered( + match entity_pose.0 { + Pose::Emerging | Pose::Digging => [0.9, 1.0, 0.9], + _ => [0.9, 2.9, 0.9], + } + .into(), + ); + } +} + +fn update_area_effect_cloud_hitbox( + mut query: Query< + (&mut HitboxShape, &area_effect_cloud::Radius), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, cloud_radius) in query.iter_mut() { + let diameter = cloud_radius.0 as f64 * 2.0; + hitbox.centered([diameter, 0.5, diameter].into()); + } +} + +fn update_armor_stand_hitbox( + mut query: Query< + (&mut HitboxShape, &armor_stand::ArmorStandFlags), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, stand_flags) in query.iter_mut() { + hitbox.centered( + if stand_flags.0 & 16 != 0 { + // Marker armor stand + [0.0; 3] + } else if stand_flags.0 & 1 != 0 { + // Small armor stand [0.5, 0.9875, 0.5] } else { [0.5, 1.975, 0.5] } - } - TrackedData::Arrow(_) => [0.5, 0.5, 0.5], - TrackedData::Axolotl(_) => [1.3, 0.6, 1.3], - TrackedData::Bat(_) => [0.5, 0.9, 0.5], - TrackedData::Bee(e) => baby(e.get_child(), [0.7, 0.6, 0.7]), - TrackedData::Blaze(_) => [0.6, 1.8, 0.6], - TrackedData::Boat(_) => [1.375, 0.5625, 1.375], - TrackedData::Camel(e) => baby(e.get_child(), [1.7, 2.375, 1.7]), - TrackedData::Cat(_) => [0.6, 0.7, 0.6], - TrackedData::CaveSpider(_) => [0.7, 0.5, 0.7], - TrackedData::Chicken(e) => baby(e.get_child(), [0.4, 0.7, 0.4]), - TrackedData::Cod(_) => [0.5, 0.3, 0.5], - TrackedData::Cow(e) => baby(e.get_child(), [0.9, 1.4, 0.9]), - TrackedData::Creeper(_) => [0.6, 1.7, 0.6], - TrackedData::Dolphin(_) => [0.9, 0.6, 0.9], - TrackedData::Donkey(e) => baby(e.get_child(), [1.5, 1.39648, 1.5]), - TrackedData::DragonFireball(_) => [1.0, 1.0, 1.0], - TrackedData::Drowned(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]), - TrackedData::ElderGuardian(_) => [1.9975, 1.9975, 1.9975], - TrackedData::EndCrystal(_) => [2.0, 2.0, 2.0], - TrackedData::EnderDragon(_) => [16.0, 8.0, 16.0], - TrackedData::Enderman(_) => [0.6, 2.9, 0.6], - TrackedData::Endermite(_) => [0.4, 0.3, 0.4], - TrackedData::Evoker(_) => [0.6, 1.95, 0.6], - TrackedData::EvokerFangs(_) => [0.5, 0.8, 0.5], - TrackedData::ExperienceOrb(_) => [0.5, 0.5, 0.5], - TrackedData::EyeOfEnder(_) => [0.25, 0.25, 0.25], - TrackedData::FallingBlock(_) => [0.98, 0.98, 0.98], - TrackedData::FireworkRocket(_) => [0.25, 0.25, 0.25], - TrackedData::Fox(e) => baby(e.get_child(), [0.6, 0.7, 0.6]), - TrackedData::Ghast(_) => [4.0, 4.0, 4.0], - TrackedData::Giant(_) => [3.6, 12.0, 3.6], - TrackedData::GlowItemFrame(e) => return item_frame(self.position, e.get_rotation()), - TrackedData::GlowSquid(_) => [0.8, 0.8, 0.8], - TrackedData::Goat(e) => { - if e.get_pose() == Pose::LongJumping { - baby(e.get_child(), [0.63, 0.91, 0.63]) - } else { - baby(e.get_child(), [0.9, 1.3, 0.9]) + .into(), + ); + } +} + +fn child_hitbox(child: bool, v: DVec3) -> DVec3 { + if child { + v / 2.0 + } else { + v + } +} + +fn update_passive_child_hitbox( + mut query: Query< + (Entity, &mut HitboxShape, &EntityKind, &passive::Child), + Or<(Changed, Added)>, + >, + pose_query: Query<&entity::Pose>, +) { + for (entity, mut hitbox, entity_kind, child) in query.iter_mut() { + let big_s = match *entity_kind { + EntityKind::BEE => [0.7, 0.6, 0.7], + EntityKind::CAMEL => [1.7, 2.375, 1.7], + EntityKind::CHICKEN => [0.4, 0.7, 0.4], + EntityKind::DONKEY => [1.5, 1.39648, 1.5], + EntityKind::FOX => [0.6, 0.7, 0.6], + EntityKind::GOAT => { + if pose_query + .get(entity) + .map_or(false, |v| v.0 == Pose::LongJumping) + { + [0.63, 0.91, 0.63] + } else { + [0.9, 1.3, 0.9] + } } - } - TrackedData::Guardian(_) => [0.85, 0.85, 0.85], - TrackedData::Hoglin(e) => baby(e.get_child(), [1.39648, 1.4, 1.39648]), - TrackedData::Horse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]), - TrackedData::Husk(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]), - TrackedData::Illusioner(_) => [0.6, 1.95, 0.6], - TrackedData::IronGolem(_) => [1.4, 2.7, 1.4], - TrackedData::Item(_) => [0.25, 0.25, 0.25], - TrackedData::ItemFrame(e) => return item_frame(self.position, e.get_rotation()), - TrackedData::Fireball(_) => [1.0, 1.0, 1.0], - TrackedData::LeashKnot(_) => [0.375, 0.5, 0.375], - TrackedData::Lightning(_) => [0.0, 0.0, 0.0], - TrackedData::Llama(e) => baby(e.get_child(), [0.9, 1.87, 0.9]), - TrackedData::LlamaSpit(_) => [0.25, 0.25, 0.25], - TrackedData::MagmaCube(e) => { - let s = 0.5202 * e.get_slime_size() as f64; - [s, s, s] - } - TrackedData::Marker(_) => [0.0, 0.0, 0.0], - TrackedData::Minecart(_) => [0.98, 0.7, 0.98], - TrackedData::ChestMinecart(_) => [0.98, 0.7, 0.98], - TrackedData::CommandBlockMinecart(_) => [0.98, 0.7, 0.98], - TrackedData::FurnaceMinecart(_) => [0.98, 0.7, 0.98], - TrackedData::HopperMinecart(_) => [0.98, 0.7, 0.98], - TrackedData::SpawnerMinecart(_) => [0.98, 0.7, 0.98], - TrackedData::TntMinecart(_) => [0.98, 0.7, 0.98], - TrackedData::Mule(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]), - TrackedData::Mooshroom(e) => baby(e.get_child(), [0.9, 1.4, 0.9]), - TrackedData::Ocelot(e) => baby(e.get_child(), [0.6, 0.7, 0.6]), - TrackedData::Painting(e) => { - let bounds: UVec3 = match e.get_variant() { - PaintingKind::Kebab => [1, 1, 1], - PaintingKind::Aztec => [1, 1, 1], - PaintingKind::Alban => [1, 1, 1], - PaintingKind::Aztec2 => [1, 1, 1], - PaintingKind::Bomb => [1, 1, 1], - PaintingKind::Plant => [1, 1, 1], - PaintingKind::Wasteland => [1, 1, 1], - PaintingKind::Pool => [2, 1, 2], - PaintingKind::Courbet => [2, 1, 2], - PaintingKind::Sea => [2, 1, 2], - PaintingKind::Sunset => [2, 1, 2], - PaintingKind::Creebet => [2, 1, 2], - PaintingKind::Wanderer => [1, 2, 1], - PaintingKind::Graham => [1, 2, 1], - PaintingKind::Match => [2, 2, 2], - PaintingKind::Bust => [2, 2, 2], - PaintingKind::Stage => [2, 2, 2], - PaintingKind::Void => [2, 2, 2], - PaintingKind::SkullAndRoses => [2, 2, 2], - PaintingKind::Wither => [2, 2, 2], - PaintingKind::Fighters => [4, 2, 4], - PaintingKind::Pointer => [4, 4, 4], - PaintingKind::Pigscene => [4, 4, 4], - PaintingKind::BurningSkull => [4, 4, 4], - PaintingKind::Skeleton => [4, 3, 4], - PaintingKind::Earth => [2, 2, 2], - PaintingKind::Wind => [2, 2, 2], - PaintingKind::Water => [2, 2, 2], - PaintingKind::Fire => [2, 2, 2], - PaintingKind::DonkeyKong => [4, 3, 4], + EntityKind::HOGLIN => [1.39648, 1.4, 1.39648], + EntityKind::HORSE | EntityKind::SKELETON_HORSE | EntityKind::ZOMBIE_HORSE => { + [1.39648, 1.6, 1.39648] + } + EntityKind::LLAMA => [0.9, 1.87, 0.9], + EntityKind::MULE => [1.39648, 1.6, 1.39648], + EntityKind::MOOSHROOM => [0.9, 1.4, 0.9], + EntityKind::OCELOT => [0.6, 0.7, 0.6], + EntityKind::PANDA => [1.3, 1.25, 1.3], + EntityKind::PIG => [0.9, 0.9, 0.9], + EntityKind::POLAR_BEAR => [1.4, 1.4, 1.4], + EntityKind::RABBIT => [0.4, 0.5, 0.4], + EntityKind::SHEEP => [0.9, 1.3, 0.9], + EntityKind::TURTLE => { + hitbox.centered( + if child.0 { + [0.36, 0.12, 0.36] + } else { + [1.2, 0.4, 1.2] + } + .into(), + ); + continue; } - .into(); + EntityKind::VILLAGER => [0.6, 1.95, 0.6], + EntityKind::WOLF => [0.6, 0.85, 0.6], + _ => { + continue; + } + }; + hitbox.centered(child_hitbox(child.0, big_s.into())); + } +} + +fn update_zombie_hitbox( + mut query: Query< + (&mut HitboxShape, &zombie::Baby), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, baby) in query.iter_mut() { + hitbox.centered(child_hitbox(baby.0, [0.6, 1.95, 0.6].into())); + } +} - let mut center_pos = self.position + 0.5; +fn update_piglin_hitbox( + mut query: Query< + (&mut HitboxShape, &piglin::Baby), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, baby) in query.iter_mut() { + hitbox.centered(child_hitbox(baby.0, [0.6, 1.95, 0.6].into())); + } +} - let (facing_x, facing_z, cc_facing_x, cc_facing_z) = - match ((self.yaw + 45.0).rem_euclid(360.0) / 90.0) as u8 { - 0 => (0, 1, 1, 0), // South - 1 => (-1, 0, 0, 1), // West - 2 => (0, -1, -1, 0), // North - _ => (1, 0, 0, -1), // East - }; +fn update_zoglin_hitbox( + mut query: Query< + (&mut HitboxShape, &zoglin::Baby), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, baby) in query.iter_mut() { + hitbox.centered(child_hitbox(baby.0, [1.39648, 1.4, 1.39648].into())); + } +} - center_pos.x -= facing_x as f64 * 0.46875; - center_pos.z -= facing_z as f64 * 0.46875; +fn update_player_hitbox( + mut query: Query< + (&mut HitboxShape, &entity::Pose), + ( + Or<(Changed, Added)>, + With, + ), + >, +) { + for (mut hitbox, pose) in query.iter_mut() { + hitbox.centered( + match pose.0 { + Pose::Sleeping | Pose::Dying => [0.2, 0.2, 0.2], + Pose::FallFlying | Pose::Swimming | Pose::SpinAttack => [0.6, 0.6, 0.6], + Pose::Sneaking => [0.6, 1.5, 0.6], + _ => [0.6, 1.8, 0.6], + } + .into(), + ); + } +} - center_pos.x += cc_facing_x as f64 * if bounds.x % 2 == 0 { 0.5 } else { 0.0 }; - center_pos.y += if bounds.y % 2 == 0 { 0.5 } else { 0.0 }; - center_pos.z += cc_facing_z as f64 * if bounds.z % 2 == 0 { 0.5 } else { 0.0 }; +fn update_item_frame_hitbox( + mut query: Query< + (&mut HitboxShape, &item_frame::Rotation), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, rotation) in query.iter_mut() { + let mut center_pos = DVec3::splat(0.5); - let bounds = match (facing_x, facing_z) { - (1, 0) | (-1, 0) => DVec3::new(0.0625, bounds.y as f64, bounds.z as f64), - _ => DVec3::new(bounds.x as f64, bounds.y as f64, 0.0625), - }; + const A: f64 = 0.46875; - return Aabb { - min: center_pos - bounds / 2.0, - max: center_pos + bounds / 2.0, - }; + match rotation.0 { + 0 => center_pos.y += A, + 1 => center_pos.y -= A, + 2 => center_pos.z += A, + 3 => center_pos.z -= A, + 4 => center_pos.x += A, + 5 => center_pos.x -= A, + _ => center_pos.y -= A, } - TrackedData::Panda(e) => baby(e.get_child(), [1.3, 1.25, 1.3]), - TrackedData::Parrot(_) => [0.5, 0.9, 0.5], - TrackedData::Phantom(_) => [0.9, 0.5, 0.9], - TrackedData::Pig(e) => baby(e.get_child(), [0.9, 0.9, 0.9]), - TrackedData::Piglin(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]), - TrackedData::PiglinBrute(_) => [0.6, 1.95, 0.6], - TrackedData::Pillager(_) => [0.6, 1.95, 0.6], - TrackedData::PolarBear(e) => baby(e.get_child(), [1.4, 1.4, 1.4]), - TrackedData::Tnt(_) => [0.98, 0.98, 0.98], - TrackedData::Pufferfish(_) => [0.7, 0.7, 0.7], - TrackedData::Rabbit(e) => baby(e.get_child(), [0.4, 0.5, 0.4]), - TrackedData::Ravager(_) => [1.95, 2.2, 1.95], - TrackedData::Salmon(_) => [0.7, 0.4, 0.7], - TrackedData::Sheep(e) => baby(e.get_child(), [0.9, 1.3, 0.9]), - TrackedData::Shulker(e) => { - const PI: f64 = std::f64::consts::PI; - - let pos = self.position + 0.5; - let mut min = pos - 0.5; - let mut max = pos + 0.5; - - let peek = 0.5 - f64::cos(e.get_peek_amount() as f64 * 0.01 * PI) * 0.5; - - match e.get_attached_face() { - Facing::Down => max.y += peek, - Facing::Up => min.y -= peek, - Facing::North => max.z += peek, - Facing::South => min.z -= peek, - Facing::West => max.x += peek, - Facing::East => min.x -= peek, - } - return Aabb { min, max }; + const BOUNDS23: DVec3 = DVec3::new(0.375, 0.375, 0.03125); + + let bounds = match rotation.0 { + 2 | 3 => BOUNDS23, + 4 | 5 => BOUNDS23.zxy(), + _ => BOUNDS23.zxy(), + }; + + hitbox.0 = Aabb { + min: center_pos - bounds, + max: center_pos + bounds, } - TrackedData::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125], - TrackedData::Silverfish(_) => [0.4, 0.3, 0.4], - TrackedData::Skeleton(_) => [0.6, 1.99, 0.6], - TrackedData::SkeletonHorse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]), - TrackedData::Slime(e) => { - let s = 0.5202 * e.get_slime_size() as f64; - [s, s, s] + } +} + +fn update_slime_hitbox( + mut query: Query< + (&mut HitboxShape, &slime::SlimeSize), + Or<(Changed, Added)>, + >, +) { + for (mut hitbox, slime_size) in query.iter_mut() { + let s = 0.5202 * slime_size.0 as f64; + hitbox.centered([s, s, s].into()); + } +} + +fn update_painting_hitbox( + mut query: Query< + (&mut HitboxShape, &painting::Variant, &Look), + Or<( + Changed, + Changed, + Added, + )>, + >, +) { + for (mut hitbox, painting_variant, look) in query.iter_mut() { + let bounds: UVec3 = match painting_variant.0 { + PaintingKind::Kebab => [1, 1, 1], + PaintingKind::Aztec => [1, 1, 1], + PaintingKind::Alban => [1, 1, 1], + PaintingKind::Aztec2 => [1, 1, 1], + PaintingKind::Bomb => [1, 1, 1], + PaintingKind::Plant => [1, 1, 1], + PaintingKind::Wasteland => [1, 1, 1], + PaintingKind::Pool => [2, 1, 2], + PaintingKind::Courbet => [2, 1, 2], + PaintingKind::Sea => [2, 1, 2], + PaintingKind::Sunset => [2, 1, 2], + PaintingKind::Creebet => [2, 1, 2], + PaintingKind::Wanderer => [1, 2, 1], + PaintingKind::Graham => [1, 2, 1], + PaintingKind::Match => [2, 2, 2], + PaintingKind::Bust => [2, 2, 2], + PaintingKind::Stage => [2, 2, 2], + PaintingKind::Void => [2, 2, 2], + PaintingKind::SkullAndRoses => [2, 2, 2], + PaintingKind::Wither => [2, 2, 2], + PaintingKind::Fighters => [4, 2, 4], + PaintingKind::Pointer => [4, 4, 4], + PaintingKind::Pigscene => [4, 4, 4], + PaintingKind::BurningSkull => [4, 4, 4], + PaintingKind::Skeleton => [4, 3, 4], + PaintingKind::Earth => [2, 2, 2], + PaintingKind::Wind => [2, 2, 2], + PaintingKind::Water => [2, 2, 2], + PaintingKind::Fire => [2, 2, 2], + PaintingKind::DonkeyKong => [4, 3, 4], } - TrackedData::SmallFireball(_) => [0.3125, 0.3125, 0.3125], - TrackedData::SnowGolem(_) => [0.7, 1.9, 0.7], - TrackedData::Snowball(_) => [0.25, 0.25, 0.25], - TrackedData::SpectralArrow(_) => [0.5, 0.5, 0.5], - TrackedData::Spider(_) => [1.4, 0.9, 1.4], - TrackedData::Squid(_) => [0.8, 0.8, 0.8], - TrackedData::Stray(_) => [0.6, 1.99, 0.6], - TrackedData::Strider(e) => baby(e.get_child(), [0.9, 1.7, 0.9]), - TrackedData::Egg(_) => [0.25, 0.25, 0.25], - TrackedData::EnderPearl(_) => [0.25, 0.25, 0.25], - TrackedData::ExperienceBottle(_) => [0.25, 0.25, 0.25], - TrackedData::Potion(_) => [0.25, 0.25, 0.25], - TrackedData::Trident(_) => [0.5, 0.5, 0.5], - TrackedData::TraderLlama(_) => [0.9, 1.87, 0.9], - TrackedData::TropicalFish(_) => [0.5, 0.4, 0.5], - TrackedData::Turtle(e) => { - if e.get_child() { - [0.36, 0.12, 0.36] - } else { - [1.2, 0.4, 1.2] - } + .into(); + + let mut center_pos = DVec3::splat(0.5); + + let (facing_x, facing_z, cc_facing_x, cc_facing_z) = + match ((look.yaw + 45.0).rem_euclid(360.0) / 90.0) as u8 { + 0 => (0, 1, 1, 0), // South + 1 => (-1, 0, 0, 1), // West + 2 => (0, -1, -1, 0), // North + _ => (1, 0, 0, -1), // East + }; + + center_pos.x -= facing_x as f64 * 0.46875; + center_pos.z -= facing_z as f64 * 0.46875; + + center_pos.x += cc_facing_x as f64 * if bounds.x % 2 == 0 { 0.5 } else { 0.0 }; + center_pos.y += if bounds.y % 2 == 0 { 0.5 } else { 0.0 }; + center_pos.z += cc_facing_z as f64 * if bounds.z % 2 == 0 { 0.5 } else { 0.0 }; + + let bounds = match (facing_x, facing_z) { + (1, 0) | (-1, 0) => DVec3::new(0.0625, bounds.y as f64, bounds.z as f64), + _ => DVec3::new(bounds.x as f64, bounds.y as f64, 0.0625), + }; + + hitbox.0 = Aabb { + min: center_pos - bounds / 2.0, + max: center_pos + bounds / 2.0, + }; + } +} + +fn update_shulker_hitbox( + mut query: Query< + ( + &mut HitboxShape, + &shulker::PeekAmount, + &shulker::AttachedFace, + ), + Or<( + Changed, + Changed, + Added, + )>, + >, +) { + use std::f64::consts::PI; + + for (mut hitbox, peek_amount, attached_face) in query.iter_mut() { + let pos = DVec3::splat(0.5); + let mut min = pos - 0.5; + let mut max = pos + 0.5; + + let peek = 0.5 - f64::cos(peek_amount.0 as f64 * 0.01 * PI) * 0.5; + + match attached_face.0 { + Direction::Down => max.y += peek, + Direction::Up => min.y -= peek, + Direction::North => max.z += peek, + Direction::South => min.z -= peek, + Direction::West => max.x += peek, + Direction::East => min.x -= peek, } - TrackedData::Vex(_) => [0.4, 0.8, 0.4], - TrackedData::Villager(e) => baby(e.get_child(), [0.6, 1.95, 0.6]), - TrackedData::Vindicator(_) => [0.6, 1.95, 0.6], - TrackedData::WanderingTrader(_) => [0.6, 1.95, 0.6], - TrackedData::Witch(_) => [0.6, 1.95, 0.6], - TrackedData::Wither(_) => [0.9, 3.5, 0.9], - TrackedData::WitherSkeleton(_) => [0.7, 2.4, 0.7], - TrackedData::WitherSkull(_) => [0.3125, 0.3125, 0.3125], - TrackedData::Wolf(e) => baby(e.get_child(), [0.6, 0.85, 0.6]), - TrackedData::Zoglin(e) => baby(e.get_baby(), [1.39648, 1.4, 1.39648]), - TrackedData::Zombie(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]), - TrackedData::ZombieHorse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]), - TrackedData::ZombieVillager(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]), - TrackedData::ZombifiedPiglin(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]), - TrackedData::Player(e) => match e.get_pose() { - Pose::Standing => [0.6, 1.8, 0.6], - Pose::Sleeping => [0.2, 0.2, 0.2], - Pose::FallFlying => [0.6, 0.6, 0.6], - Pose::Swimming => [0.6, 0.6, 0.6], - Pose::SpinAttack => [0.6, 0.6, 0.6], - Pose::Sneaking => [0.6, 1.5, 0.6], - Pose::Dying => [0.2, 0.2, 0.2], - _ => [0.6, 1.8, 0.6], - }, - TrackedData::FishingBobber(_) => [0.25, 0.25, 0.25], - }; - - Aabb::from_bottom_size(self.position, dimensions) + + hitbox.0 = Aabb { min, max }; + } } -*/ \ No newline at end of file diff --git a/crates/valence_entity/src/lib.rs b/crates/valence_entity/src/lib.rs index 0d35823dd..da921af6f 100644 --- a/crates/valence_entity/src/lib.rs +++ b/crates/valence_entity/src/lib.rs @@ -17,6 +17,9 @@ clippy::dbg_macro )] +pub mod hitbox; +pub mod packet; + use std::num::Wrapping; use std::ops::Range; @@ -29,13 +32,12 @@ use tracing::warn; use uuid::Uuid; use valence_core::chunk_pos::ChunkPos; use valence_core::despawn::Despawned; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::{Decode, Encode}; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{Decode, Encode}; use valence_core::uuid::UniqueId; use valence_core::DEFAULT_TPS; include!(concat!(env!("OUT_DIR"), "/entity.rs")); - pub struct EntityPlugin; /// When new Minecraft entities are initialized and added to @@ -402,7 +404,7 @@ pub struct PacketByteRange(pub Range); /// Cache for all the tracked data of an entity. Used for the /// [`EntityTrackerUpdateS2c`][packet] packet. /// -/// [packet]: valence_core::packet::s2c::play::EntityTrackerUpdateS2c +/// [packet]: crate::packet::EntityTrackerUpdateS2c #[derive(Component, Default, Debug)] pub struct TrackedData { init_data: Vec, @@ -417,7 +419,7 @@ impl TrackedData { /// [`EntityTrackerUpdateS2c`][packet] packet. This is used when the entity /// enters the view of a client. /// - /// [packet]: valence_core::packet::s2c::play::EntityTrackerUpdateS2c + /// [packet]: crate::packet::EntityTrackerUpdateS2c pub fn init_data(&self) -> Option<&[u8]> { if self.init_data.len() > 1 { Some(&self.init_data) @@ -430,7 +432,7 @@ impl TrackedData { /// [`EntityTrackerUpdateS2c`][packet] packet. This is used when tracked /// data is changed and the client is already in view of the entity. /// - /// [packet]: valence_core::packet::s2c::play::EntityTrackerUpdateS2c + /// [packet]: crate::packet::EntityTrackerUpdateS2c pub fn update_data(&self) -> Option<&[u8]> { if self.update_data.len() > 1 { Some(&self.update_data) diff --git a/crates/valence_entity/src/packet.rs b/crates/valence_entity/src/packet.rs new file mode 100644 index 000000000..ab6a3d4be --- /dev/null +++ b/crates/valence_entity/src/packet.rs @@ -0,0 +1,290 @@ +use std::borrow::Cow; +use std::io::Write; + +use bitfield_struct::bitfield; +use glam::DVec3; +use uuid::Uuid; +use valence_core::ident::Ident; +use valence_core::item::ItemStack; +use valence_core::protocol::byte_angle::ByteAngle; +use valence_core::protocol::raw::RawBytes; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_nbt::Compound; + +#[derive(Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITIES_DESTROY_S2C)] +pub struct EntitiesDestroyS2c<'a> { + pub entity_ids: Cow<'a, [VarInt]>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_ANIMATION_S2C)] +pub struct EntityAnimationS2c { + pub entity_id: VarInt, + pub animation: u8, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_ATTACH_S2C)] +pub struct EntityAttachS2c { + pub attached_entity_id: i32, + pub holding_entity_id: i32, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_ATTRIBUTES_S2C)] +pub struct EntityAttributesS2c<'a> { + pub entity_id: VarInt, + pub properties: Vec>, +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode)] +pub struct AttributeProperty<'a> { + pub key: Ident>, + pub value: f64, + pub modifiers: Vec, +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode)] +pub struct AttributeModifier { + pub uuid: Uuid, + pub amount: f64, + pub operation: u8, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_DAMAGE_S2C)] +pub struct EntityDamageS2c { + /// The ID of the entity taking damage + pub entity_id: VarInt, + /// The ID of the type of damage taken + pub source_type_id: VarInt, + /// The ID + 1 of the entity responsible for the damage, if present. If not + /// present, the value is 0 + pub source_cause_id: VarInt, + /// The ID + 1 of the entity that directly dealt the damage, if present. If + /// not present, the value is 0. If this field is present: + /// * and damage was dealt indirectly, such as by the use of a projectile, + /// this field will contain the ID of such projectile; + /// * and damage was dealt dirctly, such as by manually attacking, this + /// field will contain the same value as Source Cause ID. + pub source_direct_id: VarInt, + /// The Notchian server sends the Source Position when the damage was dealt + /// by the /damage command and a position was specified + pub source_pos: Option, +} + +#[derive(Clone, PartialEq, Debug, Packet)] +#[packet(id = packet_id::ENTITY_EQUIPMENT_UPDATE_S2C)] +pub struct EntityEquipmentUpdateS2c { + pub entity_id: VarInt, + pub equipment: Vec, +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode)] +pub struct EquipmentEntry { + pub slot: i8, + pub item: Option, +} + +impl Encode for EntityEquipmentUpdateS2c { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + self.entity_id.encode(&mut w)?; + + for i in 0..self.equipment.len() { + let slot = self.equipment[i].slot; + if i != self.equipment.len() - 1 { + (slot | -128).encode(&mut w)?; + } else { + slot.encode(&mut w)?; + } + self.equipment[i].item.encode(&mut w)?; + } + + Ok(()) + } +} + +impl<'a> Decode<'a> for EntityEquipmentUpdateS2c { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let entity_id = VarInt::decode(r)?; + + let mut equipment = vec![]; + + loop { + let slot = i8::decode(r)?; + let item = Option::::decode(r)?; + equipment.push(EquipmentEntry { + slot: slot & 127, + item, + }); + if slot & -128 == 0 { + break; + } + } + + Ok(Self { + entity_id, + equipment, + }) + } +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::MOVE_RELATIVE)] +pub struct MoveRelativeS2c { + pub entity_id: VarInt, + pub delta: [i16; 3], + pub on_ground: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ROTATE_AND_MOVE_RELATIVE)] +pub struct RotateAndMoveRelativeS2c { + pub entity_id: VarInt, + pub delta: [i16; 3], + pub yaw: ByteAngle, + pub pitch: ByteAngle, + pub on_ground: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ROTATE)] +pub struct RotateS2c { + pub entity_id: VarInt, + pub yaw: ByteAngle, + pub pitch: ByteAngle, + pub on_ground: bool, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_PASSENGERS_SET_S2C)] +pub struct EntityPassengersSetS2c { + /// Vehicle's entity id + pub entity_id: VarInt, + pub passengers: Vec, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_POSITION_S2C)] +pub struct EntityPositionS2c { + pub entity_id: VarInt, + pub position: DVec3, + pub yaw: ByteAngle, + pub pitch: ByteAngle, + pub on_ground: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_SET_HEAD_YAW_S2C)] +pub struct EntitySetHeadYawS2c { + pub entity_id: VarInt, + pub head_yaw: ByteAngle, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_SPAWN_S2C)] +pub struct EntitySpawnS2c { + pub entity_id: VarInt, + pub object_uuid: Uuid, + pub kind: VarInt, + pub position: DVec3, + pub pitch: ByteAngle, + pub yaw: ByteAngle, + pub head_yaw: ByteAngle, + pub data: VarInt, + pub velocity: [i16; 3], +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_STATUS_EFFECT_S2C)] +pub struct EntityStatusEffectS2c { + pub entity_id: VarInt, + pub effect_id: VarInt, + pub amplifier: u8, + pub duration: VarInt, + pub flags: Flags, + pub factor_codec: Option, +} + +#[bitfield(u8)] +#[derive(PartialEq, Eq, Encode, Decode)] +pub struct Flags { + pub is_ambient: bool, + pub show_particles: bool, + pub show_icon: bool, + #[bits(5)] + _pad: u8, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_STATUS_S2C)] +pub struct EntityStatusS2c { + pub entity_id: i32, + pub entity_status: u8, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_TRACKER_UPDATE_S2C)] +pub struct EntityTrackerUpdateS2c<'a> { + pub entity_id: VarInt, + pub metadata: RawBytes<'a>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ENTITY_VELOCITY_UPDATE_S2C)] +pub struct EntityVelocityUpdateS2c { + pub entity_id: VarInt, + pub velocity: [i16; 3], +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::EXPERIENCE_ORB_SPAWN_S2C)] +pub struct ExperienceOrbSpawnS2c { + pub entity_id: VarInt, + pub position: DVec3, + pub count: i16, +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::REMOVE_ENTITY_STATUS_EFFECT_S2C)] +pub struct RemoveEntityStatusEffectS2c { + pub entity_id: VarInt, + pub effect_id: VarInt, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::ITEM_PICKUP_ANIMATION_S2C)] +pub struct ItemPickupAnimationS2c { + pub collected_entity_id: VarInt, + pub collector_entity_id: VarInt, + pub pickup_item_count: VarInt, +} + +/// Instructs a client to face an entity. +#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOOK_AT_S2C)] +pub struct LookAtS2c { + pub feet_or_eyes: FeetOrEyes, + pub target_position: DVec3, + pub entity_to_face: Option, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub enum FeetOrEyes { + Feet, + Eyes, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub struct LookAtEntity { + pub entity_id: VarInt, + pub feet_or_eyes: FeetOrEyes, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SET_CAMERA_ENTITY_S2C)] +pub struct SetCameraEntityS2c { + pub entity_id: VarInt, +} diff --git a/crates/valence_instance/src/chunk.rs b/crates/valence_instance/src/chunk.rs index 9828de365..7df31ef89 100644 --- a/crates/valence_instance/src/chunk.rs +++ b/crates/valence_instance/src/chunk.rs @@ -8,16 +8,15 @@ use valence_biome::BiomeId; use valence_block::{BlockEntityKind, BlockState}; use valence_core::block_pos::BlockPos; use valence_core::chunk_pos::ChunkPos; -use valence_core::packet::encode::{PacketWriter, WritePacket}; -use valence_core::packet::s2c::play::chunk_data::ChunkDataBlockEntity; -use valence_core::packet::s2c::play::{ - BlockEntityUpdateS2c, BlockUpdateS2c, ChunkDataS2c, ChunkDeltaUpdateS2c, -}; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::var_long::VarLong; -use valence_core::packet::Encode; +use valence_core::protocol::encode::{PacketWriter, WritePacket}; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::var_long::VarLong; +use valence_core::protocol::Encode; use valence_nbt::{compound, Compound}; +use crate::packet::{ + BlockEntityUpdateS2c, BlockUpdateS2c, ChunkDataBlockEntity, ChunkDataS2c, ChunkDeltaUpdateS2c, +}; use crate::paletted_container::PalettedContainer; use crate::{bit_width, InstanceInfo}; diff --git a/crates/valence_instance/src/lib.rs b/crates/valence_instance/src/lib.rs index da9b98c84..52099fa99 100644 --- a/crates/valence_instance/src/lib.rs +++ b/crates/valence_instance/src/lib.rs @@ -36,21 +36,20 @@ use valence_core::block_pos::BlockPos; use valence_core::chunk_pos::ChunkPos; use valence_core::despawn::Despawned; use valence_core::ident::Ident; -use valence_core::packet::array::LengthPrefixedArray; -use valence_core::packet::byte_angle::ByteAngle; -use valence_core::packet::encode::{PacketWriter, WritePacket}; -use valence_core::packet::s2c::play::particle::Particle; -use valence_core::packet::s2c::play::{ - EntityAnimationS2c, EntityPositionS2c, EntitySetHeadYawS2c, EntityStatusS2c, - EntityTrackerUpdateS2c, EntityVelocityUpdateS2c, MoveRelative, OverlayMessageS2c, ParticleS2c, - PlaySoundS2c, Rotate, RotateAndMoveRelative, -}; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::Packet; -use valence_core::sound::{Sound, SoundCategory}; -use valence_core::text::Text; +use valence_core::particle::{Particle, ParticleS2c}; +use valence_core::protocol::array::LengthPrefixedArray; +use valence_core::protocol::byte_angle::ByteAngle; +use valence_core::protocol::encode::{PacketWriter, WritePacket}; +use valence_core::protocol::packet::sound::{PlaySoundS2c, Sound, SoundCategory}; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{Encode, Packet}; use valence_core::Server; use valence_dimension::DimensionType; +use valence_entity::packet::{ + EntityAnimationS2c, EntityPositionS2c, EntitySetHeadYawS2c, EntityStatusS2c, + EntityTrackerUpdateS2c, EntityVelocityUpdateS2c, MoveRelativeS2c, RotateAndMoveRelativeS2c, + RotateS2c, +}; use valence_entity::{ EntityAnimations, EntityId, EntityKind, EntityStatuses, HeadYaw, InitEntitiesSet, Location, Look, OldLocation, OldPosition, OnGround, PacketByteRange, Position, TrackedData, @@ -59,6 +58,7 @@ use valence_entity::{ mod chunk; mod chunk_entry; +pub mod packet; mod paletted_container; pub struct InstancePlugin; @@ -286,7 +286,7 @@ impl UpdateEntityQueryItem<'_> { let changed_position = self.pos.0 != self.old_pos.get(); if changed_position && !needs_teleport && self.look.is_changed() { - writer.write_packet(&RotateAndMoveRelative { + writer.write_packet(&RotateAndMoveRelativeS2c { entity_id, delta: (position_delta * 4096.0).to_array().map(|v| v as i16), yaw: ByteAngle::from_degrees(self.look.yaw), @@ -295,7 +295,7 @@ impl UpdateEntityQueryItem<'_> { }); } else { if changed_position && !needs_teleport { - writer.write_packet(&MoveRelative { + writer.write_packet(&MoveRelativeS2c { entity_id, delta: (position_delta * 4096.0).to_array().map(|v| v as i16), on_ground: self.on_ground.0, @@ -303,7 +303,7 @@ impl UpdateEntityQueryItem<'_> { } if self.look.is_changed() { - writer.write_packet(&Rotate { + writer.write_packet(&RotateS2c { entity_id, yaw: ByteAngle::from_degrees(self.look.yaw), pitch: ByteAngle::from_degrees(self.look.pitch), @@ -704,41 +704,14 @@ impl Instance { )) } - /// Writes a packet into the global packet buffer of this instance. All - /// clients in the instance will receive the packet. - /// - /// This is more efficient than sending the packet to each client - /// individually. - pub fn write_packet<'a, P>(&mut self, pkt: &P) - where - P: Packet<'a>, - { - PacketWriter::new( - &mut self.packet_buf, - self.info.compression_threshold, - &mut self.scratch, - ) - .write_packet(pkt); - } - - /// Writes arbitrary packet data into the global packet buffer of this - /// instance. All clients in the instance will receive the packet data. - /// - /// The packet data must be properly compressed for the current compression - /// threshold but never encrypted. Don't use this function unless you know - /// what you're doing. Consider using [`Self::write_packet`] instead. - pub fn write_packet_bytes(&mut self, bytes: &[u8]) { - self.packet_buf.extend_from_slice(bytes) - } - /// Writes a packet to all clients in view of `pos` in this instance. Has no /// effect if there is no chunk at `pos`. /// /// This is more efficient than sending the packet to each client /// individually. - pub fn write_packet_at<'a, P>(&mut self, pkt: &P, pos: impl Into) + pub fn write_packet_at

(&mut self, pkt: &P, pos: impl Into) where - P: Packet<'a>, + P: Packet + Encode, { let pos = pos.into(); if let Some(cell) = self.partition.get_mut(&pos) { @@ -820,12 +793,30 @@ impl Instance { ChunkPos::from_dvec3(position), ); } +} - /// Sets the action bar text of all players in the instance. - pub fn set_action_bar(&mut self, text: impl Into) { - self.write_packet(&OverlayMessageS2c { - action_bar_text: text.into().into(), - }); +/// Writing packets to the instance writes to the instance's global packet +/// buffer. All clients in the instance will receive the packet at the end of +/// the tick. +/// +/// This is more efficient than sending the packet to each client individually. +impl WritePacket for Instance { + #[inline] + fn write_packet

(&mut self, packet: &P) + where + P: Packet + Encode, + { + PacketWriter::new( + &mut self.packet_buf, + self.info.compression_threshold, + &mut self.scratch, + ) + .write_packet(packet) + } + + #[inline] + fn write_packet_bytes(&mut self, bytes: &[u8]) { + self.packet_buf.extend_from_slice(bytes) } } diff --git a/crates/valence_instance/src/packet.rs b/crates/valence_instance/src/packet.rs new file mode 100644 index 000000000..d9b8bef3a --- /dev/null +++ b/crates/valence_instance/src/packet.rs @@ -0,0 +1,194 @@ +use std::borrow::Cow; + +use valence_core::block_pos::BlockPos; +use valence_core::chunk_pos::ChunkPos; +use valence_core::ident::Ident; +use valence_core::protocol::array::LengthPrefixedArray; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::var_long::VarLong; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_nbt::Compound; + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_BORDER_CENTER_CHANGED_S2C)] +pub struct WorldBorderCenterChangedS2c { + pub x_pos: f64, + pub z_pos: f64, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_BORDER_INITIALIZE_S2C)] +pub struct WorldBorderInitializeS2c { + pub x: f64, + pub z: f64, + pub old_diameter: f64, + pub new_diameter: f64, + pub speed: VarLong, + pub portal_teleport_boundary: VarInt, + pub warning_blocks: VarInt, + pub warning_time: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_BORDER_INTERPOLATE_SIZE_S2C)] +pub struct WorldBorderInterpolateSizeS2c { + pub old_diameter: f64, + pub new_diameter: f64, + pub speed: VarLong, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_BORDER_SIZE_CHANGED_S2C)] +pub struct WorldBorderSizeChangedS2c { + pub diameter: f64, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_BORDER_WARNING_BLOCKS_CHANGED_S2C)] +pub struct WorldBorderWarningBlocksChangedS2c { + pub warning_blocks: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_BORDER_WARNING_TIME_CHANGED_S2C)] +pub struct WorldBorderWarningTimeChangedS2c { + pub warning_time: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_EVENT_S2C)] +pub struct WorldEventS2c { + pub event: i32, + pub location: BlockPos, + pub data: i32, + pub disable_relative_volume: bool, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::WORLD_TIME_UPDATE_S2C)] +pub struct WorldTimeUpdateS2c { + /// The age of the world in 1/20ths of a second. + pub world_age: i64, + /// The current time of day in 1/20ths of a second. + /// The value should be in the range \[0, 24000]. + /// 6000 is noon, 12000 is sunset, and 18000 is midnight. + pub time_of_day: i64, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CHUNK_BIOME_DATA_S2C)] +pub struct ChunkBiomeDataS2c<'a> { + pub chunks: Cow<'a, [ChunkBiome<'a>]>, +} + +#[derive(Clone, Debug, Encode, Decode)] +pub struct ChunkBiome<'a> { + pub pos: ChunkPos, + /// Chunk data structure, with sections containing only the `Biomes` field. + pub data: &'a [u8], +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CHUNK_DATA_S2C)] +pub struct ChunkDataS2c<'a> { + pub pos: ChunkPos, + pub heightmaps: Cow<'a, Compound>, + pub blocks_and_biomes: &'a [u8], + pub block_entities: Cow<'a, [ChunkDataBlockEntity<'a>]>, + pub trust_edges: bool, + pub sky_light_mask: Cow<'a, [u64]>, + pub block_light_mask: Cow<'a, [u64]>, + pub empty_sky_light_mask: Cow<'a, [u64]>, + pub empty_block_light_mask: Cow<'a, [u64]>, + pub sky_light_arrays: Cow<'a, [LengthPrefixedArray]>, + pub block_light_arrays: Cow<'a, [LengthPrefixedArray]>, +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode)] +pub struct ChunkDataBlockEntity<'a> { + pub packed_xz: i8, + pub y: i16, + pub kind: VarInt, + pub data: Cow<'a, Compound>, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CHUNK_DELTA_UPDATE_S2C)] +pub struct ChunkDeltaUpdateS2c<'a> { + pub chunk_section_position: i64, + pub invert_trust_edges: bool, + pub blocks: Cow<'a, [VarLong]>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CHUNK_LOAD_DISTANCE_S2C)] +pub struct ChunkLoadDistanceS2c { + pub view_distance: VarInt, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UNLOAD_CHUNK_S2C)] +pub struct UnloadChunkS2c { + pub pos: ChunkPos, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CHUNK_RENDER_DISTANCE_CENTER_S2C)] +pub struct ChunkRenderDistanceCenterS2c { + pub chunk_x: VarInt, + pub chunk_z: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LIGHT_UPDATE_S2C)] +pub struct LightUpdateS2c { + pub chunk_x: VarInt, + pub chunk_z: VarInt, + pub trust_edges: bool, + pub sky_light_mask: Vec, + pub block_light_mask: Vec, + pub empty_sky_light_mask: Vec, + pub empty_block_light_mask: Vec, + pub sky_light_arrays: Vec>, + pub block_light_arrays: Vec>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BLOCK_BREAKING_PROGRESS_S2C)] +pub struct BlockBreakingProgressS2c { + pub entity_id: VarInt, + pub position: BlockPos, + pub destroy_stage: u8, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BLOCK_ENTITY_UPDATE_S2C)] +pub struct BlockEntityUpdateS2c<'a> { + pub position: BlockPos, + pub kind: VarInt, + pub data: Cow<'a, Compound>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BLOCK_EVENT_S2C)] +pub struct BlockEventS2c { + pub position: BlockPos, + pub action_id: u8, + pub action_parameter: u8, + pub block_type: VarInt, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BLOCK_UPDATE_S2C)] +pub struct BlockUpdateS2c { + pub position: BlockPos, + pub block_id: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::EXPLOSION_S2C)] +pub struct ExplosionS2c<'a> { + pub window_id: u8, + pub recipe: Ident>, + pub make_all: bool, +} diff --git a/crates/valence_instance/src/paletted_container.rs b/crates/valence_instance/src/paletted_container.rs index 929d21a37..d79869e21 100644 --- a/crates/valence_instance/src/paletted_container.rs +++ b/crates/valence_instance/src/paletted_container.rs @@ -3,8 +3,8 @@ use std::io::Write; use arrayvec::ArrayVec; use num_integer::div_ceil; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::Encode; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::Encode; use crate::bit_width; diff --git a/crates/valence_inventory/src/lib.rs b/crates/valence_inventory/src/lib.rs index 640b6470c..cbda36c2b 100644 --- a/crates/valence_inventory/src/lib.rs +++ b/crates/valence_inventory/src/lib.rs @@ -16,6 +16,7 @@ unreachable_pub, clippy::dbg_macro )] +#![allow(clippy::type_complexity)] use std::borrow::Cow; use std::iter::FusedIterator; @@ -24,24 +25,22 @@ use std::ops::Range; use bevy_app::prelude::*; use bevy_ecs::prelude::*; +use packet::{ + ClickMode, ClickSlotC2s, CloseHandledScreenC2s, CloseScreenS2c, CreativeInventoryActionC2s, + InventoryS2c, OpenScreenS2c, ScreenHandlerSlotUpdateS2c, SlotChange, UpdateSelectedSlotC2s, + WindowType, +}; use tracing::{debug, warn}; use valence_client::event_loop::{EventLoopSchedule, EventLoopSet, PacketEvent, RunEventLoopSet}; +use valence_client::packet::{PlayerAction, PlayerActionC2s}; use valence_client::{Client, FlushPacketsSet, SpawnClientsSet}; use valence_core::game_mode::GameMode; use valence_core::item::ItemStack; -use valence_core::packet::c2s::play::click_slot::{ClickMode, Slot}; -use valence_core::packet::c2s::play::{ - ClickSlotC2s, CloseHandledScreenC2s, CreativeInventoryActionC2s, PlayerActionC2s, - UpdateSelectedSlotC2s, -}; -use valence_core::packet::encode::WritePacket; -use valence_core::packet::s2c::play::open_screen::WindowType; -use valence_core::packet::s2c::play::{ - CloseScreenS2c, InventoryS2c, OpenScreenS2c, ScreenHandlerSlotUpdateS2c, -}; -use valence_core::packet::var_int::VarInt; +use valence_core::protocol::encode::WritePacket; +use valence_core::protocol::var_int::VarInt; use valence_core::text::Text; +pub mod packet; mod validate; pub struct InventoryPlugin; @@ -338,15 +337,9 @@ pub struct ClientInventoryState { /// on the `CursorItem` component to make maintaining accurate change /// detection for end users easier. client_updated_cursor_item: bool, - // TODO: make this a separate modifiable component. - held_item_slot: u16, } impl ClientInventoryState { - pub fn held_item_slot(&self) -> u16 { - self.held_item_slot - } - #[doc(hidden)] pub fn window_id(&self) -> u8 { self.window_id @@ -358,6 +351,20 @@ impl ClientInventoryState { } } +/// Indicates which hotbar slot the player is currently holding. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Component)] +pub struct HeldItem { + held_item_slot: u16, +} + +impl HeldItem { + /// The slot ID of the currently held item, in the range 36-44 inclusive. + /// This value is safe to use on the player's inventory directly. + pub fn slot(&self) -> u16 { + self.held_item_slot + } +} + /// The item stack that the client thinks it's holding under the mouse /// cursor. #[derive(Component, Clone, PartialEq, Default, Debug)] @@ -534,6 +541,8 @@ fn init_new_client_inventories(clients: Query>, mut comman state_id: Wrapping(0), slots_changed: 0, client_updated_cursor_item: false, + }, + HeldItem { // First slot of the hotbar. held_item_slot: 36, }, @@ -744,7 +753,7 @@ pub struct ClickSlot { pub slot_id: i16, pub button: i8, pub mode: ClickMode, - pub slot_changes: Vec, + pub slot_changes: Vec, pub carried_item: Option, } @@ -1040,51 +1049,48 @@ fn handle_click_slot( fn handle_player_actions( mut packets: EventReader, - mut clients: Query<(&mut Inventory, &mut ClientInventoryState)>, + mut clients: Query<(&mut Inventory, &mut ClientInventoryState, &HeldItem)>, mut drop_item_stack_events: EventWriter, ) { for packet in packets.iter() { if let Some(pkt) = packet.decode::() { - use valence_core::packet::c2s::play::player_action::Action; - match pkt.action { - Action::DropAllItems => { - if let Ok((mut inv, mut inv_state)) = clients.get_mut(packet.client) { - if let Some(stack) = inv.replace_slot(inv_state.held_item_slot, None) { - inv_state.slots_changed |= 1 << inv_state.held_item_slot; + PlayerAction::DropAllItems => { + if let Ok((mut inv, mut inv_state, &held)) = clients.get_mut(packet.client) { + if let Some(stack) = inv.replace_slot(held.slot(), None) { + inv_state.slots_changed |= 1 << held.slot(); drop_item_stack_events.send(DropItemStack { client: packet.client, - from_slot: Some(inv_state.held_item_slot), + from_slot: Some(held.slot()), stack, }); } } } - Action::DropItem => { - if let Ok((mut inv, mut inv_state)) = clients.get_mut(packet.client) { - if let Some(mut stack) = inv.replace_slot(inv_state.held_item_slot(), None) - { + PlayerAction::DropItem => { + if let Ok((mut inv, mut inv_state, held)) = clients.get_mut(packet.client) { + if let Some(mut stack) = inv.replace_slot(held.slot(), None) { if stack.count() > 1 { inv.set_slot( - inv_state.held_item_slot(), + held.slot(), stack.clone().with_count(stack.count() - 1), ); stack.set_count(1); } - inv_state.slots_changed |= 1 << inv_state.held_item_slot(); + inv_state.slots_changed |= 1 << held.slot(); drop_item_stack_events.send(DropItemStack { client: packet.client, - from_slot: Some(inv_state.held_item_slot()), + from_slot: Some(held.slot()), stack, }) } } } - Action::SwapItemWithOffhand => { + PlayerAction::SwapItemWithOffhand => { // TODO } _ => {} @@ -1172,14 +1178,17 @@ pub struct UpdateSelectedSlot { fn handle_update_selected_slot( mut packets: EventReader, - mut clients: Query<&mut ClientInventoryState>, + mut clients: Query<&mut HeldItem>, mut events: EventWriter, ) { for packet in packets.iter() { if let Some(pkt) = packet.decode::() { - if let Ok(mut inv_state) = clients.get_mut(packet.client) { - // TODO: validate this. - inv_state.held_item_slot = convert_hotbar_slot_id(pkt.slot as u16); + if let Ok(mut held) = clients.get_mut(packet.client) { + if pkt.slot < 0 || pkt.slot > 8 { + // The client is trying to interact with a slot that does not exist, ignore. + continue; + } + held.held_item_slot = convert_hotbar_slot_id(pkt.slot as u16); events.send(UpdateSelectedSlot { client: packet.client, diff --git a/crates/valence_inventory/src/packet.rs b/crates/valence_inventory/src/packet.rs new file mode 100644 index 000000000..19f38a3f6 --- /dev/null +++ b/crates/valence_inventory/src/packet.rs @@ -0,0 +1,758 @@ +//! Inventory packets + +use std::borrow::Cow; +use std::io::Write; + +use anyhow::bail; +use valence_core::ident::Ident; +use valence_core::item::ItemStack; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_core::text::Text; + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CLICK_SLOT_C2S)] +pub struct ClickSlotC2s { + pub window_id: u8, + pub state_id: VarInt, + pub slot_idx: i16, + /// The button used to click the slot. An enum can't easily be used for this + /// because the meaning of this value depends on the mode. + pub button: i8, + pub mode: ClickMode, + pub slot_changes: Vec, + pub carried_item: Option, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Encode, Decode)] +pub enum ClickMode { + Click, + ShiftClick, + Hotbar, + CreativeMiddleClick, + DropKey, + Drag, + DoubleClick, +} + +#[derive(Clone, Debug, Encode, Decode)] +pub struct SlotChange { + pub idx: i16, + pub item: Option, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CLOSE_HANDLED_SCREEN_C2S)] +pub struct CloseHandledScreenC2s { + pub window_id: i8, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CREATIVE_INVENTORY_ACTION_C2S)] +pub struct CreativeInventoryActionC2s { + pub slot: i16, + pub clicked_item: Option, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_SELECTED_SLOT_C2S)] +pub struct UpdateSelectedSlotC2s { + pub slot: i16, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CLOSE_SCREEN_S2C)] +pub struct CloseScreenS2c { + /// Ignored by notchian clients. + pub window_id: u8, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::INVENTORY_S2C)] +pub struct InventoryS2c<'a> { + pub window_id: u8, + pub state_id: VarInt, + pub slots: Cow<'a, [Option]>, + pub carried_item: Cow<'a, Option>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub enum WindowType { + Generic9x1, + Generic9x2, + Generic9x3, + Generic9x4, + Generic9x5, + Generic9x6, + Generic3x3, + Anvil, + Beacon, + BlastFurnace, + BrewingStand, + Crafting, + Enchantment, + Furnace, + Grindstone, + Hopper, + Lectern, + Loom, + Merchant, + ShulkerBox, + Smithing, + Smoker, + Cartography, + Stonecutter, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::OPEN_SCREEN_S2C)] +pub struct OpenScreenS2c<'a> { + pub window_id: VarInt, + pub window_type: WindowType, + pub window_title: Cow<'a, Text>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::OPEN_HORSE_SCREEN_S2C)] +pub struct OpenHorseScreenS2c { + pub window_id: u8, + pub slot_count: VarInt, + pub entity_id: i32, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SCREEN_HANDLER_SLOT_UPDATE_S2C)] +pub struct ScreenHandlerSlotUpdateS2c<'a> { + pub window_id: i8, + pub state_id: VarInt, + pub slot_idx: i16, + pub slot_data: Cow<'a, Option>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SCREEN_HANDLER_PROPERTY_UPDATE_S2C)] +pub struct ScreenHandlerPropertyUpdateS2c { + pub window_id: u8, + pub property: i16, + pub value: i16, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CRAFT_REQUEST_C2S)] +pub struct CraftRequestC2s<'a> { + pub window_id: i8, + pub recipe: Ident>, + pub make_all: bool, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::CRAFT_FAILED_RESPONSE_S2C)] +pub struct CraftFailedResponseS2c<'a> { + pub window_id: u8, + pub recipe: Ident>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PICK_FROM_INVENTORY_C2S)] +pub struct PickFromInventoryC2s { + pub slot_to_use: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SET_TRADE_OFFERS_S2C)] +pub struct SetTradeOffersS2c { + pub window_id: VarInt, + pub trades: Vec, + pub villager_level: VarInt, + pub experience: VarInt, + pub is_regular_villager: bool, + pub can_restock: bool, +} + +#[derive(Clone, PartialEq, Debug, Encode, Decode)] +pub struct TradeOffer { + pub input_one: Option, + pub output_item: Option, + pub input_two: Option, + pub trade_disabled: bool, + pub number_of_trade_uses: i32, + pub max_trade_uses: i32, + pub xp: i32, + pub special_price: i32, + pub price_multiplier: f32, + pub demand: i32, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::BUTTON_CLICK_C2S)] +pub struct ButtonClickC2s { + pub window_id: i8, + pub button_id: i8, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::RECIPE_BOOK_DATA_C2S)] +pub struct RecipeBookDataC2s<'a> { + pub recipe_id: Ident>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::RENAME_ITEM_C2S)] +pub struct RenameItemC2s<'a> { + pub item_name: &'a str, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::RECIPE_CATEGORY_OPTIONS_C2S)] +pub struct RecipeCategoryOptionsC2s { + pub book_id: RecipeBookId, + pub book_open: bool, + pub filter_active: bool, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] +pub enum RecipeBookId { + Crafting, + Furnace, + BlastFurnace, + Smoker, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SELECT_MERCHANT_TRADE_C2S)] +pub struct SelectMerchantTradeC2s { + pub selected_slot: VarInt, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_BEACON_C2S)] +pub struct UpdateBeaconC2s { + pub primary_effect: Option, + pub secondary_effect: Option, +} + +#[derive(Clone, PartialEq, Eq, Debug, Packet)] +#[packet(id = packet_id::UNLOCK_RECIPES_S2C)] +pub struct UnlockRecipesS2c<'a> { + pub action: UpdateRecipeBookAction<'a>, + pub crafting_recipe_book_open: bool, + pub crafting_recipe_book_filter_active: bool, + pub smelting_recipe_book_open: bool, + pub smelting_recipe_book_filter_active: bool, + pub blast_furnace_recipe_book_open: bool, + pub blast_furnace_recipe_book_filter_active: bool, + pub smoker_recipe_book_open: bool, + pub smoker_recipe_book_filter_active: bool, + pub recipe_ids: Vec>>, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum UpdateRecipeBookAction<'a> { + Init { + recipe_ids: Vec>>, + }, + Add, + Remove, +} + +impl Encode for UnlockRecipesS2c<'_> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + VarInt(match &self.action { + UpdateRecipeBookAction::Init { .. } => 0, + UpdateRecipeBookAction::Add => 1, + UpdateRecipeBookAction::Remove => 2, + }) + .encode(&mut w)?; + + self.crafting_recipe_book_open.encode(&mut w)?; + self.crafting_recipe_book_filter_active.encode(&mut w)?; + self.smelting_recipe_book_open.encode(&mut w)?; + self.smelting_recipe_book_filter_active.encode(&mut w)?; + self.blast_furnace_recipe_book_open.encode(&mut w)?; + self.blast_furnace_recipe_book_filter_active + .encode(&mut w)?; + self.smoker_recipe_book_open.encode(&mut w)?; + self.smoker_recipe_book_filter_active.encode(&mut w)?; + self.recipe_ids.encode(&mut w)?; + if let UpdateRecipeBookAction::Init { recipe_ids } = &self.action { + recipe_ids.encode(&mut w)?; + } + + Ok(()) + } +} + +impl<'a> Decode<'a> for UnlockRecipesS2c<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + let action_id = VarInt::decode(r)?.0; + + let crafting_recipe_book_open = bool::decode(r)?; + let crafting_recipe_book_filter_active = bool::decode(r)?; + let smelting_recipe_book_open = bool::decode(r)?; + let smelting_recipe_book_filter_active = bool::decode(r)?; + let blast_furnace_recipe_book_open = bool::decode(r)?; + let blast_furnace_recipe_book_filter_active = bool::decode(r)?; + let smoker_recipe_book_open = bool::decode(r)?; + let smoker_recipe_book_filter_active = bool::decode(r)?; + let recipe_ids = Vec::decode(r)?; + + Ok(Self { + action: match action_id { + 0 => UpdateRecipeBookAction::Init { + recipe_ids: Vec::decode(r)?, + }, + 1 => UpdateRecipeBookAction::Add, + 2 => UpdateRecipeBookAction::Remove, + n => bail!("unknown recipe book action of {n}"), + }, + crafting_recipe_book_open, + crafting_recipe_book_filter_active, + smelting_recipe_book_open, + smelting_recipe_book_filter_active, + blast_furnace_recipe_book_open, + blast_furnace_recipe_book_filter_active, + smoker_recipe_book_open, + smoker_recipe_book_filter_active, + recipe_ids, + }) + } +} + +pub mod synchronize_recipes { + use anyhow::ensure; + + use super::*; + + #[derive(Clone, Debug, Encode, Decode, Packet)] + #[packet(id = packet_id::SYNCHRONIZE_RECIPES_S2C)] + pub struct SynchronizeRecipesS2c<'a> { + // TODO: this should be a Vec> + pub recipes: valence_core::protocol::raw::RawBytes<'a>, + } + + #[derive(Clone, PartialEq, Debug)] + pub enum Recipe<'a> { + CraftingShapeless { + recipe_id: Ident>, + group: &'a str, + category: CraftingCategory, + ingredients: Vec, + result: Option, + }, + CraftingShaped { + recipe_id: Ident>, + width: VarInt, + height: VarInt, + group: &'a str, + category: CraftingCategory, + ingredients: Vec, + result: Option, + }, + CraftingSpecial { + kind: SpecialCraftingKind, + recipe_id: Ident>, + category: CraftingCategory, + }, + Smelting { + recipe_id: Ident>, + group: &'a str, + category: SmeltCategory, + ingredient: Ingredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, + Blasting { + recipe_id: Ident>, + group: &'a str, + category: SmeltCategory, + ingredient: Ingredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, + Smoking { + recipe_id: Ident>, + group: &'a str, + category: SmeltCategory, + ingredient: Ingredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, + CampfireCooking { + recipe_id: Ident>, + group: &'a str, + category: SmeltCategory, + ingredient: Ingredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, + Stonecutting { + recipe_id: Ident>, + group: &'a str, + ingredient: Ingredient, + result: Option, + }, + Smithing { + recipe_id: Ident>, + base: Ingredient, + addition: Ingredient, + result: Option, + }, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub enum SpecialCraftingKind { + ArmorDye, + BookCloning, + MapCloning, + MapExtending, + FireworkRocket, + FireworkStar, + FireworkStarFade, + RepairItem, + TippedArrow, + BannerDuplicate, + BannerAddPattern, + ShieldDecoration, + ShulkerBoxColoring, + SuspiciousStew, + } + + /// Any item in the Vec may be used for the recipe. + pub type Ingredient = Vec>; + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum CraftingCategory { + Building, + Redstone, + Equipment, + Misc, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] + pub enum SmeltCategory { + Food, + Blocks, + Misc, + } + + impl<'a> Encode for Recipe<'a> { + fn encode(&self, mut w: impl Write) -> anyhow::Result<()> { + match self { + Recipe::CraftingShapeless { + recipe_id, + group, + category, + ingredients, + result, + } => { + "crafting_shapeless".encode(&mut w)?; + recipe_id.encode(&mut w)?; + group.encode(&mut w)?; + category.encode(&mut w)?; + ingredients.encode(&mut w)?; + result.encode(w) + } + Recipe::CraftingShaped { + recipe_id, + width, + height, + group, + category, + ingredients, + result, + } => { + "crafting_shaped".encode(&mut w)?; + recipe_id.encode(&mut w)?; + width.encode(&mut w)?; + height.encode(&mut w)?; + group.encode(&mut w)?; + category.encode(&mut w)?; + + ensure!( + (width.0 as usize).saturating_mul(height.0 as usize) == ingredients.len(), + "width * height must be equal to the number of ingredients" + ); + + for ing in ingredients { + ing.encode(&mut w)?; + } + + result.encode(w) + } + Recipe::CraftingSpecial { + kind, + recipe_id, + category, + } => { + match kind { + SpecialCraftingKind::ArmorDye => "crafting_special_armordye", + SpecialCraftingKind::BookCloning => "crafting_special_bookcloning", + SpecialCraftingKind::MapCloning => "crafting_special_mapcloning", + SpecialCraftingKind::MapExtending => "crafting_special_mapextending", + SpecialCraftingKind::FireworkRocket => "crafting_special_firework_rocket", + SpecialCraftingKind::FireworkStar => "crafting_special_firework_star", + SpecialCraftingKind::FireworkStarFade => { + "crafting_special_firework_star_fade" + } + SpecialCraftingKind::RepairItem => "crafting_special_repairitem", + SpecialCraftingKind::TippedArrow => "crafting_special_tippedarrow", + SpecialCraftingKind::BannerDuplicate => "crafting_special_bannerduplicate", + SpecialCraftingKind::BannerAddPattern => { + "crafting_special_banneraddpattern" + } + SpecialCraftingKind::ShieldDecoration => { + "crafting_special_shielddecoration" + } + SpecialCraftingKind::ShulkerBoxColoring => { + "crafting_special_shulkerboxcoloring" + } + SpecialCraftingKind::SuspiciousStew => "crafting_special_suspiciousstew", + } + .encode(&mut w)?; + recipe_id.encode(&mut w)?; + category.encode(w) + } + Recipe::Smelting { + recipe_id, + group, + category, + ingredient, + result, + experience, + cooking_time, + } => { + "smelting".encode(&mut w)?; + recipe_id.encode(&mut w)?; + group.encode(&mut w)?; + category.encode(&mut w)?; + ingredient.encode(&mut w)?; + result.encode(&mut w)?; + experience.encode(&mut w)?; + cooking_time.encode(w) + } + Recipe::Blasting { + recipe_id, + group, + category, + ingredient, + result, + experience, + cooking_time, + } => { + "blasting".encode(&mut w)?; + recipe_id.encode(&mut w)?; + group.encode(&mut w)?; + category.encode(&mut w)?; + ingredient.encode(&mut w)?; + result.encode(&mut w)?; + experience.encode(&mut w)?; + cooking_time.encode(w) + } + Recipe::Smoking { + recipe_id, + group, + category, + ingredient, + result, + experience, + cooking_time, + } => { + "smoking".encode(&mut w)?; + recipe_id.encode(&mut w)?; + group.encode(&mut w)?; + category.encode(&mut w)?; + ingredient.encode(&mut w)?; + result.encode(&mut w)?; + experience.encode(&mut w)?; + cooking_time.encode(w) + } + Recipe::CampfireCooking { + recipe_id, + group, + category, + ingredient, + result, + experience, + cooking_time, + } => { + "campfire_cooking".encode(&mut w)?; + recipe_id.encode(&mut w)?; + group.encode(&mut w)?; + category.encode(&mut w)?; + ingredient.encode(&mut w)?; + result.encode(&mut w)?; + experience.encode(&mut w)?; + cooking_time.encode(w) + } + Recipe::Stonecutting { + recipe_id, + group, + ingredient, + result, + } => { + "stonecutting".encode(&mut w)?; + recipe_id.encode(&mut w)?; + group.encode(&mut w)?; + ingredient.encode(&mut w)?; + result.encode(w) + } + Recipe::Smithing { + recipe_id, + base, + addition, + result, + } => { + "smithing".encode(&mut w)?; + recipe_id.encode(&mut w)?; + base.encode(&mut w)?; + addition.encode(&mut w)?; + result.encode(w) + } + } + } + } + + impl<'a> Decode<'a> for Recipe<'a> { + fn decode(r: &mut &'a [u8]) -> anyhow::Result { + Ok(match Ident::>::decode(r)?.as_str() { + "minecraft:crafting_shapeless" => Self::CraftingShapeless { + recipe_id: Decode::decode(r)?, + group: Decode::decode(r)?, + category: Decode::decode(r)?, + ingredients: Decode::decode(r)?, + result: Decode::decode(r)?, + }, + "minecraft:crafting_shaped" => { + let recipe_id = Ident::decode(r)?; + let width = VarInt::decode(r)?.0; + let height = VarInt::decode(r)?.0; + let group = <&str>::decode(r)?; + let category = CraftingCategory::decode(r)?; + + let mut ingredients = Vec::new(); + for _ in 0..width.saturating_mul(height) { + ingredients.push(Ingredient::decode(r)?); + } + + Self::CraftingShaped { + recipe_id, + width: VarInt(width), + height: VarInt(height), + group, + category, + ingredients, + result: Decode::decode(r)?, + } + } + "minecraft:smelting" => Self::Smelting { + recipe_id: Decode::decode(r)?, + group: Decode::decode(r)?, + category: Decode::decode(r)?, + ingredient: Decode::decode(r)?, + result: Decode::decode(r)?, + experience: Decode::decode(r)?, + cooking_time: Decode::decode(r)?, + }, + "minecraft:blasting" => Self::Blasting { + recipe_id: Decode::decode(r)?, + group: Decode::decode(r)?, + category: Decode::decode(r)?, + ingredient: Decode::decode(r)?, + result: Decode::decode(r)?, + experience: Decode::decode(r)?, + cooking_time: Decode::decode(r)?, + }, + "minecraft:smoking" => Self::Smoking { + recipe_id: Decode::decode(r)?, + group: Decode::decode(r)?, + category: Decode::decode(r)?, + ingredient: Decode::decode(r)?, + result: Decode::decode(r)?, + experience: Decode::decode(r)?, + cooking_time: Decode::decode(r)?, + }, + "minecraft:campfire_cooking" => Self::CampfireCooking { + recipe_id: Decode::decode(r)?, + group: Decode::decode(r)?, + category: Decode::decode(r)?, + ingredient: Decode::decode(r)?, + result: Decode::decode(r)?, + experience: Decode::decode(r)?, + cooking_time: Decode::decode(r)?, + }, + "minecraft:stonecutting" => Self::Stonecutting { + recipe_id: Decode::decode(r)?, + group: Decode::decode(r)?, + ingredient: Decode::decode(r)?, + result: Decode::decode(r)?, + }, + "minecraft:smithing" => Self::Smithing { + recipe_id: Decode::decode(r)?, + base: Decode::decode(r)?, + addition: Decode::decode(r)?, + result: Decode::decode(r)?, + }, + other => Self::CraftingSpecial { + kind: match other { + "minecraft:crafting_special_armordye" => SpecialCraftingKind::ArmorDye, + "minecraft:crafting_special_bookcloning" => { + SpecialCraftingKind::BookCloning + } + "minecraft:crafting_special_mapcloning" => SpecialCraftingKind::MapCloning, + "minecraft:crafting_special_mapextending" => { + SpecialCraftingKind::MapExtending + } + "minecraft:crafting_special_firework_rocket" => { + SpecialCraftingKind::FireworkRocket + } + "minecraft:crafting_special_firework_star" => { + SpecialCraftingKind::FireworkStar + } + "minecraft:crafting_special_firework_star_fade" => { + SpecialCraftingKind::FireworkStarFade + } + "minecraft:crafting_special_repairitem" => SpecialCraftingKind::RepairItem, + "minecraft:crafting_special_tippedarrow" => { + SpecialCraftingKind::TippedArrow + } + "minecraft:crafting_special_bannerduplicate" => { + SpecialCraftingKind::BannerDuplicate + } + "minecraft:crafting_special_banneraddpattern" => { + SpecialCraftingKind::BannerAddPattern + } + "minecraft:crafting_special_shielddecoration" => { + SpecialCraftingKind::ShieldDecoration + } + "minecraft:crafting_special_shulkerboxcoloring" => { + SpecialCraftingKind::ShulkerBoxColoring + } + "minecraft:crafting_special_suspiciousstew" => { + SpecialCraftingKind::SuspiciousStew + } + _ => bail!("unknown recipe type \"{other}\""), + }, + recipe_id: Decode::decode(r)?, + category: CraftingCategory::decode(r)?, + }, + }) + } + } +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::COOLDOWN_UPDATE_S2C)] +pub struct CooldownUpdateS2c { + pub item_id: VarInt, + pub cooldown_ticks: VarInt, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::UPDATE_SELECTED_SLOT_S2C)] +pub struct UpdateSelectedSlotS2c { + pub slot: u8, +} diff --git a/crates/valence_inventory/src/validate.rs b/crates/valence_inventory/src/validate.rs index d39f07362..ee14d3bc9 100644 --- a/crates/valence_inventory/src/validate.rs +++ b/crates/valence_inventory/src/validate.rs @@ -1,9 +1,8 @@ use anyhow::{bail, ensure}; use valence_core::item::ItemStack; -use valence_core::packet::c2s::play::click_slot::ClickMode; -use valence_core::packet::c2s::play::ClickSlotC2s; use super::{CursorItem, Inventory, InventoryWindow, PLAYER_INVENTORY_MAIN_SLOTS_COUNT}; +use crate::packet::{ClickMode, ClickSlotC2s}; /// Validates a click slot packet enforcing that all fields are valid. pub(super) fn validate_click_slot_packet( @@ -358,10 +357,10 @@ fn calculate_net_item_delta( #[cfg(test)] mod tests { use valence_core::item::{ItemKind, ItemStack}; - use valence_core::packet::c2s::play::click_slot::Slot; - use valence_core::packet::var_int::VarInt; + use valence_core::protocol::var_int::VarInt; use super::*; + use crate::packet::SlotChange; use crate::InventoryKind; #[test] @@ -373,15 +372,15 @@ mod tests { button: 2, mode: ClickMode::Drag, slot_changes: vec![ - Slot { + SlotChange { idx: 4, item: Some(ItemStack::new(ItemKind::Diamond, 21, None)), }, - Slot { + SlotChange { idx: 3, item: Some(ItemStack::new(ItemKind::Diamond, 21, None)), }, - Slot { + SlotChange { idx: 5, item: Some(ItemStack::new(ItemKind::Diamond, 21, None)), }, @@ -409,19 +408,19 @@ mod tests { button: 2, mode: ClickMode::Click, slot_changes: vec![ - Slot { + SlotChange { idx: 2, item: Some(ItemStack::new(ItemKind::Diamond, 2, None)), }, - Slot { + SlotChange { idx: 3, item: Some(ItemStack::new(ItemKind::IronIngot, 2, None)), }, - Slot { + SlotChange { idx: 4, item: Some(ItemStack::new(ItemKind::GoldIngot, 2, None)), }, - Slot { + SlotChange { idx: 5, item: Some(ItemStack::new(ItemKind::Emerald, 2, None)), }, @@ -452,7 +451,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 0, - slot_changes: vec![Slot { idx: 0, item: None }], + slot_changes: vec![SlotChange { idx: 0, item: None }], carried_item: inventory.slot(0).cloned(), }; @@ -473,7 +472,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 0, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 0, item: Some(ItemStack::new(ItemKind::Diamond, 20, None)), }], @@ -485,7 +484,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 0, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 0, item: Some(ItemStack::new(ItemKind::Diamond, 30, None)), }], @@ -511,7 +510,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 0, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 0, item: Some(ItemStack::new(ItemKind::Diamond, 64, None)), }], @@ -534,7 +533,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 0, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 0, item: Some(ItemStack::new(ItemKind::Diamond, 2, None)), }], @@ -558,7 +557,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 0, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 0, item: Some(ItemStack::new(ItemKind::Diamond, 22, None)), }], @@ -570,7 +569,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 0, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 0, item: Some(ItemStack::new(ItemKind::Diamond, 32, None)), }], @@ -583,11 +582,11 @@ mod tests { state_id: VarInt(0), slot_idx: 0, slot_changes: vec![ - Slot { + SlotChange { idx: 0, item: Some(ItemStack::new(ItemKind::Diamond, 22, None)), }, - Slot { + SlotChange { idx: 1, item: Some(ItemStack::new(ItemKind::Diamond, 22, None)), }, @@ -621,8 +620,8 @@ mod tests { state_id: VarInt(0), slot_idx: 9, slot_changes: vec![ - Slot { idx: 9, item: None }, - Slot { + SlotChange { idx: 9, item: None }, + SlotChange { idx: 36, item: Some(ItemStack::new(ItemKind::GoldIngot, 2, None)), }, @@ -636,8 +635,8 @@ mod tests { state_id: VarInt(0), slot_idx: 9, slot_changes: vec![ - Slot { idx: 9, item: None }, - Slot { + SlotChange { idx: 9, item: None }, + SlotChange { idx: 36, item: Some(ItemStack::new(ItemKind::GoldIngot, 2, None)), }, @@ -650,7 +649,7 @@ mod tests { mode: ClickMode::Click, state_id: VarInt(0), slot_idx: 9, - slot_changes: vec![Slot { idx: 9, item: None }], + slot_changes: vec![SlotChange { idx: 9, item: None }], carried_item: Some(ItemStack::new(ItemKind::GoldIngot, 2, None)), }, ClickSlotC2s { @@ -659,7 +658,7 @@ mod tests { mode: ClickMode::DropKey, state_id: VarInt(0), slot_idx: 9, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 9, item: Some(ItemStack::new(ItemKind::GoldIngot, 1, None)), }], @@ -688,15 +687,15 @@ mod tests { button: 0, mode: ClickMode::ShiftClick, slot_changes: vec![ - Slot { + SlotChange { idx: 37, item: Some(ItemStack::new(ItemKind::Diamond, 32, None)), }, - Slot { + SlotChange { idx: 36, item: Some(ItemStack::new(ItemKind::Diamond, 64, None)), }, - Slot { idx: 9, item: None }, + SlotChange { idx: 9, item: None }, ], carried_item: None, }; @@ -717,7 +716,7 @@ mod tests { slot_idx: 9, button: 0, mode: ClickMode::Click, - slot_changes: vec![Slot { idx: 9, item: None }], + slot_changes: vec![SlotChange { idx: 9, item: None }], carried_item: Some(ItemStack::new(ItemKind::Apple, 100, None)), }; @@ -736,7 +735,7 @@ mod tests { slot_idx: 9, button: 0, mode: ClickMode::Click, - slot_changes: vec![Slot { + slot_changes: vec![SlotChange { idx: 9, item: Some(ItemStack::new(ItemKind::Apple, 64, None)), }], diff --git a/crates/valence_nbt/Cargo.toml b/crates/valence_nbt/Cargo.toml index 8b95af2c1..1ec15eea6 100644 --- a/crates/valence_nbt/Cargo.toml +++ b/crates/valence_nbt/Cargo.toml @@ -10,11 +10,20 @@ version = "0.5.0" edition.workspace = true [features] +binary = ["dep:byteorder", "dep:cesu8"] +snbt = [] # When enabled, the order of fields in compounds are preserved. preserve_order = ["dep:indexmap"] +serde = ["dep:serde", "dep:thiserror", "indexmap?/serde"] [dependencies] -byteorder.workspace = true -cesu8.workspace = true +byteorder = { workspace = true, optional = true } +cesu8 = { workspace = true, optional = true } indexmap = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"], optional = true } +thiserror = { workspace = true, optional = true } uuid = { workspace = true, optional = true } + +[dev-dependencies] +pretty_assertions.workspace = true +serde_json.workspace = true diff --git a/crates/valence_nbt/README.md b/crates/valence_nbt/README.md index 4aeba9e24..1d07ce13d 100644 --- a/crates/valence_nbt/README.md +++ b/crates/valence_nbt/README.md @@ -5,48 +5,10 @@ format. [Named Binary Tag]: https://minecraft.fandom.com/wiki/NBT_format -# Examples - -Encode NBT data to its binary form. We are using the [`compound!`] macro to -conveniently construct [`Compound`] values. - -```rust -use valence_nbt::{compound, to_binary_writer, List}; - -let c = compound! { - "byte" => 5_i8, - "string" => "hello", - "list_of_float" => List::Float(vec![ - 3.1415, - 2.7182, - 1.4142 - ]), -}; - -let mut buf = vec![]; - -to_binary_writer(&mut buf, &c, "").unwrap(); -``` - -Decode NBT data from its binary form. - -```rust -use valence_nbt::{compound, from_binary_slice}; - -let some_bytes = [10, 0, 0, 3, 0, 3, 105, 110, 116, 0, 0, 222, 173, 0]; - -let expected_value = compound! { - "int" => 0xdead -}; - -let (nbt, root_name) = from_binary_slice(&mut some_bytes.as_slice()).unwrap(); - -assert_eq!(nbt, expected_value); -assert_eq!(root_name, ""); -``` - # Features - +- `binary`: Adds support for serializing and deserializing in Java edition's binary format. +- `snbt`: Adds support for serializing and deserializing in "stringified" format. - `preserve_order`: Causes the order of fields in [`Compound`]s to be preserved during insertion and deletion at a slight cost to performance. The iterators on `Compound` can then implement [`DoubleEndedIterator`]. +- `serde` Adds support for [`serde`](https://docs.rs/serde/latest/serde/) diff --git a/crates/valence_nbt/src/binary.rs b/crates/valence_nbt/src/binary.rs new file mode 100644 index 000000000..7e074653c --- /dev/null +++ b/crates/valence_nbt/src/binary.rs @@ -0,0 +1,48 @@ +//! Support for serializing and deserializing compounds in Java edition's binary +//! format. +//! +//! # Examples +//! +//! ``` +//! use valence_nbt::{compound, Compound, List}; +//! +//! let c = compound! { +//! "byte" => 5_i8, +//! "string" => "hello", +//! "list_of_float" => List::Float(vec![ +//! 3.1415, +//! 2.7182, +//! 1.4142 +//! ]), +//! }; +//! +//! let mut buf = vec![]; +//! +//! c.to_binary(&mut buf, "").unwrap(); +//! ``` +//! +//! Decode NBT data from its binary form. +//! +//! ``` +//! use valence_nbt::{compound, Compound}; +//! +//! let some_bytes = [10, 0, 0, 3, 0, 3, 105, 110, 116, 0, 0, 222, 173, 0]; +//! +//! let expected_value = compound! { +//! "int" => 0xdead +//! }; +//! +//! let (nbt, root_name) = Compound::from_binary(&mut some_bytes.as_slice()).unwrap(); +//! +//! assert_eq!(nbt, expected_value); +//! assert_eq!(root_name, ""); +//! ``` + +mod decode; +mod encode; +mod error; +mod modified_utf8; +#[cfg(test)] +mod tests; + +pub use error::*; diff --git a/crates/valence_nbt/src/to_binary_writer.rs b/crates/valence_nbt/src/binary/decode.rs similarity index 67% rename from crates/valence_nbt/src/to_binary_writer.rs rename to crates/valence_nbt/src/binary/decode.rs index 9003ad0a6..b740e56da 100644 --- a/crates/valence_nbt/src/to_binary_writer.rs +++ b/crates/valence_nbt/src/binary/decode.rs @@ -1,77 +1,88 @@ use std::io::Write; +use std::slice; use byteorder::{BigEndian, WriteBytesExt}; +use super::{modified_utf8, Error, Result}; use crate::tag::Tag; -use crate::{modified_utf8, Compound, Error, List, Result, Value}; - -/// Encodes uncompressed NBT binary data to the provided writer. -/// -/// Only compounds are permitted at the top level. This is why the function -/// accepts a [`Compound`] reference rather than a [`Value`]. -/// -/// Additionally, the root compound can be given a name. Typically the empty -/// string `""` is used. -pub fn to_binary_writer(writer: W, compound: &Compound, root_name: &str) -> Result<()> { - let mut state = EncodeState { writer }; - - state.write_tag(Tag::Compound)?; - state.write_string(root_name)?; - state.write_compound(compound)?; - - Ok(()) -} +use crate::{Compound, List, Value}; + +impl Compound { + /// Encodes uncompressed NBT binary data to the provided writer. + /// + /// Only compounds are permitted at the top level. This is why the function + /// accepts a [`Compound`] reference rather than a [`Value`]. + /// + /// Additionally, the root compound can be given a name. Typically the empty + /// string `""` is used. + pub fn to_binary(&self, writer: W, root_name: &str) -> Result<()> { + let mut state = EncodeState { writer }; + + state.write_tag(Tag::Compound)?; + state.write_string(root_name)?; + state.write_compound(self)?; -pub(crate) fn written_size(compound: &Compound, root_name: &str) -> usize { - fn value_size(val: &Value) -> usize { - match val { - Value::Byte(_) => 1, - Value::Short(_) => 2, - Value::Int(_) => 4, - Value::Long(_) => 8, - Value::Float(_) => 4, - Value::Double(_) => 8, - Value::ByteArray(ba) => 4 + ba.len(), - Value::String(s) => string_size(s), - Value::List(l) => list_size(l), - Value::Compound(c) => compound_size(c), - Value::IntArray(ia) => 4 + ia.len() * 4, - Value::LongArray(la) => 4 + la.len() * 8, - } + Ok(()) } - fn list_size(l: &List) -> usize { - let elems_size = match l { - List::End => 0, - List::Byte(b) => b.len(), - List::Short(s) => s.len() * 2, - List::Int(i) => i.len() * 4, - List::Long(l) => l.len() * 8, - List::Float(f) => f.len() * 4, - List::Double(d) => d.len() * 8, - List::ByteArray(ba) => ba.iter().map(|b| 4 + b.len()).sum(), - List::String(s) => s.iter().map(|s| string_size(s)).sum(), - List::List(l) => l.iter().map(list_size).sum(), - List::Compound(c) => c.iter().map(compound_size).sum(), - List::IntArray(i) => i.iter().map(|i| 4 + i.len() * 4).sum(), - List::LongArray(l) => l.iter().map(|l| 4 + l.len() * 8).sum(), - }; - - 1 + 4 + elems_size - } + /// Returns the number of bytes that will be written when + /// [`Compound::to_binary`] is called with this compound and root name. + /// + /// If `to_binary` results in `Ok`, the exact number of bytes + /// reported by this function will have been written. If the result is + /// `Err`, then the reported count will be greater than or equal to the + /// number of bytes that have actually been written. + pub fn written_size(&self, root_name: &str) -> usize { + fn value_size(val: &Value) -> usize { + match val { + Value::Byte(_) => 1, + Value::Short(_) => 2, + Value::Int(_) => 4, + Value::Long(_) => 8, + Value::Float(_) => 4, + Value::Double(_) => 8, + Value::ByteArray(ba) => 4 + ba.len(), + Value::String(s) => string_size(s), + Value::List(l) => list_size(l), + Value::Compound(c) => compound_size(c), + Value::IntArray(ia) => 4 + ia.len() * 4, + Value::LongArray(la) => 4 + la.len() * 8, + } + } - fn string_size(s: &str) -> usize { - 2 + modified_utf8::encoded_len(s) - } + fn list_size(l: &List) -> usize { + let elems_size = match l { + List::End => 0, + List::Byte(b) => b.len(), + List::Short(s) => s.len() * 2, + List::Int(i) => i.len() * 4, + List::Long(l) => l.len() * 8, + List::Float(f) => f.len() * 4, + List::Double(d) => d.len() * 8, + List::ByteArray(ba) => ba.iter().map(|b| 4 + b.len()).sum(), + List::String(s) => s.iter().map(|s| string_size(s)).sum(), + List::List(l) => l.iter().map(list_size).sum(), + List::Compound(c) => c.iter().map(compound_size).sum(), + List::IntArray(i) => i.iter().map(|i| 4 + i.len() * 4).sum(), + List::LongArray(l) => l.iter().map(|l| 4 + l.len() * 8).sum(), + }; + + 1 + 4 + elems_size + } - fn compound_size(c: &Compound) -> usize { - c.iter() - .map(|(k, v)| 1 + string_size(k) + value_size(v)) - .sum::() - + 1 - } + fn string_size(s: &str) -> usize { + 2 + modified_utf8::encoded_len(s) + } - 1 + string_size(root_name) + compound_size(compound) + fn compound_size(c: &Compound) -> usize { + c.iter() + .map(|(k, v)| 1 + string_size(k) + value_size(v)) + .sum::() + + 1 + } + + 1 + string_size(root_name) + compound_size(self) + } } struct EncodeState { @@ -136,7 +147,7 @@ impl EncodeState { } // SAFETY: i8 has the same layout as u8. - let bytes: &[u8] = unsafe { std::mem::transmute(bytes) }; + let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *const u8, bytes.len()) }; Ok(self.writer.write_all(bytes)?) } @@ -187,7 +198,7 @@ impl EncodeState { } // SAFETY: i8 has the same layout as u8. - let bytes: &[u8] = unsafe { std::mem::transmute(bl.as_slice()) }; + let bytes = unsafe { slice::from_raw_parts(bl.as_ptr() as *const u8, bl.len()) }; Ok(self.writer.write_all(bytes)?) } diff --git a/crates/valence_nbt/src/from_binary_slice.rs b/crates/valence_nbt/src/binary/encode.rs similarity index 89% rename from crates/valence_nbt/src/from_binary_slice.rs rename to crates/valence_nbt/src/binary/encode.rs index 2ac1f3d8f..547809726 100644 --- a/crates/valence_nbt/src/from_binary_slice.rs +++ b/crates/valence_nbt/src/binary/encode.rs @@ -3,36 +3,39 @@ use std::mem; use byteorder::{BigEndian, ReadBytesExt}; use cesu8::Cesu8DecodingError; +use super::{Error, Result}; use crate::tag::Tag; -use crate::{Compound, Error, List, Result, Value}; +use crate::{Compound, List, Value}; -/// Decodes uncompressed NBT binary data from the provided slice. -/// -/// The string returned is the name of the root compound. -pub fn from_binary_slice(slice: &mut &[u8]) -> Result<(Compound, String)> { - let mut state = DecodeState { slice, depth: 0 }; +impl Compound { + /// Decodes uncompressed NBT binary data from the provided slice. + /// + /// The string returned in the tuple is the name of the root compound + /// (typically the empty string). + pub fn from_binary(slice: &mut &[u8]) -> Result<(Self, String)> { + let mut state = DecodeState { slice, depth: 0 }; - let root_tag = state.read_tag()?; + let root_tag = state.read_tag()?; - // For cases such as Block Entity Data in the - // ChunkUpdateAndUpdateLight Packet - // https://wiki.vg/Protocol#Chunk_Data_and_Update_Light - if root_tag == Tag::End { - return Ok((Compound::new(), String::new())); - } + // For cases such as Block Entity Data in the chunk packet. + // https://wiki.vg/Protocol#Chunk_Data_and_Update_Light + if root_tag == Tag::End { + return Ok((Compound::new(), String::new())); + } - if root_tag != Tag::Compound { - return Err(Error::new_owned(format!( - "expected root tag for compound (got {root_tag})", - ))); - } + if root_tag != Tag::Compound { + return Err(Error::new_owned(format!( + "expected root tag for compound (got {root_tag})", + ))); + } - let root_name = state.read_string()?; - let root = state.read_compound()?; + let root_name = state.read_string()?; + let root = state.read_compound()?; - debug_assert_eq!(state.depth, 0); + debug_assert_eq!(state.depth, 0); - Ok((root, root_name)) + Ok((root, root_name)) + } } /// Maximum recursion depth to prevent overflowing the call stack. diff --git a/crates/valence_nbt/src/error.rs b/crates/valence_nbt/src/binary/error.rs similarity index 91% rename from crates/valence_nbt/src/error.rs rename to crates/valence_nbt/src/binary/error.rs index a3f342100..8c5c4efaf 100644 --- a/crates/valence_nbt/src/error.rs +++ b/crates/valence_nbt/src/binary/error.rs @@ -2,7 +2,9 @@ use std::error::Error as StdError; use std::fmt::{Display, Formatter}; use std::io; -/// Errors that can occur when encoding or decoding. +pub type Result = std::result::Result; + +/// Errors that can occur when encoding or decoding binary NBT. #[derive(Debug)] pub struct Error { /// Box this to keep the size of `Result` small. diff --git a/crates/valence_nbt/src/modified_utf8.rs b/crates/valence_nbt/src/binary/modified_utf8.rs similarity index 100% rename from crates/valence_nbt/src/modified_utf8.rs rename to crates/valence_nbt/src/binary/modified_utf8.rs diff --git a/crates/valence_nbt/src/tests.rs b/crates/valence_nbt/src/binary/tests.rs similarity index 79% rename from crates/valence_nbt/src/tests.rs rename to crates/valence_nbt/src/binary/tests.rs index ee93bc712..897cabbf1 100644 --- a/crates/valence_nbt/src/tests.rs +++ b/crates/valence_nbt/src/binary/tests.rs @@ -1,5 +1,5 @@ use crate::tag::Tag; -use crate::{compound, from_binary_slice, to_binary_writer, Compound, List, Value}; +use crate::{compound, Compound, List, Value}; const ROOT_NAME: &str = "The root name‽"; @@ -9,11 +9,11 @@ fn round_trip() { let compound = example_compound(); - to_binary_writer(&mut buf, &compound, ROOT_NAME).unwrap(); + compound.to_binary(&mut buf, ROOT_NAME).unwrap(); println!("{buf:?}"); - let (decoded, root_name) = from_binary_slice(&mut buf.as_slice()).unwrap(); + let (decoded, root_name) = Compound::from_binary(&mut buf.as_slice()).unwrap(); assert_eq!(root_name, ROOT_NAME); assert_eq!(compound, decoded); @@ -28,7 +28,7 @@ fn check_min_sizes() { let dbg = format!("{min_val:?}"); let mut buf = vec![]; - to_binary_writer(&mut buf, &compound!("" => min_val), "").unwrap(); + compound!("" => min_val).to_binary(&mut buf, "").unwrap(); assert_eq!( expected_size, @@ -65,7 +65,7 @@ fn deeply_nested_compound_decode() { buf.push(Tag::End as u8); // End root compound // Should not overflow the stack - let _ = from_binary_slice(&mut buf.as_slice()); + let _ = Compound::from_binary(&mut buf.as_slice()); } #[test] @@ -84,7 +84,7 @@ fn deeply_nested_list_decode() { buf.push(Tag::End as u8); // End root compound // Should not overflow the stack - let _ = from_binary_slice(&mut buf.as_slice()); + let _ = Compound::from_binary(&mut buf.as_slice()); } #[test] @@ -92,26 +92,11 @@ fn correct_length() { let c = example_compound(); let mut buf = vec![]; - to_binary_writer(&mut buf, &c, "abc").unwrap(); + c.to_binary(&mut buf, "abc").unwrap(); assert_eq!(c.written_size("abc"), buf.len()); } -#[cfg(feature = "preserve_order")] -#[test] -fn preserves_order() { - let letters = ["g", "b", "d", "e", "h", "z", "m", "a", "q"]; - - let mut c = Compound::new(); - for l in letters { - c.insert(l, 0_i8); - } - - for (k, l) in c.keys().zip(letters) { - assert_eq!(k, l); - } -} - fn example_compound() -> Compound { fn inner() -> Compound { compound! { diff --git a/crates/valence_nbt/src/compound.rs b/crates/valence_nbt/src/compound.rs index f6d65c806..2d2649dd9 100644 --- a/crates/valence_nbt/src/compound.rs +++ b/crates/valence_nbt/src/compound.rs @@ -4,11 +4,15 @@ use std::hash::Hash; use std::iter::FusedIterator; use std::ops::{Index, IndexMut}; -use crate::to_binary_writer::written_size; use crate::Value; /// A map type with [`String`] keys and [`Value`] values. #[derive(Clone, PartialEq, Default)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(transparent) +)] pub struct Compound { map: Map, } @@ -19,74 +23,6 @@ type Map = std::collections::BTreeMap; #[cfg(feature = "preserve_order")] type Map = indexmap::IndexMap; -impl Compound { - /// Returns the number of bytes that will be written when - /// [`to_binary_writer`] is called with this compound and root name. - /// - /// If [`to_binary_writer`] results in `Ok`, the exact number of bytes - /// reported by this function will have been written. If the result is - /// `Err`, then the reported count will be greater than or equal to the - /// number of bytes that have actually been written. - /// - /// [`to_binary_writer`]: crate::to_binary_writer() - pub fn written_size(&self, root_name: &str) -> usize { - written_size(self, root_name) - } - - /// Inserts all items from `other` into `self` recursively. - /// - /// # Example - /// - /// ``` - /// use valence_nbt::compound; - /// - /// let mut this = compound! { - /// "foo" => 10, - /// "bar" => compound! { - /// "baz" => 20, - /// } - /// }; - /// - /// let other = compound! { - /// "foo" => 15, - /// "bar" => compound! { - /// "quux" => "hello", - /// } - /// }; - /// - /// this.merge(other); - /// - /// assert_eq!( - /// this, - /// compound! { - /// "foo" => 15, - /// "bar" => compound! { - /// "baz" => 20, - /// "quux" => "hello", - /// } - /// } - /// ); - /// ``` - pub fn merge(&mut self, other: Compound) { - for (k, v) in other { - match (self.entry(k), v) { - (Entry::Occupied(mut oe), Value::Compound(other)) => { - if let Value::Compound(this) = oe.get_mut() { - // Insert compound recursively. - this.merge(other); - } - } - (Entry::Occupied(mut oe), value) => { - oe.insert(value); - } - (Entry::Vacant(ve), value) => { - ve.insert(value); - } - } - } - } -} - impl fmt::Debug for Compound { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.map.fmt(f) @@ -241,6 +177,59 @@ impl Compound { { self.map.retain(f) } + + /// Inserts all items from `other` into `self` recursively. + /// + /// # Example + /// + /// ``` + /// use valence_nbt::compound; + /// + /// let mut this = compound! { + /// "foo" => 10, + /// "bar" => compound! { + /// "baz" => 20, + /// } + /// }; + /// + /// let other = compound! { + /// "foo" => 15, + /// "bar" => compound! { + /// "quux" => "hello", + /// } + /// }; + /// + /// this.merge(other); + /// + /// assert_eq!( + /// this, + /// compound! { + /// "foo" => 15, + /// "bar" => compound! { + /// "baz" => 20, + /// "quux" => "hello", + /// } + /// } + /// ); + /// ``` + pub fn merge(&mut self, other: Compound) { + for (k, v) in other { + match (self.entry(k), v) { + (Entry::Occupied(mut oe), Value::Compound(other)) => { + if let Value::Compound(this) = oe.get_mut() { + // Insert compound recursively. + this.merge(other); + } + } + (Entry::Occupied(mut oe), value) => { + oe.insert(value); + } + (Entry::Vacant(ve), value) => { + ve.insert(value); + } + } + } + } } impl Extend<(String, Value)> for Compound { @@ -502,3 +491,23 @@ pub struct ValuesMut<'a> { } impl_iterator_traits!((ValuesMut<'a>) => &'a mut Value); + +#[cfg(test)] +mod tests { + #[cfg(feature = "preserve_order")] + #[test] + fn compound_preserves_order() { + use super::*; + + let letters = ["g", "b", "d", "e", "h", "z", "m", "a", "q"]; + + let mut c = Compound::new(); + for l in letters { + c.insert(l, 0_i8); + } + + for (k, l) in c.keys().zip(letters) { + assert_eq!(k, l); + } + } +} diff --git a/crates/valence_nbt/src/lib.rs b/crates/valence_nbt/src/lib.rs index 5038f92b7..addcee0fe 100644 --- a/crates/valence_nbt/src/lib.rs +++ b/crates/valence_nbt/src/lib.rs @@ -18,25 +18,18 @@ )] pub use compound::Compound; -pub use error::Error; -pub use from_binary_slice::from_binary_slice; pub use tag::Tag; -pub use to_binary_writer::to_binary_writer; pub use value::{List, Value}; +#[cfg(feature = "binary")] +pub mod binary; pub mod compound; -mod error; -mod from_binary_slice; -mod modified_utf8; +#[cfg(feature = "serde")] +pub mod serde; +#[cfg(feature = "snbt")] pub mod snbt; -mod to_binary_writer; -pub mod value; - mod tag; -#[cfg(test)] -mod tests; - -type Result = std::result::Result; +pub mod value; /// A convenience macro for constructing [`Compound`]s. /// diff --git a/crates/valence_nbt/src/serde.rs b/crates/valence_nbt/src/serde.rs new file mode 100644 index 000000000..7dd780f60 --- /dev/null +++ b/crates/valence_nbt/src/serde.rs @@ -0,0 +1,160 @@ +use std::fmt; +use std::mem::ManuallyDrop; + +pub use ser::*; +use thiserror::Error; + +mod de; +mod ser; + +/// Errors that can occur while serializing or deserializing. +#[derive(Clone, Error, Debug)] +#[error("{0}")] + +pub struct Error(Box); + +impl Error { + fn new(s: impl Into>) -> Self { + Self(s.into()) + } +} + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + Self::new(format!("{msg}")) + } +} + +impl serde::ser::Error for Error { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + Self::new(format!("{msg}")) + } +} + +#[inline] +fn u8_vec_to_i8_vec(vec: Vec) -> Vec { + // SAFETY: Layouts of u8 and i8 are the same and we're being careful not to drop + // the original vec after calling Vec::from_raw_parts. + unsafe { + let mut vec = ManuallyDrop::new(vec); + Vec::from_raw_parts(vec.as_mut_ptr() as *mut i8, vec.len(), vec.capacity()) + } +} + +#[inline] +fn i8_vec_to_u8_vec(vec: Vec) -> Vec { + // SAFETY: Layouts of u8 and i8 are the same and we're being careful not to drop + // the original vec after calling Vec::from_raw_parts. + unsafe { + let mut vec = ManuallyDrop::new(vec); + Vec::from_raw_parts(vec.as_mut_ptr() as *mut u8, vec.len(), vec.capacity()) + } +} + +#[cfg(test)] +mod tests { + use pretty_assertions::assert_eq; + use serde::{Deserialize, Serialize}; + use serde_json::json; + + use super::*; + use crate::{compound, Compound, List}; + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Struct { + foo: i32, + bar: StructInner, + baz: String, + quux: Vec, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct StructInner { + a: bool, + b: i64, + c: Vec>, + d: Vec, + } + + fn make_struct() -> Struct { + Struct { + foo: i32::MIN, + bar: StructInner { + a: true, + b: 123456789, + c: vec![vec![1, 2, 3], vec![4, 5, 6]], + d: vec![], + }, + baz: "🤨".into(), + quux: vec![std::f32::consts::PI, f32::MAX, f32::MIN], + } + } + + fn make_compound() -> Compound { + compound! { + "foo" => i32::MIN, + "bar" => compound! { + "a" => true, + "b" => 123456789_i64, + "c" => List::IntArray(vec![vec![1, 2, 3], vec![4, 5, 6]]), + "d" => List::End, + }, + "baz" => "🤨", + "quux" => List::Float(vec![ + std::f32::consts::PI, + f32::MAX, + f32::MIN, + ]), + } + } + + fn make_json() -> serde_json::Value { + json!({ + "foo": i32::MIN, + "bar": { + "a": true, + "b": 123456789_i64, + "c": [[1, 2, 3], [4, 5, 6]], + "d": [] + }, + "baz": "🤨", + "quux": [ + std::f32::consts::PI, + f32::MAX, + f32::MIN, + ] + }) + } + + #[test] + fn struct_to_compound() { + let c = make_struct().serialize(CompoundSerializer).unwrap(); + + assert_eq!(c, make_compound()); + } + + #[test] + fn compound_to_struct() { + let s = Struct::deserialize(make_compound()).unwrap(); + + assert_eq!(s, make_struct()); + } + + #[test] + fn compound_to_json() { + let mut j = serde_json::to_value(make_compound()).unwrap(); + + // Bools map to bytes in NBT, but the result should be the same otherwise. + let p = j.pointer_mut("/bar/a").unwrap(); + assert_eq!(*p, serde_json::Value::from(1)); + *p = true.into(); + + assert_eq!(j, make_json()); + } +} diff --git a/crates/valence_nbt/src/serde/de.rs b/crates/valence_nbt/src/serde/de.rs new file mode 100644 index 000000000..199618a57 --- /dev/null +++ b/crates/valence_nbt/src/serde/de.rs @@ -0,0 +1,356 @@ +use std::{fmt, slice}; + +use serde::de::value::{MapAccessDeserializer, MapDeserializer, SeqAccessDeserializer}; +use serde::de::{self, IntoDeserializer, SeqAccess, Visitor}; +use serde::{forward_to_deserialize_any, Deserialize, Deserializer}; + +use super::Error; +use crate::serde::{i8_vec_to_u8_vec, u8_vec_to_i8_vec}; +use crate::{Compound, List, Value}; + +impl<'de> Deserialize<'de> for Value { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ValueVisitor; + + impl<'de> Visitor<'de> for ValueVisitor { + type Value = Value; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid NBT type") + } + + fn visit_bool(self, v: bool) -> Result + where + E: de::Error, + { + Ok(Value::Byte(v as _)) + } + + fn visit_i8(self, v: i8) -> Result + where + E: de::Error, + { + Ok(Value::Byte(v)) + } + + fn visit_i16(self, v: i16) -> Result + where + E: de::Error, + { + Ok(Value::Short(v)) + } + + fn visit_i32(self, v: i32) -> Result + where + E: de::Error, + { + Ok(Value::Int(v)) + } + + fn visit_i64(self, v: i64) -> Result + where + E: de::Error, + { + Ok(Value::Long(v)) + } + + fn visit_u8(self, v: u8) -> Result + where + E: de::Error, + { + Ok(Value::Byte(v as _)) + } + + fn visit_u16(self, v: u16) -> Result + where + E: de::Error, + { + Ok(Value::Short(v as _)) + } + + fn visit_u32(self, v: u32) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as _)) + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + Ok(Value::Long(v as _)) + } + + fn visit_f32(self, v: f32) -> Result + where + E: de::Error, + { + Ok(Value::Float(v)) + } + + fn visit_f64(self, v: f64) -> Result + where + E: de::Error, + { + Ok(Value::Double(v)) + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(Value::String(v.into())) + } + + fn visit_string(self, v: String) -> Result + where + E: de::Error, + { + Ok(Value::String(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + let slice: &[i8] = + unsafe { slice::from_raw_parts(v.as_ptr() as *const i8, v.len()) }; + + Ok(Value::ByteArray(slice.into())) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: de::Error, + { + Ok(Value::ByteArray(u8_vec_to_i8_vec(v))) + } + + fn visit_seq(self, seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + Ok(List::deserialize(SeqAccessDeserializer::new(seq))?.into()) + } + + fn visit_map(self, map: A) -> Result + where + A: de::MapAccess<'de>, + { + Ok(Compound::deserialize(MapAccessDeserializer::new(map))?.into()) + } + } + + deserializer.deserialize_any(ValueVisitor) + } +} + +impl<'de> Deserialize<'de> for List { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ListVisitor; + + impl<'de> Visitor<'de> for ListVisitor { + type Value = List; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a sequence or bytes") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + match seq.next_element::()? { + Some(v) => match v { + Value::Byte(v) => deserialize_seq_remainder(v, seq), + Value::Short(v) => deserialize_seq_remainder(v, seq), + Value::Int(v) => deserialize_seq_remainder(v, seq), + Value::Long(v) => deserialize_seq_remainder(v, seq), + Value::Float(v) => deserialize_seq_remainder(v, seq), + Value::Double(v) => deserialize_seq_remainder(v, seq), + Value::ByteArray(v) => deserialize_seq_remainder(v, seq), + Value::String(v) => deserialize_seq_remainder(v, seq), + Value::List(v) => deserialize_seq_remainder(v, seq), + Value::Compound(v) => deserialize_seq_remainder(v, seq), + Value::IntArray(v) => deserialize_seq_remainder(v, seq), + Value::LongArray(v) => deserialize_seq_remainder(v, seq), + }, + None => Ok(List::End), + } + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: de::Error, + { + Ok(List::Byte(u8_vec_to_i8_vec(v))) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + let bytes: &[i8] = + unsafe { slice::from_raw_parts(v.as_ptr() as *const i8, v.len()) }; + + Ok(List::Byte(bytes.into())) + } + } + + deserializer.deserialize_seq(ListVisitor) + } +} + +/// Deserializes the remainder of a sequence after having +/// determined the type of the first element. +fn deserialize_seq_remainder<'de, T, A, R>(first: T, mut seq: A) -> Result +where + T: Deserialize<'de>, + Vec: Into, + A: de::SeqAccess<'de>, +{ + let mut vec = match seq.size_hint() { + Some(n) => Vec::with_capacity(n + 1), + None => Vec::new(), + }; + + vec.push(first); + + while let Some(v) = seq.next_element()? { + vec.push(v); + } + + Ok(vec.into()) +} + +impl<'de> Deserializer<'de> for Compound { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_map(MapDeserializer::new(self.into_iter())) + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } +} + +impl<'de> IntoDeserializer<'de, Error> for Compound { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> Deserializer<'de> for Value { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self { + Value::Byte(v) => visitor.visit_i8(v), + Value::Short(v) => visitor.visit_i16(v), + Value::Int(v) => visitor.visit_i32(v), + Value::Long(v) => visitor.visit_i64(v), + Value::Float(v) => visitor.visit_f32(v), + Value::Double(v) => visitor.visit_f64(v), + Value::ByteArray(v) => visitor.visit_byte_buf(i8_vec_to_u8_vec(v)), + Value::String(v) => visitor.visit_string(v), + Value::List(v) => v.deserialize_any(visitor), + Value::Compound(v) => v.into_deserializer().deserialize_any(visitor), + Value::IntArray(v) => v.into_deserializer().deserialize_any(visitor), + Value::LongArray(v) => v.into_deserializer().deserialize_any(visitor), + } + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self { + Value::Byte(b) => visitor.visit_bool(b != 0), + _ => self.deserialize_any(visitor), + } + } + + forward_to_deserialize_any! { + i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } +} + +impl<'de> IntoDeserializer<'de, Error> for Value { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> Deserializer<'de> for List { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + struct EndSeqAccess; + + impl<'de> SeqAccess<'de> for EndSeqAccess { + type Error = Error; + + fn next_element_seed(&mut self, _seed: T) -> Result, Self::Error> + where + T: de::DeserializeSeed<'de>, + { + Ok(None) + } + } + + match self { + List::End => visitor.visit_seq(EndSeqAccess), + List::Byte(v) => visitor.visit_byte_buf(i8_vec_to_u8_vec(v)), + List::Short(v) => v.into_deserializer().deserialize_any(visitor), + List::Int(v) => v.into_deserializer().deserialize_any(visitor), + List::Long(v) => v.into_deserializer().deserialize_any(visitor), + List::Float(v) => v.into_deserializer().deserialize_any(visitor), + List::Double(v) => v.into_deserializer().deserialize_any(visitor), + List::ByteArray(v) => v.into_deserializer().deserialize_any(visitor), + List::String(v) => v.into_deserializer().deserialize_any(visitor), + List::List(v) => v.into_deserializer().deserialize_any(visitor), + List::Compound(v) => v.into_deserializer().deserialize_any(visitor), + List::IntArray(v) => v.into_deserializer().deserialize_any(visitor), + List::LongArray(v) => v.into_deserializer().deserialize_any(visitor), + } + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } +} + +impl<'de> IntoDeserializer<'de, Error> for List { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} diff --git a/crates/valence_nbt/src/serde/ser.rs b/crates/valence_nbt/src/serde/ser.rs new file mode 100644 index 000000000..941beffbb --- /dev/null +++ b/crates/valence_nbt/src/serde/ser.rs @@ -0,0 +1,625 @@ +use core::slice; +use std::marker::PhantomData; + +use serde::ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct}; +use serde::{Serialize, Serializer}; + +use super::{u8_vec_to_i8_vec, Error}; +use crate::{Compound, List, Value}; + +impl Serialize for Value { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Value::Byte(v) => serializer.serialize_i8(*v), + Value::Short(v) => serializer.serialize_i16(*v), + Value::Int(v) => serializer.serialize_i32(*v), + Value::Long(v) => serializer.serialize_i64(*v), + Value::Float(v) => serializer.serialize_f32(*v), + Value::Double(v) => serializer.serialize_f64(*v), + Value::ByteArray(v) => { + // SAFETY: i8 has the same layout as u8. + let bytes = unsafe { slice::from_raw_parts(v.as_ptr() as *const u8, v.len()) }; + + serializer.serialize_bytes(bytes) + } + Value::String(v) => serializer.serialize_str(v), + Value::List(v) => v.serialize(serializer), + Value::Compound(v) => v.serialize(serializer), + Value::IntArray(v) => v.serialize(serializer), + Value::LongArray(v) => v.serialize(serializer), + } + } +} + +impl Serialize for List { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + List::End => serializer.serialize_seq(Some(0))?.end(), + List::Byte(v) => v.serialize(serializer), + List::Short(v) => v.serialize(serializer), + List::Int(v) => v.serialize(serializer), + List::Long(v) => v.serialize(serializer), + List::Float(v) => v.serialize(serializer), + List::Double(v) => v.serialize(serializer), + List::ByteArray(v) => v.serialize(serializer), + List::String(v) => v.serialize(serializer), + List::List(v) => v.serialize(serializer), + List::Compound(v) => v.serialize(serializer), + List::IntArray(v) => v.serialize(serializer), + List::LongArray(v) => v.serialize(serializer), + } + } +} + +macro_rules! unsupported { + ($lit:literal) => { + Err(Error::new(concat!("unsupported type: ", $lit))) + }; +} + +/// [`Serializer`] whose output is [`Compound`]. +pub struct CompoundSerializer; + +impl Serializer for CompoundSerializer { + type Ok = Compound; + + type Error = Error; + + type SerializeSeq = Impossible; + + type SerializeTuple = Impossible; + + type SerializeTupleStruct = Impossible; + + type SerializeTupleVariant = Impossible; + + type SerializeMap = GenericSerializeMap; + + type SerializeStruct = GenericSerializeStruct; + + type SerializeStructVariant = Impossible; + + fn serialize_bool(self, _v: bool) -> Result { + unsupported!("bool") + } + + fn serialize_i8(self, _v: i8) -> Result { + unsupported!("i8") + } + + fn serialize_i16(self, _v: i16) -> Result { + unsupported!("i16") + } + + fn serialize_i32(self, _v: i32) -> Result { + unsupported!("i32") + } + + fn serialize_i64(self, _v: i64) -> Result { + unsupported!("i64") + } + + fn serialize_u8(self, _v: u8) -> Result { + unsupported!("u8") + } + + fn serialize_u16(self, _v: u16) -> Result { + unsupported!("u16") + } + + fn serialize_u32(self, _v: u32) -> Result { + unsupported!("u32") + } + + fn serialize_u64(self, _v: u64) -> Result { + unsupported!("u64") + } + + fn serialize_f32(self, _v: f32) -> Result { + unsupported!("f32") + } + + fn serialize_f64(self, _v: f64) -> Result { + unsupported!("f64") + } + + fn serialize_char(self, _v: char) -> Result { + unsupported!("char") + } + + fn serialize_str(self, _v: &str) -> Result { + unsupported!("str") + } + + fn serialize_bytes(self, _v: &[u8]) -> Result { + unsupported!("bytes") + } + + fn serialize_none(self) -> Result { + unsupported!("none") + } + + fn serialize_some(self, _value: &T) -> Result + where + T: Serialize, + { + unsupported!("some") + } + + fn serialize_unit(self) -> Result { + unsupported!("unit") + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + unsupported!("unit struct") + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + ) -> Result { + unsupported!("unit variant") + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + _value: &T, + ) -> Result + where + T: Serialize, + { + unsupported!("newtype struct") + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: Serialize, + { + unsupported!("newtype variant") + } + + fn serialize_seq(self, _len: Option) -> Result { + unsupported!("seq") + } + + fn serialize_tuple(self, _len: usize) -> Result { + unsupported!("tuple") + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + unsupported!("tuple struct") + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + unsupported!("tuple variant") + } + + fn serialize_map(self, len: Option) -> Result { + Ok(GenericSerializeMap::new(len)) + } + + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + Ok(GenericSerializeStruct::new(len)) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + unsupported!("struct variant") + } +} + +/// [`Serializer`] whose output is [`Value`]. +struct ValueSerializer; + +impl Serializer for ValueSerializer { + type Ok = Value; + + type Error = Error; + + type SerializeSeq = ValueSerializeSeq; + + type SerializeTuple = Impossible; + + type SerializeTupleStruct = Impossible; + + type SerializeTupleVariant = Impossible; + + type SerializeMap = GenericSerializeMap; + + type SerializeStruct = GenericSerializeStruct; + + type SerializeStructVariant = Impossible; + + fn serialize_bool(self, v: bool) -> Result { + Ok(Value::Byte(v as _)) + } + + fn serialize_i8(self, v: i8) -> Result { + Ok(Value::Byte(v)) + } + + fn serialize_i16(self, v: i16) -> Result { + Ok(Value::Short(v)) + } + + fn serialize_i32(self, v: i32) -> Result { + Ok(Value::Int(v)) + } + + fn serialize_i64(self, v: i64) -> Result { + Ok(Value::Long(v)) + } + + fn serialize_u8(self, v: u8) -> Result { + Ok(Value::Byte(v as _)) + } + + fn serialize_u16(self, v: u16) -> Result { + Ok(Value::Short(v as _)) + } + + fn serialize_u32(self, v: u32) -> Result { + Ok(Value::Int(v as _)) + } + + fn serialize_u64(self, v: u64) -> Result { + Ok(Value::Long(v as _)) + } + + fn serialize_f32(self, v: f32) -> Result { + Ok(Value::Float(v)) + } + + fn serialize_f64(self, v: f64) -> Result { + Ok(Value::Double(v)) + } + + fn serialize_char(self, v: char) -> Result { + Ok(Value::String(v.into())) + } + + fn serialize_str(self, v: &str) -> Result { + Ok(Value::String(v.into())) + } + + fn serialize_bytes(self, v: &[u8]) -> Result { + Ok(Value::ByteArray(u8_vec_to_i8_vec(v.into()))) + } + + fn serialize_none(self) -> Result { + unsupported!("none") + } + + fn serialize_some(self, _value: &T) -> Result + where + T: Serialize, + { + unsupported!("some") + } + + fn serialize_unit(self) -> Result { + unsupported!("unit") + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + self.serialize_unit() + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + ) -> Result { + unsupported!("unit variant") + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: Serialize, + { + unsupported!("newtype variant") + } + + fn serialize_seq(self, len: Option) -> Result { + Ok(ValueSerializeSeq::End { + len: len.unwrap_or(0), + }) + } + + fn serialize_tuple(self, _len: usize) -> Result { + unsupported!("tuple") + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + unsupported!("tuple struct") + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + unsupported!("tuple variant") + } + + fn serialize_map(self, len: Option) -> Result { + Ok(GenericSerializeMap::new(len)) + } + + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + Ok(GenericSerializeStruct::new(len)) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + unsupported!("struct variant") + } +} + +enum ValueSerializeSeq { + End { len: usize }, + Byte(Vec), + Short(Vec), + Int(Vec), + Long(Vec), + Float(Vec), + Double(Vec), + ByteArray(Vec>), + String(Vec), + List(Vec), + Compound(Vec), + IntArray(Vec>), + LongArray(Vec>), +} + +impl SerializeSeq for ValueSerializeSeq { + type Ok = Value; + + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + macro_rules! serialize_variant { + ($variant:ident, $vec:ident, $elem:ident) => {{ + match $elem.serialize(ValueSerializer)? { + Value::$variant(val) => { + $vec.push(val); + Ok(()) + } + _ => Err(Error::new(concat!( + "heterogeneous NBT list (expected `", + stringify!($variant), + "` element)" + ))), + } + }}; + } + + match self { + Self::End { len } => { + fn vec(elem: T, len: usize) -> Vec { + let mut vec = Vec::with_capacity(len); + vec.push(elem); + vec + } + + // Set the first element of the list. + *self = match value.serialize(ValueSerializer)? { + Value::Byte(v) => Self::Byte(vec(v, *len)), + Value::Short(v) => Self::Short(vec(v, *len)), + Value::Int(v) => Self::Int(vec(v, *len)), + Value::Long(v) => Self::Long(vec(v, *len)), + Value::Float(v) => Self::Float(vec(v, *len)), + Value::Double(v) => Self::Double(vec(v, *len)), + Value::ByteArray(v) => Self::ByteArray(vec(v, *len)), + Value::String(v) => Self::String(vec(v, *len)), + Value::List(v) => Self::List(vec(v, *len)), + Value::Compound(v) => Self::Compound(vec(v, *len)), + Value::IntArray(v) => Self::IntArray(vec(v, *len)), + Value::LongArray(v) => Self::LongArray(vec(v, *len)), + }; + Ok(()) + } + Self::Byte(v) => serialize_variant!(Byte, v, value), + Self::Short(v) => serialize_variant!(Short, v, value), + Self::Int(v) => serialize_variant!(Int, v, value), + Self::Long(v) => serialize_variant!(Long, v, value), + Self::Float(v) => serialize_variant!(Float, v, value), + Self::Double(v) => serialize_variant!(Double, v, value), + Self::ByteArray(v) => serialize_variant!(ByteArray, v, value), + Self::String(v) => serialize_variant!(String, v, value), + Self::List(v) => serialize_variant!(List, v, value), + Self::Compound(v) => serialize_variant!(Compound, v, value), + Self::IntArray(v) => serialize_variant!(IntArray, v, value), + Self::LongArray(v) => serialize_variant!(LongArray, v, value), + } + } + + fn end(self) -> Result { + Ok(match self { + Self::End { .. } => List::End.into(), + Self::Byte(v) => v.into(), + Self::Short(v) => List::Short(v).into(), + Self::Int(v) => v.into(), + Self::Long(v) => List::Long(v).into(), + Self::Float(v) => List::Float(v).into(), + Self::Double(v) => List::Double(v).into(), + Self::ByteArray(v) => List::ByteArray(v).into(), + Self::String(v) => List::String(v).into(), + Self::List(v) => List::List(v).into(), + Self::Compound(v) => List::Compound(v).into(), + Self::IntArray(v) => List::IntArray(v).into(), + Self::LongArray(v) => List::LongArray(v).into(), + }) + } +} + +#[doc(hidden)] +pub struct GenericSerializeMap { + /// Temp storage for `serialize_key`. + key: Option, + res: Compound, + _marker: PhantomData, +} + +impl GenericSerializeMap { + pub fn new(len: Option) -> Self { + Self { + key: None, + res: Compound::with_capacity(len.unwrap_or(0)), + _marker: PhantomData, + } + } +} + +impl SerializeMap for GenericSerializeMap +where + Compound: Into, +{ + type Ok = Ok; + + type Error = Error; + + fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + debug_assert!( + self.key.is_none(), + "call to `serialize_key` must be followed by `serialize_value`" + ); + + match key.serialize(ValueSerializer)? { + Value::String(s) => { + self.key = Some(s); + Ok(()) + } + _ => Err(Error::new("invalid map key type (expected string)")), + } + } + + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + let key = self + .key + .take() + .expect("missing previous call to `serialize_key`"); + self.res.insert(key, value.serialize(ValueSerializer)?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.res.into()) + } +} + +#[doc(hidden)] +pub struct GenericSerializeStruct { + c: Compound, + _marker: PhantomData, +} + +impl GenericSerializeStruct { + fn new(len: usize) -> Self { + Self { + c: Compound::with_capacity(len), + _marker: PhantomData, + } + } +} + +impl SerializeStruct for GenericSerializeStruct +where + Compound: Into, +{ + type Ok = Ok; + + type Error = Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Self::Error> + where + T: Serialize, + { + self.c.insert(key, value.serialize(ValueSerializer)?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.c.into()) + } +} diff --git a/crates/valence_network/src/connect.rs b/crates/valence_network/src/connect.rs index 0339f218b..73c6f94ac 100644 --- a/crates/valence_network/src/connect.rs +++ b/crates/valence_network/src/connect.rs @@ -4,7 +4,7 @@ use std::io; use std::net::SocketAddr; use std::time::Duration; -use anyhow::{anyhow, bail, ensure, Context}; +use anyhow::{bail, ensure, Context}; use base64::prelude::*; use hmac::digest::Update; use hmac::{Hmac, Mac}; @@ -19,23 +19,20 @@ use tokio::net::{TcpListener, TcpStream}; use tracing::{error, info, trace, warn}; use uuid::Uuid; use valence_client::is_valid_username; -use valence_core::packet::c2s::handshake::handshake::NextState; -use valence_core::packet::c2s::handshake::HandshakeC2s; -use valence_core::packet::c2s::login::{LoginHelloC2s, LoginKeyC2s, LoginQueryResponseC2s}; -use valence_core::packet::c2s::status::{QueryPingC2s, QueryRequestC2s}; -use valence_core::packet::decode::PacketDecoder; -use valence_core::packet::encode::PacketEncoder; -use valence_core::packet::raw::RawBytes; -use valence_core::packet::s2c::login::{ - LoginCompressionS2c, LoginDisconnectS2c, LoginHelloS2c, LoginQueryRequestS2c, LoginSuccessS2c, -}; -use valence_core::packet::s2c::status::{QueryPongS2c, QueryResponseS2c}; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::Decode; use valence_core::property::Property; +use valence_core::protocol::decode::PacketDecoder; +use valence_core::protocol::encode::PacketEncoder; +use valence_core::protocol::raw::RawBytes; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::Decode; use valence_core::text::Text; use valence_core::{ident, translation_key, MINECRAFT_VERSION, PROTOCOL_VERSION}; +use crate::packet::{ + HandshakeC2s, HandshakeNextState, LoginCompressionS2c, LoginDisconnectS2c, LoginHelloC2s, + LoginHelloS2c, LoginKeyC2s, LoginQueryRequestS2c, LoginQueryResponseC2s, LoginSuccessS2c, + QueryPingC2s, QueryPongS2c, QueryRequestC2s, QueryResponseS2c, +}; use crate::packet_io::PacketIo; use crate::{CleanupOnDrop, ConnectionMode, NewClientInfo, ServerListPing, SharedNetworkState}; @@ -101,7 +98,7 @@ async fn handle_connection(shared: SharedNetworkState, stream: TcpStream, remote struct HandshakeData { protocol_version: i32, server_address: String, - next_state: NextState, + next_state: HandshakeNextState, } async fn handle_handshake( @@ -124,10 +121,10 @@ async fn handle_handshake( ); match handshake.next_state { - NextState::Status => handle_status(shared, io, remote_addr, handshake) + HandshakeNextState::Status => handle_status(shared, io, remote_addr, handshake) .await .context("error handling status"), - NextState::Login => { + HandshakeNextState::Login => { match handle_login(&shared, &mut io, remote_addr, handshake) .await .context("error handling login")? @@ -230,7 +227,9 @@ async fn handle_login( let info = match shared.connection_mode() { ConnectionMode::Online { .. } => login_online(shared, conn, remote_addr, username).await?, ConnectionMode::Offline => login_offline(remote_addr, username)?, - ConnectionMode::BungeeCord => login_bungeecord(&handshake.server_address, username)?, + ConnectionMode::BungeeCord => { + login_bungeecord(remote_addr, &handshake.server_address, username)? + } ConnectionMode::Velocity { secret } => login_velocity(conn, username, secret).await?, }; @@ -375,11 +374,15 @@ fn auth_digest(bytes: &[u8]) -> String { BigInt::from_signed_bytes_be(bytes).to_str_radix(16) } +fn offline_uuid(username: &str) -> anyhow::Result { + Uuid::from_slice(&Sha256::digest(username)[..16]).map_err(Into::into) +} + /// Login procedure for offline mode. fn login_offline(remote_addr: SocketAddr, username: String) -> anyhow::Result { Ok(NewClientInfo { // Derive the client's UUID from a hash of their username. - uuid: Uuid::from_slice(&Sha256::digest(username.as_str())[..16])?, + uuid: offline_uuid(username.as_str())?, username, properties: vec![].into(), ip: remote_addr.ip(), @@ -387,24 +390,40 @@ fn login_offline(remote_addr: SocketAddr, username: String) -> anyhow::Result anyhow::Result { +fn login_bungeecord( + remote_addr: SocketAddr, + server_address: &str, + username: String, +) -> anyhow::Result { // Get data from server_address field of the handshake - let [_, client_ip, uuid, properties]: [&str; 4] = server_address - .split('\0') - .take(4) - .collect::>() - .try_into() - .map_err(|_| anyhow!("malformed BungeeCord server address data"))?; + let data = server_address.split('\0').take(4).collect::>(); + + // Ip of player, only given if ip_forward on bungee is true + let ip = match data.get(1) { + Some(ip) => ip.parse()?, + None => remote_addr.ip(), + }; + + // Uuid of player, only given if ip_forward on bungee is true + let uuid = match data.get(2) { + Some(uuid) => uuid.parse()?, + None => offline_uuid(username.as_str())?, + }; // Read properties and get textures - let properties: Vec = - serde_json::from_str(properties).context("failed to parse BungeeCord player properties")?; + // Properties of player's game profile, only given if ip_forward and online_mode + // on bungee both are true + let properties: Vec = match data.get(3) { + Some(properties) => serde_json::from_str(properties) + .context("failed to parse BungeeCord player properties")?, + None => vec![], + }; Ok(NewClientInfo { - uuid: uuid.parse()?, + uuid, username, properties: properties.into(), - ip: client_ip.parse()?, + ip, }) } diff --git a/crates/valence_network/src/lib.rs b/crates/valence_network/src/lib.rs index 40ab0b180..73784629b 100644 --- a/crates/valence_network/src/lib.rs +++ b/crates/valence_network/src/lib.rs @@ -19,6 +19,7 @@ mod byte_channel; mod connect; +pub mod packet; mod packet_io; use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; diff --git a/crates/valence_network/src/packet.rs b/crates/valence_network/src/packet.rs new file mode 100644 index 000000000..9016be0c8 --- /dev/null +++ b/crates/valence_network/src/packet.rs @@ -0,0 +1,105 @@ +use std::borrow::Cow; + +use uuid::Uuid; +use valence_core::ident::Ident; +use valence_core::property::Property; +use valence_core::protocol::raw::RawBytes; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_core::text::Text; + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::HANDSHAKE_C2S)] +pub struct HandshakeC2s<'a> { + pub protocol_version: VarInt, + pub server_address: &'a str, + pub server_port: u16, + pub next_state: HandshakeNextState, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Encode, Decode)] +pub enum HandshakeNextState { + #[packet(tag = 1)] + Status, + #[packet(tag = 2)] + Login, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::QUERY_PING_C2S)] +pub struct QueryPingC2s { + pub payload: u64, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::QUERY_REQUEST_C2S)] +pub struct QueryRequestC2s; + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::QUERY_PONG_S2C)] +pub struct QueryPongS2c { + pub payload: u64, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::QUERY_RESPONSE_S2C)] +pub struct QueryResponseS2c<'a> { + pub json: &'a str, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_HELLO_C2S)] +pub struct LoginHelloC2s<'a> { + pub username: &'a str, // TODO: bound this + pub profile_id: Option, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_KEY_C2S)] +pub struct LoginKeyC2s<'a> { + pub shared_secret: &'a [u8], + pub verify_token: &'a [u8], +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_QUERY_RESPONSE_C2S)] +pub struct LoginQueryResponseC2s<'a> { + pub message_id: VarInt, + pub data: Option>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_COMPRESSION_S2C)] +pub struct LoginCompressionS2c { + pub threshold: VarInt, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_DISCONNECT_S2C)] +pub struct LoginDisconnectS2c<'a> { + pub reason: Cow<'a, Text>, +} + +#[derive(Copy, Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_HELLO_S2C)] +pub struct LoginHelloS2c<'a> { + pub server_id: &'a str, + pub public_key: &'a [u8], + pub verify_token: &'a [u8], +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_QUERY_REQUEST_S2C)] +pub struct LoginQueryRequestS2c<'a> { + pub message_id: VarInt, + pub channel: Ident>, + pub data: RawBytes<'a>, +} + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::LOGIN_SUCCESS_S2C)] +pub struct LoginSuccessS2c<'a> { + pub uuid: Uuid, + pub username: &'a str, // TODO: bound this. + pub properties: Cow<'a, [Property]>, +} diff --git a/crates/valence_network/src/packet_io.rs b/crates/valence_network/src/packet_io.rs index ef7e5a311..7cdad5827 100644 --- a/crates/valence_network/src/packet_io.rs +++ b/crates/valence_network/src/packet_io.rs @@ -4,7 +4,7 @@ use std::time::{Duration, Instant}; use std::{io, mem}; use anyhow::bail; -use bytes::{Buf, BytesMut}; +use bytes::BytesMut; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; use tokio::sync::Semaphore; @@ -12,10 +12,9 @@ use tokio::task::JoinHandle; use tokio::time::timeout; use tracing::{debug, warn}; use valence_client::{ClientBundleArgs, ClientConnection, ReceivedPacket}; -use valence_core::packet::decode::{decode_packet, PacketDecoder}; -use valence_core::packet::encode::PacketEncoder; -use valence_core::packet::var_int::VarInt; -use valence_core::packet::{Decode, Packet}; +use valence_core::protocol::decode::{PacketDecoder, PacketFrame}; +use valence_core::protocol::encode::PacketEncoder; +use valence_core::protocol::{Decode, Encode, Packet}; use crate::byte_channel::{byte_channel, ByteSender, TrySendError}; use crate::{CleanupOnDrop, NewClientInfo}; @@ -24,7 +23,7 @@ pub(crate) struct PacketIo { stream: TcpStream, enc: PacketEncoder, dec: PacketDecoder, - frame: BytesMut, + frame: PacketFrame, timeout: Duration, } @@ -41,14 +40,17 @@ impl PacketIo { stream, enc, dec, - frame: BytesMut::new(), + frame: PacketFrame { + id: -1, + body: BytesMut::new(), + }, timeout, } } - pub(crate) async fn send_packet<'a, P>(&mut self, pkt: &P) -> anyhow::Result<()> + pub(crate) async fn send_packet

(&mut self, pkt: &P) -> anyhow::Result<()> where - P: Packet<'a>, + P: Packet + Encode, { self.enc.append_packet(pkt)?; let bytes = self.enc.take(); @@ -58,14 +60,14 @@ impl PacketIo { pub(crate) async fn recv_packet<'a, P>(&'a mut self) -> anyhow::Result

where - P: Packet<'a>, + P: Packet + Decode<'a>, { timeout(self.timeout, async { loop { if let Some(frame) = self.dec.try_next_packet()? { self.frame = frame; - return decode_packet(&self.frame); + return self.frame.decode(); } self.dec.reserve(READ_BUF_SIZE); @@ -114,8 +116,8 @@ impl PacketIo { let mut buf = BytesMut::new(); loop { - let mut data = match self.dec.try_next_packet() { - Ok(Some(data)) => data, + let frame = match self.dec.try_next_packet() { + Ok(Some(frame)) => frame, Ok(None) => { // Incomplete packet. Need more data. @@ -141,24 +143,8 @@ impl PacketIo { let timestamp = Instant::now(); - // Remove the packet ID from the front of the data. - let packet_id = { - let mut r = &data[..]; - - match VarInt::decode(&mut r) { - Ok(id) => { - data.advance(data.len() - r.len()); - id.0 - } - Err(e) => { - warn!("failed to decode packet ID: {e:#}"); - break; - } - } - }; - // Estimate memory usage of this packet. - let cost = mem::size_of::() + data.len(); + let cost = mem::size_of::() + frame.body.len(); if cost > incoming_byte_limit { debug!( @@ -182,8 +168,8 @@ impl PacketIo { let packet = ReceivedPacket { timestamp, - id: packet_id, - data: data.freeze(), + id: frame.id, + body: frame.body.freeze(), }; if incoming_sender.try_send(packet).is_err() { @@ -255,7 +241,7 @@ impl ClientConnection for RealClientConnection { fn try_recv(&mut self) -> anyhow::Result> { match self.recv.try_recv() { Ok(packet) => { - let cost = mem::size_of::() + packet.data.len(); + let cost = mem::size_of::() + packet.body.len(); // Add the permits back that we removed eariler. self.recv_sem.add_permits(cost); diff --git a/crates/valence_player_list/Cargo.toml b/crates/valence_player_list/Cargo.toml index d32714a5b..21aef41c6 100644 --- a/crates/valence_player_list/Cargo.toml +++ b/crates/valence_player_list/Cargo.toml @@ -4,9 +4,11 @@ version.workspace = true edition.workspace = true [dependencies] +anyhow.workspace = true bevy_app.workspace = true bevy_ecs.workspace = true +bitfield-struct.workspace = true valence_core.workspace = true valence_client.workspace = true valence_instance.workspace = true -uuid.workspace = true \ No newline at end of file +uuid.workspace = true diff --git a/crates/valence_player_list/src/lib.rs b/crates/valence_player_list/src/lib.rs index 1091d3927..0ed91ffd5 100644 --- a/crates/valence_player_list/src/lib.rs +++ b/crates/valence_player_list/src/lib.rs @@ -16,23 +16,27 @@ unreachable_pub, clippy::dbg_macro )] +#![allow(clippy::type_complexity)] + +pub mod packet; use std::borrow::Cow; use bevy_app::prelude::*; use bevy_ecs::prelude::*; +use packet::{PlayerListActions, PlayerListHeaderS2c, PlayerListS2c}; use uuid::Uuid; use valence_client::{Client, Ping, Properties, Username}; use valence_core::despawn::Despawned; use valence_core::game_mode::GameMode; -use valence_core::packet::encode::{PacketWriter, WritePacket}; -use valence_core::packet::s2c::play::player_list::{Actions, Entry, PlayerListS2c}; -use valence_core::packet::s2c::play::{PlayerListHeaderS2c, PlayerRemoveS2c}; +use valence_core::protocol::encode::{PacketWriter, WritePacket}; use valence_core::text::Text; use valence_core::uuid::UniqueId; use valence_core::Server; use valence_instance::WriteUpdatePacketsToInstancesSet; +use crate::packet::PlayerRemoveS2c; + pub struct PlayerListPlugin; #[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -189,7 +193,6 @@ fn add_new_clients_to_player_list( } } -#[allow(clippy::type_complexity)] fn init_player_list_for_clients( mut clients: Query<&mut Client, (Added, Without)>, player_list: Res, @@ -208,7 +211,7 @@ fn init_player_list_for_clients( ) { if player_list.manage_clients { for mut client in &mut clients { - let actions = Actions::new() + let actions = PlayerListActions::new() .with_add_player(true) .with_update_game_mode(true) .with_update_listed(true) @@ -218,15 +221,17 @@ fn init_player_list_for_clients( let entries: Vec<_> = entries .iter() .map( - |(uuid, username, props, game_mode, ping, display_name, listed)| Entry { - player_uuid: uuid.0, - username: &username.0, - properties: Cow::Borrowed(&props.0), - chat_data: None, - listed: listed.0, - ping: ping.0, - game_mode: *game_mode, - display_name: display_name.0.as_ref().map(Cow::Borrowed), + |(uuid, username, props, game_mode, ping, display_name, listed)| { + packet::PlayerListEntry { + player_uuid: uuid.0, + username: &username.0, + properties: Cow::Borrowed(&props.0), + chat_data: None, + listed: listed.0, + ping: ping.0, + game_mode: *game_mode, + display_name: display_name.0.as_ref().map(Cow::Borrowed), + } }, ) .collect(); @@ -277,7 +282,6 @@ fn remove_despawned_entries( } } -#[allow(clippy::type_complexity)] fn update_entries( entries: Query< ( @@ -314,7 +318,7 @@ fn update_entries( ); for (uuid, username, props, game_mode, ping, display_name, listed) in &entries { - let mut actions = Actions::new(); + let mut actions = PlayerListActions::new(); // Did a change occur that would force us to overwrite the entry? This also adds // new entries. @@ -356,7 +360,7 @@ fn update_entries( debug_assert_ne!(u8::from(actions), 0); } - let entry = Entry { + let entry = packet::PlayerListEntry { player_uuid: uuid.0, username: &username.0, properties: (&props.0).into(), diff --git a/crates/valence_core/src/packet/s2c/play/player_list.rs b/crates/valence_player_list/src/packet.rs similarity index 77% rename from crates/valence_core/src/packet/s2c/play/player_list.rs rename to crates/valence_player_list/src/packet.rs index fef3bc837..b842b5cbe 100644 --- a/crates/valence_core/src/packet/s2c/play/player_list.rs +++ b/crates/valence_player_list/src/packet.rs @@ -3,21 +3,28 @@ use std::io::Write; use bitfield_struct::bitfield; use uuid::Uuid; +use valence_core::game_mode::GameMode; +use valence_core::property::Property; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_core::text::Text; + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_LIST_HEADER_S2C)] +pub struct PlayerListHeaderS2c<'a> { + pub header: Cow<'a, Text>, + pub footer: Cow<'a, Text>, +} -use crate::game_mode::GameMode; -use crate::packet::var_int::VarInt; -use crate::packet::{Decode, Encode}; -use crate::property::Property; -use crate::text::Text; - -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Packet)] +#[packet(id = packet_id::PLAYER_LIST_S2C)] pub struct PlayerListS2c<'a> { - pub actions: Actions, - pub entries: Cow<'a, [Entry<'a>]>, + pub actions: PlayerListActions, + pub entries: Cow<'a, [PlayerListEntry<'a>]>, } #[bitfield(u8)] -pub struct Actions { +pub struct PlayerListActions { pub add_player: bool, pub initialize_chat: bool, pub update_game_mode: bool, @@ -29,7 +36,7 @@ pub struct Actions { } #[derive(Clone, Default, Debug)] -pub struct Entry<'a> { +pub struct PlayerListEntry<'a> { pub player_uuid: Uuid, pub username: &'a str, pub properties: Cow<'a, [Property]>, @@ -91,12 +98,12 @@ impl<'a> Encode for PlayerListS2c<'a> { impl<'a> Decode<'a> for PlayerListS2c<'a> { fn decode(r: &mut &'a [u8]) -> anyhow::Result { - let actions = Actions(u8::decode(r)?); + let actions = PlayerListActions(u8::decode(r)?); let mut entries = vec![]; for _ in 0..VarInt::decode(r)?.0 { - let mut entry = Entry { + let mut entry = PlayerListEntry { player_uuid: Uuid::decode(r)?, ..Default::default() }; @@ -135,3 +142,9 @@ impl<'a> Decode<'a> for PlayerListS2c<'a> { }) } } + +#[derive(Clone, PartialEq, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::PLAYER_REMOVE_S2C)] +pub struct PlayerRemoveS2c<'a> { + pub uuids: Cow<'a, [Uuid]>, +} diff --git a/crates/valence_registry/Cargo.toml b/crates/valence_registry/Cargo.toml index 6160285e3..02cf1af37 100644 --- a/crates/valence_registry/Cargo.toml +++ b/crates/valence_registry/Cargo.toml @@ -8,4 +8,6 @@ tracing.workspace = true valence_core.workspace = true valence_nbt.workspace = true bevy_ecs.workspace = true -bevy_app.workspace = true \ No newline at end of file +bevy_app.workspace = true +serde.workspace = true +serde_json.workspace = true \ No newline at end of file diff --git a/crates/valence_registry/src/lib.rs b/crates/valence_registry/src/lib.rs index 9d376a1c2..0d9bdaa60 100644 --- a/crates/valence_registry/src/lib.rs +++ b/crates/valence_registry/src/lib.rs @@ -25,6 +25,10 @@ use tracing::error; use valence_core::ident::Ident; use valence_nbt::{compound, Compound, List, Value}; +mod tags; + +pub use tags::*; + pub struct RegistryPlugin; /// The [`SystemSet`] where the [`RegistryCodec`] cache is rebuilt. Systems that @@ -35,8 +39,11 @@ pub struct RegistryCodecSet; impl Plugin for RegistryPlugin { fn build(&self, app: &mut bevy_app::App) { app.init_resource::() + .init_resource::() .configure_set(RegistryCodecSet.in_base_set(CoreSet::PostUpdate)) - .add_system(cache_registry_codec.in_set(RegistryCodecSet)); + .add_startup_system(init_tags_registry.in_set(RegistryCodecSet)) + .add_system(cache_registry_codec.in_set(RegistryCodecSet)) + .add_system(cache_tags_packet.in_set(RegistryCodecSet)); } } @@ -106,7 +113,7 @@ impl RegistryCodec { impl Default for RegistryCodec { fn default() -> Self { let codec = include_bytes!("../../../extracted/registry_codec_1.19.4.dat"); - let compound = valence_nbt::from_binary_slice(&mut codec.as_slice()) + let compound = Compound::from_binary(&mut codec.as_slice()) .expect("failed to decode vanilla registry codec") .0; diff --git a/crates/valence_registry/src/tags.rs b/crates/valence_registry/src/tags.rs new file mode 100644 index 000000000..b80ef0d45 --- /dev/null +++ b/crates/valence_registry/src/tags.rs @@ -0,0 +1,84 @@ +use std::borrow::Cow; + +use bevy_ecs::prelude::*; +use serde::Deserialize; +use valence_core::ident::Ident; +use valence_core::protocol::encode::{PacketWriter, WritePacket}; +use valence_core::protocol::var_int::VarInt; +use valence_core::protocol::{packet_id, Decode, Encode, Packet}; +use valence_core::Server; + +#[derive(Clone, Debug, Encode, Decode, Packet)] +#[packet(id = packet_id::SYNCHRONIZE_TAGS_S2C)] +pub struct SynchronizeTagsS2c<'a> { + pub registries: Cow<'a, [Registry]>, +} + +#[derive(Debug, Resource, Default)] +pub struct TagsRegistry { + pub registries: Vec, + cached_packet: Vec, +} + +#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Encode, Decode)] +pub struct Registry { + pub registry: Ident, + pub tags: Vec, +} + +#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Encode, Decode)] +pub struct TagEntry { + pub name: Ident, + pub entries: Vec, +} + +impl<'a> TagsRegistry { + pub(crate) fn build_synchronize_tags(&'a self) -> SynchronizeTagsS2c<'a> { + SynchronizeTagsS2c { + registries: Cow::Borrowed(&self.registries), + } + } + + pub fn sync_tags_packet(&self) -> &Vec { + &self.cached_packet + } +} + +pub(crate) fn init_tags_registry(mut tags: ResMut) { + let registries = + serde_json::from_str::>(include_str!("../../../extracted/tags.json")) + .expect("tags.json is invalid"); + tags.registries = registries; +} + +pub(crate) fn cache_tags_packet(server: Res, tags: ResMut) { + if tags.is_changed() { + let tags = tags.into_inner(); + let packet = tags.build_synchronize_tags(); + let mut bytes = vec![]; + let mut scratch = vec![]; + let mut writer = + PacketWriter::new(&mut bytes, server.compression_threshold(), &mut scratch); + writer.write_packet(&packet); + tags.cached_packet = bytes; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::RegistryPlugin; + + #[test] + fn smoke_test() { + let mut app = bevy_app::App::new(); + app.add_plugin(RegistryPlugin); + app.insert_resource(Server::default()); + app.update(); + + let tags_registry = app.world.get_resource::().unwrap(); + let packet = tags_registry.build_synchronize_tags(); + assert!(!packet.registries.is_empty()); + assert!(!tags_registry.cached_packet.is_empty()); + } +} diff --git a/crates/valence_schem/src/lib.rs b/crates/valence_schem/src/lib.rs index b8859bba3..308661683 100644 --- a/crates/valence_schem/src/lib.rs +++ b/crates/valence_schem/src/lib.rs @@ -34,8 +34,8 @@ use valence_block::{BlockEntityKind, BlockState, ParseBlockStateError}; use valence_core::block_pos::BlockPos; use valence_core::chunk_pos::ChunkPos; use valence_core::ident::Ident; -use valence_core::packet::var_int::{VarInt, VarIntDecodeError}; -use valence_core::packet::Encode; +use valence_core::protocol::var_int::{VarInt, VarIntDecodeError}; +use valence_core::protocol::Encode; use valence_instance::{Block as ValenceBlock, Instance}; use valence_nbt::{compound, Compound, List, Value}; @@ -93,7 +93,7 @@ pub enum LoadSchematicError { Io(#[from] io::Error), #[error(transparent)] - Nbt(#[from] valence_nbt::Error), + Nbt(#[from] valence_nbt::binary::Error), #[error("missing schematic")] MissingSchematic, @@ -230,7 +230,7 @@ pub enum SaveSchematicError { Io(#[from] io::Error), #[error(transparent)] - Nbt(#[from] valence_nbt::Error), + Nbt(#[from] valence_nbt::binary::Error), } impl Schematic { @@ -241,7 +241,7 @@ impl Schematic { let mut z = GzDecoder::new(BufReader::new(file)); z.read_to_end(&mut buf)?; - let root = valence_nbt::from_binary_slice(&mut buf.as_slice())?.0; + let root = Compound::from_binary(&mut buf.as_slice())?.0; Self::deserialize(&root) } @@ -760,7 +760,7 @@ impl Schematic { let nbt = self.serialize(); let file = File::create(path)?; let mut z = GzEncoder::new(file, Compression::best()); - valence_nbt::to_binary_writer(&mut z, &nbt, "")?; + nbt.to_binary(&mut z, "")?; z.flush()?; Ok(()) } diff --git a/extracted/blocks.json b/extracted/blocks.json index 514e496a2..59caa8b86 100644 --- a/extracted/blocks.json +++ b/extracted/blocks.json @@ -2,203 +2,203 @@ "block_entity_types": [ { "id": 0, - "namespace": "minecraft", - "path": "furnace" + "ident": "minecraft:furnace", + "name": "furnace" }, { "id": 1, - "namespace": "minecraft", - "path": "chest" + "ident": "minecraft:chest", + "name": "chest" }, { "id": 2, - "namespace": "minecraft", - "path": "trapped_chest" + "ident": "minecraft:trapped_chest", + "name": "trapped_chest" }, { "id": 3, - "namespace": "minecraft", - "path": "ender_chest" + "ident": "minecraft:ender_chest", + "name": "ender_chest" }, { "id": 4, - "namespace": "minecraft", - "path": "jukebox" + "ident": "minecraft:jukebox", + "name": "jukebox" }, { "id": 5, - "namespace": "minecraft", - "path": "dispenser" + "ident": "minecraft:dispenser", + "name": "dispenser" }, { "id": 6, - "namespace": "minecraft", - "path": "dropper" + "ident": "minecraft:dropper", + "name": "dropper" }, { "id": 7, - "namespace": "minecraft", - "path": "sign" + "ident": "minecraft:sign", + "name": "sign" }, { "id": 8, - "namespace": "minecraft", - "path": "hanging_sign" + "ident": "minecraft:hanging_sign", + "name": "hanging_sign" }, { "id": 9, - "namespace": "minecraft", - "path": "mob_spawner" + "ident": "minecraft:mob_spawner", + "name": "mob_spawner" }, { "id": 10, - "namespace": "minecraft", - "path": "piston" + "ident": "minecraft:piston", + "name": "piston" }, { "id": 11, - "namespace": "minecraft", - "path": "brewing_stand" + "ident": "minecraft:brewing_stand", + "name": "brewing_stand" }, { "id": 12, - "namespace": "minecraft", - "path": "enchanting_table" + "ident": "minecraft:enchanting_table", + "name": "enchanting_table" }, { "id": 13, - "namespace": "minecraft", - "path": "end_portal" + "ident": "minecraft:end_portal", + "name": "end_portal" }, { "id": 14, - "namespace": "minecraft", - "path": "beacon" + "ident": "minecraft:beacon", + "name": "beacon" }, { "id": 15, - "namespace": "minecraft", - "path": "skull" + "ident": "minecraft:skull", + "name": "skull" }, { "id": 16, - "namespace": "minecraft", - "path": "daylight_detector" + "ident": "minecraft:daylight_detector", + "name": "daylight_detector" }, { "id": 17, - "namespace": "minecraft", - "path": "hopper" + "ident": "minecraft:hopper", + "name": "hopper" }, { "id": 18, - "namespace": "minecraft", - "path": "comparator" + "ident": "minecraft:comparator", + "name": "comparator" }, { "id": 19, - "namespace": "minecraft", - "path": "banner" + "ident": "minecraft:banner", + "name": "banner" }, { "id": 20, - "namespace": "minecraft", - "path": "structure_block" + "ident": "minecraft:structure_block", + "name": "structure_block" }, { "id": 21, - "namespace": "minecraft", - "path": "end_gateway" + "ident": "minecraft:end_gateway", + "name": "end_gateway" }, { "id": 22, - "namespace": "minecraft", - "path": "command_block" + "ident": "minecraft:command_block", + "name": "command_block" }, { "id": 23, - "namespace": "minecraft", - "path": "shulker_box" + "ident": "minecraft:shulker_box", + "name": "shulker_box" }, { "id": 24, - "namespace": "minecraft", - "path": "bed" + "ident": "minecraft:bed", + "name": "bed" }, { "id": 25, - "namespace": "minecraft", - "path": "conduit" + "ident": "minecraft:conduit", + "name": "conduit" }, { "id": 26, - "namespace": "minecraft", - "path": "barrel" + "ident": "minecraft:barrel", + "name": "barrel" }, { "id": 27, - "namespace": "minecraft", - "path": "smoker" + "ident": "minecraft:smoker", + "name": "smoker" }, { "id": 28, - "namespace": "minecraft", - "path": "blast_furnace" + "ident": "minecraft:blast_furnace", + "name": "blast_furnace" }, { "id": 29, - "namespace": "minecraft", - "path": "lectern" + "ident": "minecraft:lectern", + "name": "lectern" }, { "id": 30, - "namespace": "minecraft", - "path": "bell" + "ident": "minecraft:bell", + "name": "bell" }, { "id": 31, - "namespace": "minecraft", - "path": "jigsaw" + "ident": "minecraft:jigsaw", + "name": "jigsaw" }, { "id": 32, - "namespace": "minecraft", - "path": "campfire" + "ident": "minecraft:campfire", + "name": "campfire" }, { "id": 33, - "namespace": "minecraft", - "path": "beehive" + "ident": "minecraft:beehive", + "name": "beehive" }, { "id": 34, - "namespace": "minecraft", - "path": "sculk_sensor" + "ident": "minecraft:sculk_sensor", + "name": "sculk_sensor" }, { "id": 35, - "namespace": "minecraft", - "path": "sculk_catalyst" + "ident": "minecraft:sculk_catalyst", + "name": "sculk_catalyst" }, { "id": 36, - "namespace": "minecraft", - "path": "sculk_shrieker" + "ident": "minecraft:sculk_shrieker", + "name": "sculk_shrieker" }, { "id": 37, - "namespace": "minecraft", - "path": "chiseled_bookshelf" + "ident": "minecraft:chiseled_bookshelf", + "name": "chiseled_bookshelf" }, { "id": 38, - "namespace": "minecraft", - "path": "suspicious_sand" + "ident": "minecraft:suspicious_sand", + "name": "suspicious_sand" }, { "id": 39, - "namespace": "minecraft", - "path": "decorated_pot" + "ident": "minecraft:decorated_pot", + "name": "decorated_pot" } ], "shapes": [ diff --git a/extracted/tags.json b/extracted/tags.json new file mode 100644 index 000000000..e83bc33c3 --- /dev/null +++ b/extracted/tags.json @@ -0,0 +1,7235 @@ +[ + { + "registry": "minecraft:painting_variant", + "tags": [ + { + "name": "minecraft:placeable", + "entries": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 29 + ] + } + ] + }, + { + "registry": "minecraft:worldgen/biome", + "tags": [ + { + "name": "c:climate_wet", + "entries": [ + 10, + 11, + 8, + 12, + 34, + 28, + 21, + 5, + 56, + 39, + 23, + 30, + 52, + 29, + 1, + 27, + 48 + ] + }, + { + "name": "minecraft:has_structure/nether_fortress", + "entries": [ + 33, + 47, + 6, + 57, + 2 + ] + }, + { + "name": "minecraft:water_on_map_outlines", + "entries": [ + 10, + 8, + 12, + 11, + 21, + 34, + 5, + 28, + 56, + 39, + 23, + 52, + 30 + ] + }, + { + "name": "minecraft:is_river", + "entries": [ + 39, + 23 + ] + }, + { + "name": "c:nether_forests", + "entries": [ + 57, + 6 + ] + }, + { + "name": "minecraft:has_structure/mineshaft", + "entries": [ + 10, + 8, + 12, + 11, + 21, + 34, + 5, + 28, + 56, + 39, + 23, + 3, + 43, + 31, + 22, + 26, + 49, + 45, + 60, + 58, + 59, + 53, + 46, + 36, + 37, + 1, + 27, + 48, + 20, + 19, + 4, + 35, + 7, + 24, + 50, + 32, + 25, + 61, + 13, + 40, + 44, + 38, + 51, + 52, + 30, + 41, + 14, + 29 + ] + }, + { + "name": "minecraft:has_structure/jungle_temple", + "entries": [ + 1, + 27 + ] + }, + { + "name": "c:mushroom", + "entries": [ + 32 + ] + }, + { + "name": "c:in_nether", + "entries": [ + 33, + 47, + 6, + 57, + 2 + ] + }, + { + "name": "c:tree_coniferous", + "entries": [ + 24, + 53, + 46, + 36, + 37 + ] + }, + { + "name": "c:mountain", + "entries": [ + 31, + 22, + 26, + 49, + 45 + ] + }, + { + "name": "c:savanna", + "entries": [ + 40, + 41, + 61 + ] + }, + { + "name": "minecraft:is_jungle", + "entries": [ + 1, + 27, + 48 + ] + }, + { + "name": "minecraft:is_ocean", + "entries": [ + 10, + 8, + 12, + 11, + 21, + 34, + 5, + 28, + 56 + ] + }, + { + "name": "c:caves", + "entries": [ + 9, + 14, + 29 + ] + }, + { + "name": "c:floral", + "entries": [ + 51, + 19 + ] + }, + { + "name": "minecraft:has_structure/buried_treasure", + "entries": [ + 3, + 43 + ] + }, + { + "name": "minecraft:has_structure/ocean_ruin_cold", + "entries": [ + 21, + 5, + 34, + 10, + 8, + 12 + ] + }, + { + "name": "minecraft:produces_corals_from_bonemeal", + "entries": [ + 56 + ] + }, + { + "name": "c:badlands", + "entries": [ + 62, + 18, + 0 + ] + }, + { + "name": "minecraft:has_structure/stronghold", + "entries": [ + 32, + 10, + 21, + 8, + 5, + 12, + 34, + 11, + 28, + 56, + 50, + 52, + 30, + 45, + 44, + 43, + 59, + 24, + 60, + 46, + 58, + 53, + 38, + 31, + 3, + 20, + 37, + 19, + 4, + 7, + 41, + 40, + 27, + 0, + 13, + 62, + 26, + 49, + 23, + 39, + 25, + 36, + 51, + 35, + 48, + 1, + 18, + 61, + 22, + 14, + 29, + 9 + ] + }, + { + "name": "minecraft:has_structure/mineshaft_mesa", + "entries": [ + 0, + 18, + 62 + ] + }, + { + "name": "minecraft:has_structure/ancient_city", + "entries": [ + 9 + ] + }, + { + "name": "c:underground", + "entries": [ + 9, + 14, + 29 + ] + }, + { + "name": "minecraft:has_structure/shipwreck", + "entries": [ + 10, + 8, + 12, + 11, + 21, + 34, + 5, + 28, + 56 + ] + }, + { + "name": "c:snowy_plains", + "entries": [ + 44 + ] + }, + { + "name": "minecraft:is_savanna", + "entries": [ + 40, + 41, + 61 + ] + }, + { + "name": "c:vegetation_dense", + "entries": [ + 1, + 27, + 48, + 51 + ] + }, + { + "name": "c:jungle", + "entries": [ + 1, + 27, + 48 + ] + }, + { + "name": "minecraft:has_structure/igloo", + "entries": [ + 46, + 44, + 45 + ] + }, + { + "name": "minecraft:has_structure/shipwreck_beached", + "entries": [ + 3, + 43 + ] + }, + { + "name": "c:wasteland", + "entries": [] + }, + { + "name": "minecraft:is_hill", + "entries": [ + 60, + 58, + 59 + ] + }, + { + "name": "c:forest", + "entries": [ + 20, + 19, + 4, + 35, + 7, + 24 + ] + }, + { + "name": "c:end_islands", + "entries": [] + }, + { + "name": "c:dead", + "entries": [] + }, + { + "name": "minecraft:allows_surface_slime_spawns", + "entries": [ + 52, + 30 + ] + }, + { + "name": "c:deep_ocean", + "entries": [ + 10, + 11, + 8, + 12 + ] + }, + { + "name": "c:swamp", + "entries": [ + 30, + 52 + ] + }, + { + "name": "minecraft:required_ocean_monument_surrounding", + "entries": [ + 10, + 8, + 12, + 11, + 21, + 34, + 5, + 28, + 56, + 39, + 23 + ] + }, + { + "name": "c:in_overworld", + "entries": [ + 32, + 10, + 21, + 8, + 5, + 12, + 34, + 11, + 28, + 56, + 50, + 52, + 30, + 45, + 44, + 43, + 59, + 24, + 60, + 46, + 58, + 53, + 38, + 31, + 3, + 20, + 37, + 19, + 4, + 7, + 41, + 40, + 27, + 0, + 13, + 62, + 26, + 49, + 23, + 39, + 25, + 36, + 51, + 35, + 48, + 1, + 18, + 61, + 22, + 14, + 29, + 9 + ] + }, + { + "name": "c:tree_jungle", + "entries": [ + 1, + 27, + 48 + ] + }, + { + "name": "minecraft:has_structure/ocean_monument", + "entries": [ + 10, + 8, + 12, + 11 + ] + }, + { + "name": "minecraft:spawns_white_rabbits", + "entries": [ + 44, + 25, + 21, + 46, + 23, + 43, + 22, + 26, + 45, + 24 + ] + }, + { + "name": "minecraft:has_structure/ruined_portal_jungle", + "entries": [ + 1, + 27, + 48 + ] + }, + { + "name": "c:climate_cold", + "entries": [ + 44, + 24, + 26, + 53, + 46, + 37, + 36, + 22, + 25 + ] + }, + { + "name": "minecraft:plays_underwater_music", + "entries": [ + 10, + 8, + 12, + 11, + 21, + 34, + 5, + 28, + 56, + 39, + 23 + ] + }, + { + "name": "minecraft:has_structure/desert_pyramid", + "entries": [ + 13 + ] + }, + { + "name": "minecraft:has_structure/ruined_portal_ocean", + "entries": [ + 10, + 8, + 12, + 11, + 21, + 34, + 5, + 28, + 56 + ] + }, + { + "name": "c:in_the_end", + "entries": [ + 54, + 16, + 17, + 42, + 15 + ] + }, + { + "name": "minecraft:is_deep_ocean", + "entries": [ + 10, + 8, + 12, + 11 + ] + }, + { + "name": "c:river", + "entries": [ + 39, + 23 + ] + }, + { + "name": "c:windswept", + "entries": [ + 60, + 59, + 58, + 61 + ] + }, + { + "name": "minecraft:is_nether", + "entries": [ + 33, + 47, + 6, + 57, + 2 + ] + }, + { + "name": "c:climate_hot", + "entries": [ + 1, + 27, + 48, + 40, + 41, + 61, + 13, + 62, + 18, + 0, + 49, + 32, + 33, + 47, + 6, + 57, + 2 + ] + }, + { + "name": "c:desert", + "entries": [ + 13 + ] + }, + { + "name": "minecraft:increased_fire_burnout", + "entries": [ + 1, + 32, + 30, + 45, + 22, + 26, + 52, + 27 + ] + }, + { + "name": "minecraft:has_structure/woodland_mansion", + "entries": [ + 7 + ] + }, + { + "name": "c:mountain_peak", + "entries": [ + 22, + 26, + 49 + ] + }, + { + "name": "minecraft:has_structure/ocean_ruin_warm", + "entries": [ + 28, + 56, + 11 + ] + }, + { + "name": "c:shallow_ocean", + "entries": [ + 34, + 28, + 21, + 5, + 56 + ] + }, + { + "name": "c:flower_forests", + "entries": [ + 19 + ] + }, + { + "name": "c:tree_deciduous", + "entries": [ + 20, + 58, + 19, + 4, + 7, + 35 + ] + }, + { + "name": "minecraft:has_structure/village_desert", + "entries": [ + 13 + ] + }, + { + "name": "minecraft:more_frequent_drowned_spawns", + "entries": [ + 39, + 23 + ] + }, + { + "name": "c:tree_savanna", + "entries": [ + 40, + 41, + 61 + ] + }, + { + "name": "minecraft:spawns_snow_foxes", + "entries": [ + 44, + 25, + 21, + 46, + 23, + 43, + 22, + 26, + 45, + 24 + ] + }, + { + "name": "minecraft:is_forest", + "entries": [ + 20, + 19, + 4, + 35, + 7, + 24 + ] + }, + { + "name": "minecraft:is_overworld", + "entries": [ + 32, + 10, + 21, + 8, + 5, + 12, + 34, + 11, + 28, + 56, + 50, + 52, + 30, + 45, + 44, + 43, + 59, + 24, + 60, + 46, + 58, + 53, + 38, + 31, + 3, + 20, + 37, + 19, + 4, + 7, + 41, + 40, + 27, + 0, + 13, + 62, + 26, + 49, + 23, + 39, + 25, + 36, + 51, + 35, + 48, + 1, + 18, + 61, + 22, + 14, + 29, + 9 + ] + }, + { + "name": "minecraft:has_structure/village_savanna", + "entries": [ + 40 + ] + }, + { + "name": "c:icy", + "entries": [ + 22, + 25 + ] + }, + { + "name": "c:stony_shores", + "entries": [ + 50 + ] + }, + { + "name": "minecraft:without_patrol_spawns", + "entries": [ + 32 + ] + }, + { + "name": "c:mountain_slope", + "entries": [ + 45 + ] + }, + { + "name": "minecraft:has_structure/village_taiga", + "entries": [ + 53 + ] + }, + { + "name": "minecraft:allows_tropical_fish_spawns_at_any_height", + "entries": [ + 29 + ] + }, + { + "name": "c:void", + "entries": [ + 55 + ] + }, + { + "name": "minecraft:polar_bears_spawn_on_alternate_blocks", + "entries": [ + 21, + 10 + ] + }, + { + "name": "minecraft:has_closer_water_fog", + "entries": [ + 52, + 30 + ] + }, + { + "name": "minecraft:has_structure/village_plains", + "entries": [ + 38, + 31 + ] + }, + { + "name": "c:ocean", + "entries": [ + 10, + 11, + 8, + 12, + 34, + 28, + 21, + 5, + 56 + ] + }, + { + "name": "minecraft:snow_golem_melts", + "entries": [ + 0, + 2, + 6, + 13, + 18, + 33, + 40, + 41, + 47, + 57, + 61, + 62 + ] + }, + { + "name": "minecraft:without_wandering_trader_spawns", + "entries": [ + 55 + ] + }, + { + "name": "minecraft:has_structure/ruined_portal_desert", + "entries": [ + 13 + ] + }, + { + "name": "minecraft:has_structure/ruined_portal_swamp", + "entries": [ + 52, + 30 + ] + }, + { + "name": "minecraft:has_structure/swamp_hut", + "entries": [ + 52 + ] + }, + { + "name": "minecraft:spawns_cold_variant_frogs", + "entries": [ + 44, + 25, + 22, + 26, + 45, + 21, + 10, + 24, + 9, + 23, + 46, + 43, + 54, + 16, + 17, + 42, + 15 + ] + }, + { + "name": "minecraft:has_structure/ruined_portal_nether", + "entries": [ + 33, + 47, + 6, + 57, + 2 + ] + }, + { + "name": "minecraft:is_end", + "entries": [ + 54, + 16, + 17, + 42, + 15 + ] + }, + { + "name": "c:climate_dry", + "entries": [ + 33, + 47, + 6, + 57, + 2, + 62, + 18, + 0, + 13, + 40, + 41, + 61 + ] + }, + { + "name": "minecraft:stronghold_biased_to", + "entries": [ + 38, + 51, + 44, + 25, + 13, + 20, + 19, + 4, + 7, + 35, + 36, + 37, + 53, + 46, + 40, + 41, + 60, + 59, + 58, + 61, + 27, + 48, + 1, + 0, + 18, + 62, + 31, + 24, + 45, + 22, + 26, + 49, + 32, + 14, + 29 + ] + }, + { + "name": "minecraft:without_zombie_sieges", + "entries": [ + 32 + ] + }, + { + "name": "minecraft:is_beach", + "entries": [ + 3, + 43 + ] + }, + { + "name": "minecraft:has_structure/ruined_portal_standard", + "entries": [ + 3, + 43, + 39, + 23, + 53, + 46, + 36, + 37, + 20, + 19, + 4, + 35, + 7, + 24, + 32, + 25, + 14, + 29, + 40, + 44, + 38, + 51 + ] + }, + { + "name": "minecraft:has_structure/pillager_outpost", + "entries": [ + 13, + 38, + 40, + 44, + 53, + 31, + 22, + 26, + 49, + 45, + 24 + ] + }, + { + "name": "c:birch_forest", + "entries": [ + 4, + 35 + ] + }, + { + "name": "c:mesa", + "entries": [ + 62, + 18, + 0 + ] + }, + { + "name": "minecraft:is_taiga", + "entries": [ + 53, + 46, + 36, + 37 + ] + }, + { + "name": "minecraft:spawns_warm_variant_frogs", + "entries": [ + 13, + 56, + 1, + 27, + 48, + 40, + 41, + 61, + 33, + 47, + 6, + 57, + 2, + 0, + 18, + 62, + 30 + ] + }, + { + "name": "minecraft:mineshaft_blocking", + "entries": [ + 9 + ] + }, + { + "name": "minecraft:is_mountain", + "entries": [ + 31, + 22, + 26, + 49, + 45 + ] + }, + { + "name": "c:plains", + "entries": [ + 51, + 38 + ] + }, + { + "name": "minecraft:has_structure/village_snowy", + "entries": [ + 44 + ] + }, + { + "name": "c:snowy", + "entries": [ + 43, + 44, + 45, + 46 + ] + }, + { + "name": "minecraft:is_badlands", + "entries": [ + 0, + 18, + 62 + ] + }, + { + "name": "c:climate_temperate", + "entries": [ + 20, + 51, + 52, + 50, + 7, + 58, + 4, + 35, + 31, + 38 + ] + }, + { + "name": "minecraft:spawns_gold_rabbits", + "entries": [ + 13 + ] + }, + { + "name": "c:aquatic", + "entries": [ + 10, + 11, + 8, + 12, + 34, + 28, + 21, + 5, + 56, + 39, + 23 + ] + }, + { + "name": "c:taiga", + "entries": [ + 53, + 46, + 36, + 37 + ] + }, + { + "name": "c:aquatic_icy", + "entries": [ + 23, + 10, + 21 + ] + }, + { + "name": "minecraft:has_structure/nether_fossil", + "entries": [ + 47 + ] + }, + { + "name": "c:vegetation_sparse", + "entries": [ + 40, + 41, + 61, + 13, + 29 + ] + }, + { + "name": "minecraft:has_structure/end_city", + "entries": [ + 16, + 17 + ] + }, + { + "name": "c:beach", + "entries": [ + 3, + 43, + 50 + ] + }, + { + "name": "c:extreme_hills", + "entries": [ + 59, + 60 + ] + }, + { + "name": "minecraft:reduce_water_ambient_spawns", + "entries": [ + 39, + 23 + ] + }, + { + "name": "minecraft:has_structure/bastion_remnant", + "entries": [ + 6, + 33, + 47, + 57 + ] + }, + { + "name": "minecraft:has_structure/ruined_portal_mountain", + "entries": [ + 0, + 18, + 62, + 60, + 58, + 59, + 41, + 61, + 50, + 31, + 22, + 26, + 49, + 45 + ] + } + ] + }, + { + "registry": "minecraft:point_of_interest_type", + "tags": [ + { + "name": "minecraft:bee_home", + "entries": [ + 15, + 16 + ] + }, + { + "name": "minecraft:acquirable_job_site", + "entries": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12 + ] + }, + { + "name": "minecraft:village", + "entries": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ] + } + ] + }, + { + "registry": "minecraft:game_event", + "tags": [ + { + "name": "minecraft:vibrations", + "entries": [ + 1, + 2, + 3, + 5, + 6, + 7, + 8, + 0, + 4, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 26, + 27, + 28, + 29, + 30, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 42, + 44, + 45, + 46, + 47, + 25 + ] + }, + { + "name": "minecraft:ignore_vibrations_sneaking", + "entries": [ + 28, + 40, + 45, + 46, + 31, + 30 + ] + }, + { + "name": "minecraft:warden_can_listen", + "entries": [ + 1, + 2, + 3, + 5, + 6, + 7, + 8, + 0, + 4, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 26, + 27, + 28, + 29, + 30, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 42, + 44, + 45, + 46, + 47, + 43, + 41 + ] + }, + { + "name": "minecraft:allay_can_listen", + "entries": [ + 35 + ] + }, + { + "name": "minecraft:shrieker_can_listen", + "entries": [ + 41 + ] + } + ] + }, + { + "registry": "minecraft:cat_variant", + "tags": [ + { + "name": "minecraft:default_spawns", + "entries": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + }, + { + "name": "minecraft:full_moon_spawns", + "entries": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ] + } + ] + }, + { + "registry": "minecraft:damage_type", + "tags": [ + { + "name": "minecraft:always_most_significant_fall", + "entries": [ + 28 + ] + }, + { + "name": "minecraft:bypasses_effects", + "entries": [ + 33 + ] + }, + { + "name": "minecraft:is_fall", + "entries": [ + 8, + 32 + ] + }, + { + "name": "minecraft:bypasses_armor", + "entries": [ + 27, + 19, + 3, + 5, + 14, + 16, + 40, + 4, + 33, + 8, + 15, + 32, + 23, + 20, + 28, + 31 + ] + }, + { + "name": "minecraft:ignites_armor_stands", + "entries": [ + 18 + ] + }, + { + "name": "minecraft:witch_resistant_to", + "entries": [ + 23, + 20, + 31, + 36 + ] + }, + { + "name": "minecraft:bypasses_invulnerability", + "entries": [ + 28 + ] + }, + { + "name": "minecraft:burns_armor_stands", + "entries": [ + 27 + ] + }, + { + "name": "minecraft:avoids_guardian_thorns", + "entries": [ + 23, + 36, + 13, + 7, + 30, + 1 + ] + }, + { + "name": "minecraft:bypasses_resistance", + "entries": [ + 28 + ] + }, + { + "name": "minecraft:no_anger", + "entries": [ + 25 + ] + }, + { + "name": "minecraft:is_drowning", + "entries": [ + 5 + ] + }, + { + "name": "minecraft:always_triggers_silverfish", + "entries": [ + 23 + ] + }, + { + "name": "minecraft:always_hurts_ender_dragons", + "entries": [ + 13, + 7, + 30, + 1 + ] + }, + { + "name": "minecraft:is_fire", + "entries": [ + 18, + 27, + 21, + 17, + 39, + 12 + ] + }, + { + "name": "minecraft:damages_helmet", + "entries": [ + 9, + 10, + 11 + ] + }, + { + "name": "minecraft:bypasses_shield", + "entries": [ + 27, + 19, + 3, + 5, + 14, + 16, + 40, + 4, + 33, + 8, + 15, + 32, + 23, + 20, + 28, + 31, + 9, + 11 + ] + }, + { + "name": "minecraft:wither_immune_to", + "entries": [ + 5 + ] + }, + { + "name": "minecraft:no_impact", + "entries": [ + 5 + ] + }, + { + "name": "minecraft:is_explosion", + "entries": [ + 13, + 7, + 30, + 1 + ] + }, + { + "name": "minecraft:is_lightning", + "entries": [ + 22 + ] + }, + { + "name": "minecraft:is_freezing", + "entries": [ + 15 + ] + }, + { + "name": "minecraft:is_projectile", + "entries": [ + 0, + 38, + 26, + 39, + 12, + 41, + 37 + ] + }, + { + "name": "minecraft:bypasses_enchantments", + "entries": [ + 31 + ] + } + ] + }, + { + "registry": "minecraft:enchantment", + "tags": [ + { + "name": "c:weapon_damage_enhancement", + "entries": [ + 15, + 31, + 14, + 24, + 13 + ] + }, + { + "name": "c:entity_movement_enhancement", + "entries": [ + 12, + 8, + 11 + ] + }, + { + "name": "c:looting", + "entries": [ + 18 + ] + }, + { + "name": "c:entity_defense_enhancement", + "entries": [ + 2, + 0, + 3, + 4, + 1, + 5 + ] + }, + { + "name": "c:fortune", + "entries": [ + 23 + ] + } + ] + }, + { + "registry": "minecraft:block", + "tags": [ + { + "name": "minecraft:soul_fire_base_blocks", + "entries": [ + 256, + 257 + ] + }, + { + "name": "minecraft:campfires", + "entries": [ + 782, + 783 + ] + }, + { + "name": "minecraft:infiniburn_nether", + "entries": [ + 255, + 604 + ] + }, + { + "name": "minecraft:wooden_slabs", + "entries": [ + 538, + 539, + 540, + 541, + 542, + 544, + 808, + 809, + 545 + ] + }, + { + "name": "minecraft:snaps_goat_horn", + "entries": [ + 49, + 47, + 45, + 48, + 46, + 51, + 52, + 1, + 495, + 40, + 42, + 918, + 341 + ] + }, + { + "name": "minecraft:coal_ores", + "entries": [ + 42, + 43 + ] + }, + { + "name": "minecraft:occludes_vibration_signals", + "entries": [ + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144 + ] + }, + { + "name": "minecraft:replaceable_plants", + "entries": [ + 122, + 123, + 124, + 316, + 317, + 496, + 497, + 498, + 499, + 500, + 501, + 962 + ] + }, + { + "name": "minecraft:small_flowers", + "entries": [ + 146, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 159, + 158 + ] + }, + { + "name": "minecraft:azalea_root_replaceable", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 493, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 36, + 250, + 37, + 34, + 248, + 908 + ] + }, + { + "name": "minecraft:wooden_trapdoors", + "entries": [ + 288, + 286, + 290, + 287, + 284, + 285, + 814, + 815, + 291 + ] + }, + { + "name": "minecraft:invalid_spawn_inside", + "entries": [ + 334, + 600 + ] + }, + { + "name": "minecraft:wolves_spawnable_on", + "entries": [ + 8, + 246, + 248 + ] + }, + { + "name": "minecraft:foxes_spawnable_on", + "entries": [ + 8, + 246, + 248, + 11, + 10 + ] + }, + { + "name": "minecraft:wool", + "entries": [ + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144 + ] + }, + { + "name": "minecraft:stairs", + "entries": [ + 175, + 347, + 348, + 349, + 456, + 458, + 818, + 819, + 459, + 197, + 340, + 326, + 320, + 319, + 595, + 421, + 537, + 470, + 469, + 471, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 846, + 854, + 857, + 967, + 971, + 975, + 979, + 924, + 925, + 926, + 927, + 941, + 942, + 943, + 940, + 321 + ] + }, + { + "name": "minecraft:logs", + "entries": [ + 51, + 71, + 61, + 79, + 45, + 65, + 62, + 73, + 49, + 69, + 59, + 77, + 47, + 67, + 57, + 75, + 48, + 68, + 58, + 76, + 46, + 66, + 56, + 74, + 52, + 72, + 63, + 80, + 794, + 795, + 796, + 797, + 785, + 786, + 787, + 788 + ] + }, + { + "name": "minecraft:beehives", + "entries": [ + 832, + 833 + ] + }, + { + "name": "minecraft:all_signs", + "entries": [ + 185, + 186, + 187, + 188, + 190, + 191, + 824, + 825, + 192, + 198, + 199, + 200, + 201, + 203, + 204, + 826, + 827, + 205 + ] + }, + { + "name": "minecraft:ice", + "entries": [ + 247, + 495, + 720, + 603 + ] + }, + { + "name": "minecraft:azalea_grows_on", + "entries": [ + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 34, + 36, + 493, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 248, + 908 + ] + }, + { + "name": "minecraft:wool_carpets", + "entries": [ + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492 + ] + }, + { + "name": "minecraft:crops", + "entries": [ + 598, + 382, + 383, + 182, + 315, + 314 + ] + }, + { + "name": "minecraft:dragon_immune", + "entries": [ + 463, + 31, + 334, + 335, + 600, + 350, + 601, + 602, + 828, + 829, + 145, + 169, + 838, + 336, + 308, + 839, + 996 + ] + }, + { + "name": "minecraft:mangrove_roots_can_grow_through", + "entries": [ + 964, + 54, + 53, + 956, + 316, + 30, + 246 + ] + }, + { + "name": "minecraft:features_cannot_replace", + "entries": [ + 31, + 174, + 176, + 335, + 996 + ] + }, + { + "name": "minecraft:valid_spawn", + "entries": [ + 8, + 11 + ] + }, + { + "name": "minecraft:mushroom_grow_block", + "entries": [ + 322, + 11, + 798, + 789 + ] + }, + { + "name": "minecraft:wooden_doors", + "entries": [ + 194, + 582, + 583, + 584, + 585, + 587, + 822, + 823, + 588 + ] + }, + { + "name": "minecraft:crystal_sound_blocks", + "entries": [ + 899, + 900 + ] + }, + { + "name": "minecraft:warped_stems", + "entries": [ + 785, + 786, + 787, + 788 + ] + }, + { + "name": "minecraft:standing_signs", + "entries": [ + 185, + 186, + 187, + 188, + 190, + 191, + 824, + 825, + 192 + ] + }, + { + "name": "minecraft:infiniburn_end", + "entries": [ + 255, + 604, + 31 + ] + }, + { + "name": "minecraft:emerald_ores", + "entries": [ + 341, + 342 + ] + }, + { + "name": "minecraft:crimson_stems", + "entries": [ + 794, + 795, + 796, + 797 + ] + }, + { + "name": "minecraft:needs_stone_tool", + "entries": [ + 163, + 987, + 40, + 41, + 96, + 94, + 95, + 917, + 988, + 918, + 919, + 931, + 927, + 923, + 915, + 929, + 925, + 921, + 914, + 928, + 924, + 920, + 916, + 930, + 926, + 922, + 932, + 947, + 943, + 939, + 933, + 945, + 941, + 937, + 934, + 946, + 942, + 938, + 935, + 944, + 940, + 936, + 948 + ] + }, + { + "name": "minecraft:lava_pool_stone_cannot_replace", + "entries": [ + 31, + 174, + 176, + 335, + 996, + 84, + 81, + 82, + 87, + 85, + 83, + 89, + 90, + 88, + 51, + 71, + 61, + 79, + 45, + 65, + 62, + 73, + 49, + 69, + 59, + 77, + 47, + 67, + 57, + 75, + 48, + 68, + 58, + 76, + 46, + 66, + 56, + 74, + 52, + 72, + 63, + 80, + 794, + 795, + 796, + 797, + 785, + 786, + 787, + 788 + ] + }, + { + "name": "minecraft:inside_step_sound_blocks", + "entries": [ + 246, + 908, + 911, + 323, + 793, + 792, + 805, + 317, + 904, + 956, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492 + ] + }, + { + "name": "minecraft:prevent_mob_spawning_inside", + "entries": [ + 196, + 118, + 119, + 422 + ] + }, + { + "name": "minecraft:terracotta", + "entries": [ + 493, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439 + ] + }, + { + "name": "minecraft:climbable", + "entries": [ + 195, + 316, + 768, + 801, + 802, + 803, + 804, + 951, + 952 + ] + }, + { + "name": "minecraft:wart_blocks", + "entries": [ + 605, + 791 + ] + }, + { + "name": "minecraft:parrots_spawnable_on", + "entries": [ + 8, + 0, + 84, + 81, + 82, + 87, + 85, + 83, + 89, + 90, + 88, + 51, + 71, + 61, + 79, + 45, + 65, + 62, + 73, + 49, + 69, + 59, + 77, + 47, + 67, + 57, + 75, + 48, + 68, + 58, + 76, + 46, + 66, + 56, + 74, + 52, + 72, + 63, + 80, + 794, + 795, + 796, + 797, + 785, + 786, + 787, + 788 + ] + }, + { + "name": "minecraft:dark_oak_logs", + "entries": [ + 51, + 71, + 61, + 79 + ] + }, + { + "name": "minecraft:frog_prefer_jump_to", + "entries": [ + 323, + 959 + ] + }, + { + "name": "minecraft:coral_plants", + "entries": [ + 694, + 695, + 696, + 697, + 698 + ] + }, + { + "name": "minecraft:goats_spawnable_on", + "entries": [ + 8, + 1, + 246, + 248, + 495, + 37 + ] + }, + { + "name": "c:chests", + "entries": [ + 176, + 343, + 410 + ] + }, + { + "name": "minecraft:sculk_replaceable_world_gen", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 493, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 798, + 789, + 255, + 258, + 845, + 34, + 36, + 37, + 256, + 257, + 906, + 986, + 250, + 950, + 336, + 534, + 98, + 978, + 974, + 966, + 983, + 984, + 970 + ] + }, + { + "name": "minecraft:beacon_base_blocks", + "entries": [ + 836, + 346, + 180, + 162, + 163 + ] + }, + { + "name": "c:quartz_ores", + "entries": [ + 416 + ] + }, + { + "name": "minecraft:frogs_spawnable_on", + "entries": [ + 8, + 964, + 53, + 54 + ] + }, + { + "name": "minecraft:shulker_boxes", + "entries": [ + 610, + 626, + 622, + 623, + 620, + 618, + 624, + 614, + 619, + 616, + 613, + 612, + 617, + 621, + 625, + 611, + 615 + ] + }, + { + "name": "minecraft:anvil", + "entries": [ + 407, + 408, + 409 + ] + }, + { + "name": "minecraft:birch_logs", + "entries": [ + 47, + 67, + 57, + 75 + ] + }, + { + "name": "minecraft:moss_replaceable", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965, + 952, + 951, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54 + ] + }, + { + "name": "minecraft:lapis_ores", + "entries": [ + 94, + 95 + ] + }, + { + "name": "minecraft:wall_corals", + "entries": [ + 714, + 715, + 716, + 717, + 718 + ] + }, + { + "name": "minecraft:convertable_to_mud", + "entries": [ + 9, + 10, + 963 + ] + }, + { + "name": "minecraft:lush_ground_replaceable", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965, + 952, + 951, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 250, + 37, + 34 + ] + }, + { + "name": "c:glass_panes", + "entries": [ + 310, + 447, + 455, + 441, + 451, + 452, + 449, + 453, + 443, + 448, + 445, + 442, + 446, + 450, + 454, + 440, + 444 + ] + }, + { + "name": "minecraft:fences", + "entries": [ + 253, + 577, + 579, + 574, + 575, + 576, + 812, + 813, + 580, + 325 + ] + }, + { + "name": "minecraft:saplings", + "entries": [ + 23, + 24, + 25, + 26, + 27, + 29, + 954, + 955, + 30 + ] + }, + { + "name": "minecraft:mineable/pickaxe", + "entries": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 12, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 162, + 163, + 164, + 168, + 169, + 174, + 178, + 179, + 180, + 184, + 197, + 230, + 231, + 241, + 242, + 255, + 258, + 259, + 293, + 294, + 295, + 296, + 308, + 309, + 319, + 320, + 324, + 325, + 326, + 328, + 329, + 336, + 340, + 341, + 342, + 343, + 346, + 411, + 412, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 493, + 494, + 534, + 535, + 536, + 537, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 593, + 594, + 595, + 596, + 604, + 606, + 607, + 609, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 699, + 700, + 701, + 702, + 703, + 709, + 710, + 711, + 712, + 713, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 771, + 772, + 775, + 778, + 779, + 780, + 781, + 789, + 798, + 836, + 837, + 838, + 839, + 844, + 845, + 846, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 856, + 857, + 858, + 859, + 862, + 863, + 864, + 905, + 906, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 965, + 966, + 967, + 968, + 970, + 971, + 972, + 974, + 975, + 976, + 978, + 979, + 980, + 982, + 983, + 984, + 986, + 987, + 988, + 989, + 247, + 495, + 720, + 245, + 127, + 120, + 128, + 901, + 904, + 903, + 902, + 899, + 900, + 300, + 304, + 303, + 985, + 299, + 302, + 301, + 352, + 353, + 755, + 756, + 757, + 758, + 759, + 760, + 762, + 763, + 764, + 765, + 766, + 767, + 847, + 855, + 861, + 969, + 973, + 977, + 981, + 761, + 610, + 626, + 622, + 623, + 620, + 618, + 624, + 614, + 619, + 616, + 613, + 612, + 617, + 621, + 625, + 611, + 615, + 407, + 408, + 409, + 330, + 331, + 332, + 333, + 196, + 118, + 119, + 422, + 721, + 298, + 321, + 556, + 297 + ] + }, + { + "name": "minecraft:beds", + "entries": [ + 116, + 117, + 113, + 114, + 111, + 109, + 115, + 105, + 110, + 107, + 104, + 103, + 108, + 112, + 102, + 106 + ] + }, + { + "name": "minecraft:iron_ores", + "entries": [ + 40, + 41 + ] + }, + { + "name": "minecraft:oak_logs", + "entries": [ + 45, + 65, + 62, + 73 + ] + }, + { + "name": "minecraft:unstable_bottom_center", + "entries": [ + 569, + 567, + 571, + 568, + 318, + 566, + 816, + 817, + 572 + ] + }, + { + "name": "minecraft:doors", + "entries": [ + 194, + 582, + 583, + 584, + 585, + 587, + 822, + 823, + 588, + 231 + ] + }, + { + "name": "minecraft:enderman_holdable", + "entries": [ + 146, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 159, + 158, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 34, + 36, + 37, + 160, + 161, + 165, + 249, + 250, + 254, + 264, + 311, + 799, + 798, + 805, + 790, + 789, + 792 + ] + }, + { + "name": "minecraft:banners", + "entries": [ + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533 + ] + }, + { + "name": "minecraft:infiniburn_overworld", + "entries": [ + 255, + 604 + ] + }, + { + "name": "minecraft:smelts_to_glass", + "entries": [ + 34, + 36 + ] + }, + { + "name": "minecraft:flower_pots", + "entries": [ + 354, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 365, + 356, + 357, + 358, + 359, + 360, + 362, + 378, + 379, + 380, + 364, + 381, + 375, + 376, + 377, + 724, + 840, + 841, + 842, + 843, + 990, + 991, + 363 + ] + }, + { + "name": "minecraft:wooden_fences", + "entries": [ + 253, + 577, + 579, + 574, + 575, + 576, + 812, + 813, + 580 + ] + }, + { + "name": "minecraft:piglin_repellents", + "entries": [ + 173, + 260, + 781, + 261, + 783 + ] + }, + { + "name": "minecraft:mooshrooms_spawnable_on", + "entries": [ + 322 + ] + }, + { + "name": "minecraft:wall_post_override", + "entries": [ + 170, + 260, + 243, + 345, + 185, + 186, + 187, + 188, + 190, + 191, + 824, + 825, + 192, + 198, + 199, + 200, + 201, + 203, + 204, + 826, + 827, + 205, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 411, + 412, + 232, + 233, + 234, + 235, + 236, + 238, + 810, + 811, + 239, + 230, + 859 + ] + }, + { + "name": "c:bookshelves", + "entries": [ + 166 + ] + }, + { + "name": "minecraft:portals", + "entries": [ + 263, + 334, + 600 + ] + }, + { + "name": "minecraft:bamboo_plantable_on", + "entries": [ + 34, + 36, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 723, + 722, + 37 + ] + }, + { + "name": "minecraft:polar_bears_spawnable_on_alternate", + "entries": [ + 247 + ] + }, + { + "name": "minecraft:cauldrons", + "entries": [ + 330, + 331, + 332, + 333 + ] + }, + { + "name": "minecraft:big_dripleaf_placeable", + "entries": [ + 250, + 958, + 9, + 8, + 11, + 10, + 322, + 963, + 964, + 54, + 183 + ] + }, + { + "name": "minecraft:pressure_plates", + "entries": [ + 411, + 412, + 232, + 233, + 234, + 235, + 236, + 238, + 810, + 811, + 239, + 230, + 859 + ] + }, + { + "name": "minecraft:dampens_vibrations", + "entries": [ + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492 + ] + }, + { + "name": "minecraft:mangrove_logs", + "entries": [ + 52, + 72, + 63, + 80 + ] + }, + { + "name": "minecraft:overworld_carver_replaceables", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 34, + 36, + 493, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 40, + 41, + 918, + 919, + 32, + 37, + 98, + 534, + 906, + 246, + 495, + 987, + 988 + ] + }, + { + "name": "minecraft:snow_layer_can_survive_on", + "entries": [ + 834, + 256, + 964 + ] + }, + { + "name": "minecraft:jungle_logs", + "entries": [ + 48, + 68, + 58, + 76 + ] + }, + { + "name": "minecraft:dead_bush_may_place_on", + "entries": [ + 34, + 36, + 493, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54 + ] + }, + { + "name": "minecraft:snow_layer_cannot_survive_on", + "entries": [ + 247, + 495, + 463 + ] + }, + { + "name": "minecraft:sculk_replaceable", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 493, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 798, + 789, + 255, + 258, + 845, + 34, + 36, + 37, + 256, + 257, + 906, + 986, + 250, + 950, + 336, + 534, + 98 + ] + }, + { + "name": "minecraft:wooden_stairs", + "entries": [ + 175, + 347, + 348, + 349, + 456, + 458, + 818, + 819, + 459 + ] + }, + { + "name": "minecraft:signs", + "entries": [ + 185, + 186, + 187, + 188, + 190, + 191, + 824, + 825, + 192, + 198, + 199, + 200, + 201, + 203, + 204, + 826, + 827, + 205 + ] + }, + { + "name": "minecraft:spruce_logs", + "entries": [ + 46, + 66, + 56, + 74 + ] + }, + { + "name": "minecraft:ancient_city_replaceable", + "entries": [ + 965, + 978, + 974, + 980, + 976, + 979, + 977, + 981, + 966, + 983, + 984, + 136 + ] + }, + { + "name": "minecraft:mangrove_logs_can_grow_through", + "entries": [ + 964, + 54, + 53, + 88, + 52, + 30, + 956, + 316 + ] + }, + { + "name": "minecraft:base_stone_overworld", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965 + ] + }, + { + "name": "minecraft:wooden_buttons", + "entries": [ + 384, + 385, + 386, + 387, + 388, + 390, + 820, + 821, + 391 + ] + }, + { + "name": "minecraft:axolotls_spawnable_on", + "entries": [ + 250 + ] + }, + { + "name": "minecraft:wither_summon_base_blocks", + "entries": [ + 256, + 257 + ] + }, + { + "name": "minecraft:dripstone_replaceable_blocks", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965 + ] + }, + { + "name": "minecraft:hoglin_repellents", + "entries": [ + 790, + 841, + 263, + 839 + ] + }, + { + "name": "minecraft:stone_bricks", + "entries": [ + 293, + 294, + 295, + 296 + ] + }, + { + "name": "minecraft:fire", + "entries": [ + 172, + 173 + ] + }, + { + "name": "minecraft:mineable/axe", + "entries": [ + 101, + 313, + 312, + 954, + 723, + 770, + 832, + 833, + 598, + 960, + 959, + 166, + 305, + 160, + 782, + 382, + 773, + 264, + 952, + 951, + 176, + 592, + 591, + 339, + 830, + 181, + 799, + 414, + 124, + 123, + 774, + 317, + 122, + 962, + 265, + 252, + 195, + 501, + 776, + 323, + 769, + 315, + 311, + 307, + 327, + 383, + 314, + 254, + 306, + 161, + 768, + 961, + 777, + 783, + 953, + 251, + 784, + 500, + 410, + 804, + 803, + 316, + 790, + 802, + 801, + 182, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 569, + 567, + 571, + 568, + 318, + 566, + 816, + 817, + 572, + 51, + 71, + 61, + 79, + 45, + 65, + 62, + 73, + 49, + 69, + 59, + 77, + 47, + 67, + 57, + 75, + 48, + 68, + 58, + 76, + 46, + 66, + 56, + 74, + 52, + 72, + 63, + 80, + 794, + 795, + 796, + 797, + 785, + 786, + 787, + 788, + 13, + 14, + 15, + 16, + 17, + 19, + 806, + 807, + 20, + 23, + 24, + 25, + 26, + 27, + 29, + 955, + 30, + 185, + 186, + 187, + 188, + 190, + 191, + 824, + 825, + 192, + 198, + 199, + 200, + 201, + 203, + 204, + 826, + 827, + 205, + 384, + 385, + 386, + 387, + 388, + 390, + 820, + 821, + 391, + 194, + 582, + 583, + 584, + 585, + 587, + 822, + 823, + 588, + 253, + 577, + 579, + 574, + 575, + 576, + 812, + 813, + 580, + 232, + 233, + 234, + 235, + 236, + 238, + 810, + 811, + 239, + 538, + 539, + 540, + 541, + 542, + 544, + 808, + 809, + 545, + 175, + 347, + 348, + 349, + 456, + 458, + 818, + 819, + 459, + 288, + 286, + 290, + 287, + 284, + 285, + 814, + 815, + 291, + 53 + ] + }, + { + "name": "minecraft:needs_diamond_tool", + "entries": [ + 169, + 838, + 836, + 839, + 837 + ] + }, + { + "name": "minecraft:base_stone_nether", + "entries": [ + 255, + 258, + 845 + ] + }, + { + "name": "minecraft:wall_signs", + "entries": [ + 198, + 199, + 200, + 201, + 203, + 204, + 826, + 827, + 205 + ] + }, + { + "name": "minecraft:slabs", + "entries": [ + 538, + 539, + 540, + 541, + 542, + 544, + 808, + 809, + 545, + 548, + 549, + 555, + 550, + 561, + 558, + 559, + 554, + 553, + 557, + 552, + 472, + 473, + 474, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 551, + 560, + 848, + 853, + 858, + 968, + 972, + 976, + 980, + 945, + 946, + 947, + 928, + 929, + 930, + 931, + 944, + 556 + ] + }, + { + "name": "minecraft:animals_spawnable_on", + "entries": [ + 8 + ] + }, + { + "name": "minecraft:guarded_by_piglins", + "entries": [ + 162, + 770, + 176, + 343, + 856, + 410, + 989, + 610, + 626, + 622, + 623, + 620, + 618, + 624, + 614, + 619, + 616, + 613, + 612, + 617, + 621, + 625, + 611, + 615, + 38, + 44, + 39 + ] + }, + { + "name": "minecraft:mineable/shovel", + "entries": [ + 250, + 9, + 10, + 11, + 183, + 8, + 37, + 322, + 34, + 36, + 248, + 246, + 256, + 599, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 257, + 963, + 54, + 964 + ] + }, + { + "name": "minecraft:nether_carver_replaceables", + "entries": [ + 1, + 2, + 4, + 6, + 905, + 965, + 255, + 258, + 845, + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54, + 798, + 789, + 605, + 791, + 256, + 257 + ] + }, + { + "name": "minecraft:stone_ore_replaceables", + "entries": [ + 1, + 2, + 4, + 6 + ] + }, + { + "name": "minecraft:redstone_ores", + "entries": [ + 241, + 242 + ] + }, + { + "name": "minecraft:trapdoors", + "entries": [ + 288, + 286, + 290, + 287, + 284, + 285, + 814, + 815, + 291, + 465 + ] + }, + { + "name": "minecraft:fall_damage_resetting", + "entries": [ + 195, + 316, + 768, + 801, + 802, + 803, + 804, + 951, + 952, + 784, + 121 + ] + }, + { + "name": "minecraft:flowers", + "entries": [ + 146, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 159, + 158, + 496, + 497, + 499, + 498, + 90, + 955, + 30 + ] + }, + { + "name": "minecraft:buttons", + "entries": [ + 384, + 385, + 386, + 387, + 388, + 390, + 820, + 821, + 391, + 245, + 860 + ] + }, + { + "name": "minecraft:corals", + "entries": [ + 694, + 695, + 696, + 697, + 698, + 704, + 705, + 706, + 707, + 708 + ] + }, + { + "name": "minecraft:rabbits_spawnable_on", + "entries": [ + 8, + 246, + 248, + 34 + ] + }, + { + "name": "minecraft:planks", + "entries": [ + 13, + 14, + 15, + 16, + 17, + 19, + 806, + 807, + 20 + ] + }, + { + "name": "minecraft:soul_speed_blocks", + "entries": [ + 256, + 257 + ] + }, + { + "name": "minecraft:rails", + "entries": [ + 196, + 118, + 119, + 422 + ] + }, + { + "name": "minecraft:diamond_ores", + "entries": [ + 178, + 179 + ] + }, + { + "name": "minecraft:geode_invalid_blocks", + "entries": [ + 31, + 32, + 33, + 247, + 495, + 720 + ] + }, + { + "name": "minecraft:overworld_natural_logs", + "entries": [ + 49, + 47, + 45, + 48, + 46, + 51, + 52 + ] + }, + { + "name": "minecraft:deepslate_ore_replaceables", + "entries": [ + 965, + 905 + ] + }, + { + "name": "c:shulker_boxes", + "entries": [ + 610, + 622, + 623, + 620, + 618, + 624, + 614, + 619, + 616, + 613, + 612, + 617, + 621, + 625, + 611, + 615, + 626 + ] + }, + { + "name": "minecraft:leaves", + "entries": [ + 84, + 81, + 82, + 87, + 85, + 83, + 89, + 90, + 88 + ] + }, + { + "name": "minecraft:walls", + "entries": [ + 352, + 353, + 755, + 756, + 757, + 758, + 759, + 760, + 762, + 763, + 764, + 765, + 766, + 767, + 847, + 855, + 861, + 969, + 973, + 977, + 981, + 761 + ] + }, + { + "name": "minecraft:cave_vines", + "entries": [ + 952, + 951 + ] + }, + { + "name": "c:glass_blocks", + "entries": [ + 93, + 275, + 283, + 269, + 279, + 280, + 277, + 281, + 271, + 276, + 273, + 270, + 274, + 278, + 282, + 907, + 268, + 272 + ] + }, + { + "name": "minecraft:coral_blocks", + "entries": [ + 684, + 685, + 686, + 687, + 688 + ] + }, + { + "name": "minecraft:strider_warm_blocks", + "entries": [ + 33 + ] + }, + { + "name": "minecraft:fence_gates", + "entries": [ + 569, + 567, + 571, + 568, + 318, + 566, + 816, + 817, + 572 + ] + }, + { + "name": "minecraft:bee_growables", + "entries": [ + 598, + 382, + 383, + 182, + 315, + 314, + 784, + 951, + 952 + ] + }, + { + "name": "minecraft:wither_immune", + "entries": [ + 463, + 31, + 334, + 335, + 600, + 350, + 601, + 602, + 828, + 829, + 145, + 464, + 996 + ] + }, + { + "name": "minecraft:wooden_pressure_plates", + "entries": [ + 232, + 233, + 234, + 235, + 236, + 238, + 810, + 811, + 239 + ] + }, + { + "name": "minecraft:acacia_logs", + "entries": [ + 49, + 69, + 59, + 77 + ] + }, + { + "name": "minecraft:candles", + "entries": [ + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881 + ] + }, + { + "name": "minecraft:tall_flowers", + "entries": [ + 496, + 497, + 499, + 498 + ] + }, + { + "name": "minecraft:dragon_transparent", + "entries": [ + 464, + 172, + 173 + ] + }, + { + "name": "minecraft:underwater_bonemeals", + "entries": [ + 125, + 694, + 695, + 696, + 697, + 698, + 704, + 705, + 706, + 707, + 708, + 714, + 715, + 716, + 717, + 718 + ] + }, + { + "name": "minecraft:stone_pressure_plates", + "entries": [ + 230, + 859 + ] + }, + { + "name": "minecraft:impermeable", + "entries": [ + 93, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 907 + ] + }, + { + "name": "minecraft:copper_ores", + "entries": [ + 918, + 919 + ] + }, + { + "name": "minecraft:snow", + "entries": [ + 246, + 248, + 908 + ] + }, + { + "name": "minecraft:nylium", + "entries": [ + 798, + 789 + ] + }, + { + "name": "minecraft:sand", + "entries": [ + 34, + 36 + ] + }, + { + "name": "minecraft:gold_ores", + "entries": [ + 38, + 44, + 39 + ] + }, + { + "name": "minecraft:small_dripleaf_placeable", + "entries": [ + 250, + 958 + ] + }, + { + "name": "minecraft:completes_find_tree_tutorial", + "entries": [ + 51, + 71, + 61, + 79, + 45, + 65, + 62, + 73, + 49, + 69, + 59, + 77, + 47, + 67, + 57, + 75, + 48, + 68, + 58, + 76, + 46, + 66, + 56, + 74, + 52, + 72, + 63, + 80, + 794, + 795, + 796, + 797, + 785, + 786, + 787, + 788, + 84, + 81, + 82, + 87, + 85, + 83, + 89, + 90, + 88, + 605, + 791 + ] + }, + { + "name": "minecraft:logs_that_burn", + "entries": [ + 51, + 71, + 61, + 79, + 45, + 65, + 62, + 73, + 49, + 69, + 59, + 77, + 47, + 67, + 57, + 75, + 48, + 68, + 58, + 76, + 46, + 66, + 56, + 74, + 52, + 72, + 63, + 80 + ] + }, + { + "name": "minecraft:mineable/hoe", + "entries": [ + 605, + 791, + 476, + 677, + 831, + 800, + 91, + 92, + 84, + 81, + 82, + 87, + 85, + 83, + 89, + 90, + 88, + 909, + 958, + 956, + 910, + 912, + 911, + 913 + ] + }, + { + "name": "minecraft:dirt", + "entries": [ + 9, + 8, + 11, + 10, + 322, + 963, + 958, + 964, + 54 + ] + }, + { + "name": "c:ores", + "entries": [ + 241, + 242, + 918, + 919, + 38, + 44, + 39, + 40, + 41, + 42, + 43, + 341, + 342, + 94, + 95, + 178, + 179, + 416 + ] + }, + { + "name": "minecraft:candle_cakes", + "entries": [ + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898 + ] + }, + { + "name": "minecraft:needs_iron_tool", + "entries": [ + 180, + 178, + 179, + 341, + 342, + 346, + 162, + 989, + 38, + 39, + 241, + 242 + ] + } + ] + }, + { + "registry": "minecraft:item", + "tags": [ + { + "name": "minecraft:soul_fire_base_blocks", + "entries": [ + 302, + 303 + ] + }, + { + "name": "c:foods", + "entries": [ + 755, + 805, + 811, + 837, + 838, + 840, + 841, + 891, + 892, + 893, + 894, + 895, + 896, + 936, + 939, + 940, + 943, + 944, + 945, + 946, + 947, + 955, + 1047, + 1048, + 1049, + 1050, + 1052, + 1061, + 1068, + 1069, + 1070, + 1081, + 1082, + 1100, + 1103, + 1105, + 1138, + 1159, + 1160, + 1167 + ] + }, + { + "name": "minecraft:beacon_payment_items", + "entries": [ + 771, + 761, + 760, + 770, + 766 + ] + }, + { + "name": "c:lava_buckets", + "entries": [ + 866 + ] + }, + { + "name": "c:raw_iron_ores", + "entries": [ + 765 + ] + }, + { + "name": "minecraft:wooden_slabs", + "entries": [ + 228, + 229, + 230, + 231, + 232, + 234, + 238, + 239, + 235 + ] + }, + { + "name": "c:magenta_dyes", + "entries": [ + 902 + ] + }, + { + "name": "minecraft:coal_ores", + "entries": [ + 48, + 49 + ] + }, + { + "name": "minecraft:small_flowers", + "entries": [ + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207 + ] + }, + { + "name": "c:axes", + "entries": [ + 796, + 786, + 776, + 781, + 791, + 801 + ] + }, + { + "name": "c:raw_iron_blocks", + "entries": [ + 68 + ] + }, + { + "name": "minecraft:wooden_trapdoors", + "entries": [ + 701, + 699, + 703, + 700, + 697, + 698, + 706, + 707, + 704 + ] + }, + { + "name": "c:pink_dyes", + "entries": [ + 906 + ] + }, + { + "name": "c:shovels", + "entries": [ + 794, + 784, + 774, + 779, + 789, + 799 + ] + }, + { + "name": "c:black_dyes", + "entries": [ + 915 + ] + }, + { + "name": "c:lapis", + "entries": [ + 762 + ] + }, + { + "name": "c:netherite_ingots", + "entries": [ + 771 + ] + }, + { + "name": "minecraft:axolotl_tempt_items", + "entries": [ + 874 + ] + }, + { + "name": "c:spears", + "entries": [ + 1133 + ] + }, + { + "name": "minecraft:wool", + "entries": [ + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194 + ] + }, + { + "name": "minecraft:stairs", + "entries": [ + 359, + 360, + 361, + 362, + 363, + 365, + 369, + 370, + 366, + 280, + 356, + 346, + 338, + 337, + 273, + 402, + 489, + 483, + 482, + 484, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 1173, + 1181, + 1177, + 610, + 611, + 613, + 612, + 88, + 87, + 86, + 85, + 103, + 102, + 101, + 104, + 339 + ] + }, + { + "name": "c:purple_dyes", + "entries": [ + 910 + ] + }, + { + "name": "minecraft:logs", + "entries": [ + 115, + 149, + 128, + 138, + 109, + 143, + 122, + 132, + 113, + 147, + 126, + 136, + 111, + 145, + 124, + 134, + 112, + 146, + 125, + 135, + 110, + 144, + 123, + 133, + 116, + 150, + 129, + 139, + 119, + 130, + 151, + 140, + 120, + 131, + 152, + 141 + ] + }, + { + "name": "minecraft:tools", + "entries": [ + 793, + 778, + 783, + 798, + 773, + 788, + 796, + 781, + 786, + 801, + 776, + 791, + 795, + 780, + 785, + 800, + 775, + 790, + 794, + 779, + 784, + 799, + 774, + 789, + 797, + 782, + 787, + 802, + 777, + 792, + 1133 + ] + }, + { + "name": "minecraft:creeper_drop_music_discs", + "entries": [ + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128 + ] + }, + { + "name": "minecraft:compasses", + "entries": [ + 884, + 885 + ] + }, + { + "name": "c:lime_dyes", + "entries": [ + 905 + ] + }, + { + "name": "minecraft:arrows", + "entries": [ + 757, + 1109, + 1108 + ] + }, + { + "name": "minecraft:wool_carpets", + "entries": [ + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437 + ] + }, + { + "name": "c:dyes", + "entries": [ + 915, + 911, + 912, + 913, + 914, + 900, + 904, + 908, + 903, + 905, + 902, + 901, + 906, + 909, + 907, + 910 + ] + }, + { + "name": "c:pickaxes", + "entries": [ + 795, + 785, + 775, + 780, + 790, + 800 + ] + }, + { + "name": "c:white_dyes", + "entries": [ + 900 + ] + }, + { + "name": "c:brown_dyes", + "entries": [ + 912 + ] + }, + { + "name": "minecraft:wooden_doors", + "entries": [ + 685, + 686, + 687, + 688, + 689, + 691, + 694, + 695, + 692 + ] + }, + { + "name": "minecraft:warped_stems", + "entries": [ + 120, + 131, + 152, + 141 + ] + }, + { + "name": "minecraft:emerald_ores", + "entries": [ + 58, + 59 + ] + }, + { + "name": "minecraft:crimson_stems", + "entries": [ + 119, + 130, + 151, + 140 + ] + }, + { + "name": "c:gold_ingots", + "entries": [ + 770 + ] + }, + { + "name": "minecraft:ignored_by_piglin_babies", + "entries": [ + 869 + ] + }, + { + "name": "minecraft:swords", + "entries": [ + 793, + 778, + 783, + 798, + 773, + 788 + ] + }, + { + "name": "minecraft:stone_tool_materials", + "entries": [ + 22, + 1171, + 9 + ] + }, + { + "name": "minecraft:terracotta", + "entries": [ + 438, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418 + ] + }, + { + "name": "minecraft:wart_blocks", + "entries": [ + 493, + 494 + ] + }, + { + "name": "c:light_gray_dyes", + "entries": [ + 908 + ] + }, + { + "name": "minecraft:dark_oak_logs", + "entries": [ + 115, + 149, + 128, + 138 + ] + }, + { + "name": "minecraft:non_flammable_wood", + "entries": [ + 120, + 131, + 152, + 141, + 119, + 130, + 151, + 140, + 32, + 33, + 238, + 239, + 682, + 683, + 296, + 297, + 706, + 707, + 717, + 718, + 369, + 370, + 667, + 668, + 694, + 695, + 851, + 852 + ] + }, + { + "name": "minecraft:coals", + "entries": [ + 758, + 759 + ] + }, + { + "name": "minecraft:piglin_food", + "entries": [ + 837, + 838 + ] + }, + { + "name": "c:quartz_ores", + "entries": [ + 65 + ] + }, + { + "name": "minecraft:anvil", + "entries": [ + 395, + 396, + 397 + ] + }, + { + "name": "minecraft:birch_logs", + "entries": [ + 111, + 145, + 124, + 134 + ] + }, + { + "name": "minecraft:lapis_ores", + "entries": [ + 60, + 61 + ] + }, + { + "name": "c:emeralds", + "entries": [ + 761 + ] + }, + { + "name": "minecraft:axes", + "entries": [ + 796, + 781, + 786, + 801, + 776, + 791 + ] + }, + { + "name": "minecraft:hoes", + "entries": [ + 797, + 782, + 787, + 802, + 777, + 792 + ] + }, + { + "name": "c:green_dyes", + "entries": [ + 913 + ] + }, + { + "name": "c:raw_gold_ores", + "entries": [ + 769 + ] + }, + { + "name": "c:glass_panes", + "entries": [ + 333, + 470, + 478, + 464, + 474, + 475, + 472, + 476, + 466, + 471, + 468, + 465, + 469, + 473, + 477, + 463, + 467 + ] + }, + { + "name": "minecraft:fences", + "entries": [ + 287, + 291, + 293, + 288, + 289, + 290, + 296, + 297, + 294, + 345 + ] + }, + { + "name": "minecraft:saplings", + "entries": [ + 35, + 36, + 37, + 38, + 39, + 41, + 174, + 175, + 42 + ] + }, + { + "name": "minecraft:beds", + "entries": [ + 934, + 935, + 931, + 932, + 929, + 927, + 933, + 923, + 928, + 925, + 922, + 921, + 926, + 930, + 920, + 924 + ] + }, + { + "name": "minecraft:iron_ores", + "entries": [ + 50, + 51 + ] + }, + { + "name": "minecraft:oak_logs", + "entries": [ + 109, + 143, + 122, + 132 + ] + }, + { + "name": "minecraft:doors", + "entries": [ + 685, + 686, + 687, + 688, + 689, + 691, + 694, + 695, + 692, + 684 + ] + }, + { + "name": "c:raw_copper_ores", + "entries": [ + 767 + ] + }, + { + "name": "minecraft:banners", + "entries": [ + 1083, + 1084, + 1085, + 1086, + 1087, + 1088, + 1089, + 1090, + 1091, + 1092, + 1093, + 1094, + 1095, + 1096, + 1097, + 1098 + ] + }, + { + "name": "c:diamonds", + "entries": [ + 760 + ] + }, + { + "name": "c:orange_dyes", + "entries": [ + 901 + ] + }, + { + "name": "minecraft:smelts_to_glass", + "entries": [ + 44, + 46 + ] + }, + { + "name": "minecraft:stone_crafting_materials", + "entries": [ + 22, + 1171, + 9 + ] + }, + { + "name": "minecraft:wooden_fences", + "entries": [ + 287, + 291, + 293, + 288, + 289, + 290, + 296, + 297, + 294 + ] + }, + { + "name": "minecraft:piglin_repellents", + "entries": [ + 307, + 1158, + 1162 + ] + }, + { + "name": "c:redstone_dusts", + "entries": [ + 632 + ] + }, + { + "name": "c:shears", + "entries": [ + 938 + ] + }, + { + "name": "c:potions", + "entries": [ + 1110, + 1107, + 953 + ] + }, + { + "name": "minecraft:dampens_vibrations", + "entries": [ + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437 + ] + }, + { + "name": "minecraft:mangrove_logs", + "entries": [ + 116, + 150, + 129, + 139 + ] + }, + { + "name": "minecraft:jungle_logs", + "entries": [ + 112, + 146, + 125, + 135 + ] + }, + { + "name": "minecraft:lectern_books", + "entries": [ + 1043, + 1042 + ] + }, + { + "name": "minecraft:spruce_logs", + "entries": [ + 110, + 144, + 123, + 133 + ] + }, + { + "name": "minecraft:signs", + "entries": [ + 842, + 843, + 844, + 846, + 845, + 848, + 851, + 852, + 849 + ] + }, + { + "name": "minecraft:wooden_stairs", + "entries": [ + 359, + 360, + 361, + 362, + 363, + 365, + 369, + 370, + 366 + ] + }, + { + "name": "minecraft:wooden_buttons", + "entries": [ + 658, + 659, + 660, + 661, + 662, + 664, + 667, + 668, + 665 + ] + }, + { + "name": "minecraft:fishes", + "entries": [ + 891, + 895, + 892, + 896, + 894, + 893 + ] + }, + { + "name": "c:milk_buckets", + "entries": [ + 870 + ] + }, + { + "name": "minecraft:stone_bricks", + "entries": [ + 316, + 317, + 318, + 319 + ] + }, + { + "name": "c:light_blue_dyes", + "entries": [ + 903 + ] + }, + { + "name": "c:iron_ingots", + "entries": [ + 766 + ] + }, + { + "name": "minecraft:shovels", + "entries": [ + 794, + 779, + 784, + 799, + 774, + 789 + ] + }, + { + "name": "minecraft:chest_boats", + "entries": [ + 733, + 735, + 737, + 739, + 741, + 745, + 747 + ] + }, + { + "name": "minecraft:creeper_igniters", + "entries": [ + 754, + 1041 + ] + }, + { + "name": "c:gray_dyes", + "entries": [ + 907 + ] + }, + { + "name": "minecraft:slabs", + "entries": [ + 228, + 229, + 230, + 231, + 232, + 234, + 238, + 239, + 235, + 240, + 241, + 247, + 242, + 253, + 250, + 251, + 246, + 245, + 249, + 244, + 254, + 255, + 256, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 243, + 252, + 1172, + 1180, + 1176, + 627, + 628, + 630, + 629, + 107, + 106, + 105, + 92, + 91, + 90, + 89, + 108, + 248 + ] + }, + { + "name": "c:coal", + "entries": [ + 758, + 759 + ] + }, + { + "name": "c:raw_copper_blocks", + "entries": [ + 69 + ] + }, + { + "name": "c:raw_gold_blocks", + "entries": [ + 70 + ] + }, + { + "name": "minecraft:trapdoors", + "entries": [ + 701, + 699, + 703, + 700, + 697, + 698, + 706, + 707, + 704, + 696 + ] + }, + { + "name": "minecraft:redstone_ores", + "entries": [ + 56, + 57 + ] + }, + { + "name": "c:hoes", + "entries": [ + 797, + 787, + 777, + 782, + 792, + 802 + ] + }, + { + "name": "c:red_dyes", + "entries": [ + 914 + ] + }, + { + "name": "minecraft:buttons", + "entries": [ + 658, + 659, + 660, + 661, + 662, + 664, + 667, + 668, + 665, + 656, + 657 + ] + }, + { + "name": "minecraft:flowers", + "entries": [ + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 441, + 442, + 444, + 443, + 162, + 175, + 42 + ] + }, + { + "name": "c:bows", + "entries": [ + 1137, + 756 + ] + }, + { + "name": "minecraft:planks", + "entries": [ + 23, + 24, + 25, + 26, + 27, + 29, + 32, + 33, + 30 + ] + }, + { + "name": "minecraft:boats", + "entries": [ + 732, + 734, + 736, + 738, + 740, + 744, + 746, + 733, + 735, + 737, + 739, + 741, + 745, + 747 + ] + }, + { + "name": "minecraft:fox_food", + "entries": [ + 1159, + 1160 + ] + }, + { + "name": "minecraft:rails", + "entries": [ + 721, + 719, + 720, + 722 + ] + }, + { + "name": "minecraft:diamond_ores", + "entries": [ + 62, + 63 + ] + }, + { + "name": "c:water_buckets", + "entries": [ + 875, + 873, + 871, + 874, + 872, + 876, + 865 + ] + }, + { + "name": "c:yellow_dyes", + "entries": [ + 904 + ] + }, + { + "name": "c:empty_buckets", + "entries": [ + 864 + ] + }, + { + "name": "c:shulker_boxes", + "entries": [ + 498, + 510, + 511, + 508, + 506, + 512, + 502, + 507, + 504, + 501, + 500, + 505, + 509, + 513, + 499, + 503, + 514 + ] + }, + { + "name": "minecraft:leaves", + "entries": [ + 156, + 153, + 154, + 159, + 157, + 155, + 161, + 162, + 160 + ] + }, + { + "name": "minecraft:walls", + "entries": [ + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 390, + 389, + 391, + 392, + 394, + 393, + 381 + ] + }, + { + "name": "c:glass_blocks", + "entries": [ + 165, + 454, + 462, + 448, + 458, + 459, + 456, + 460, + 450, + 455, + 452, + 449, + 453, + 457, + 461, + 166, + 447, + 451 + ] + }, + { + "name": "minecraft:fence_gates", + "entries": [ + 712, + 710, + 714, + 711, + 708, + 709, + 717, + 718, + 715 + ] + }, + { + "name": "c:cyan_dyes", + "entries": [ + 909 + ] + }, + { + "name": "minecraft:wooden_pressure_plates", + "entries": [ + 673, + 674, + 675, + 676, + 677, + 679, + 682, + 683, + 680 + ] + }, + { + "name": "minecraft:acacia_logs", + "entries": [ + 113, + 147, + 126, + 136 + ] + }, + { + "name": "minecraft:candles", + "entries": [ + 1184, + 1185, + 1186, + 1187, + 1188, + 1189, + 1190, + 1191, + 1192, + 1193, + 1194, + 1195, + 1196, + 1197, + 1198, + 1199, + 1200 + ] + }, + { + "name": "minecraft:piglin_loved", + "entries": [ + 54, + 64, + 55, + 75, + 1174, + 671, + 770, + 1156, + 888, + 1052, + 962, + 840, + 841, + 828, + 829, + 830, + 831, + 1075, + 783, + 785, + 784, + 786, + 787, + 769, + 70 + ] + }, + { + "name": "minecraft:music_discs", + "entries": [ + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128, + 1131, + 1129, + 1130 + ] + }, + { + "name": "minecraft:tall_flowers", + "entries": [ + 441, + 442, + 444, + 443 + ] + }, + { + "name": "c:quartz", + "entries": [ + 763 + ] + }, + { + "name": "c:blue_dyes", + "entries": [ + 911 + ] + }, + { + "name": "c:copper_ingots", + "entries": [ + 768 + ] + }, + { + "name": "c:swords", + "entries": [ + 793, + 783, + 773, + 778, + 788, + 798 + ] + }, + { + "name": "minecraft:sand", + "entries": [ + 44, + 46 + ] + }, + { + "name": "minecraft:copper_ores", + "entries": [ + 52, + 53 + ] + }, + { + "name": "minecraft:gold_ores", + "entries": [ + 54, + 64, + 55 + ] + }, + { + "name": "minecraft:freeze_immune_wearables", + "entries": [ + 815, + 814, + 813, + 812, + 1077 + ] + }, + { + "name": "minecraft:logs_that_burn", + "entries": [ + 115, + 149, + 128, + 138, + 109, + 143, + 122, + 132, + 113, + 147, + 126, + 136, + 111, + 145, + 124, + 134, + 112, + 146, + 125, + 135, + 110, + 144, + 123, + 133, + 116, + 150, + 129, + 139 + ] + }, + { + "name": "minecraft:completes_find_tree_tutorial", + "entries": [ + 115, + 149, + 128, + 138, + 109, + 143, + 122, + 132, + 113, + 147, + 126, + 136, + 111, + 145, + 124, + 134, + 112, + 146, + 125, + 135, + 110, + 144, + 123, + 133, + 116, + 150, + 129, + 139, + 119, + 130, + 151, + 140, + 120, + 131, + 152, + 141, + 156, + 153, + 154, + 159, + 157, + 155, + 161, + 162, + 160, + 493, + 494 + ] + }, + { + "name": "minecraft:dirt", + "entries": [ + 15, + 14, + 17, + 16, + 340, + 18, + 223, + 19, + 118 + ] + }, + { + "name": "c:ores", + "entries": [ + 50, + 51, + 52, + 53, + 56, + 57, + 54, + 64, + 55, + 48, + 49, + 62, + 63, + 60, + 61, + 65, + 58, + 59 + ] + }, + { + "name": "c:shields", + "entries": [ + 1111 + ] + }, + { + "name": "minecraft:pickaxes", + "entries": [ + 795, + 780, + 785, + 800, + 775, + 790 + ] + }, + { + "name": "minecraft:cluster_max_harvestables", + "entries": [ + 795, + 785, + 790, + 800, + 780, + 775 + ] + } + ] + }, + { + "registry": "minecraft:entity_type", + "tags": [ + { + "name": "minecraft:axolotl_always_hostiles", + "entries": [ + 23, + 46, + 25 + ] + }, + { + "name": "minecraft:freeze_immune_entity_types", + "entries": [ + 97, + 76, + 91, + 113 + ] + }, + { + "name": "minecraft:dismounts_underwater", + "entries": [ + 10, + 15, + 21, + 49, + 60, + 66, + 72, + 80, + 95, + 98, + 103, + 119 + ] + }, + { + "name": "minecraft:impact_projectiles", + "entries": [ + 3, + 94, + 92, + 57, + 89, + 24, + 104, + 22, + 115 + ] + }, + { + "name": "minecraft:beehive_inhabitors", + "entries": [ + 6 + ] + }, + { + "name": "c:boats", + "entries": [ + 9, + 13 + ] + }, + { + "name": "minecraft:skeletons", + "entries": [ + 86, + 97, + 114 + ] + }, + { + "name": "minecraft:arrows", + "entries": [ + 3, + 94 + ] + }, + { + "name": "minecraft:freeze_hurts_extra_types", + "entries": [ + 98, + 7, + 62 + ] + }, + { + "name": "minecraft:frog_food", + "entries": [ + 88, + 62 + ] + }, + { + "name": "c:bosses", + "entries": [ + 27, + 113 + ] + }, + { + "name": "minecraft:fall_damage_immune", + "entries": [ + 53, + 91, + 83, + 0, + 5, + 6, + 7, + 11, + 15, + 41, + 71, + 62, + 67, + 70, + 113 + ] + }, + { + "name": "c:minecarts", + "entries": [ + 64, + 102, + 14, + 40, + 17, + 48, + 93 + ] + }, + { + "name": "minecraft:raiders", + "entries": [ + 31, + 75, + 80, + 109, + 51, + 112 + ] + }, + { + "name": "minecraft:powder_snow_walkable_mobs", + "entries": [ + 79, + 30, + 85, + 38 + ] + }, + { + "name": "minecraft:axolotl_hunt_targets", + "entries": [ + 105, + 78, + 81, + 16, + 96, + 44, + 99 + ] + } + ] + }, + { + "registry": "minecraft:banner_pattern", + "tags": [ + { + "name": "minecraft:pattern_item/piglin", + "entries": [ + 40 + ] + }, + { + "name": "minecraft:no_item_required", + "entries": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34 + ] + }, + { + "name": "minecraft:pattern_item/flower", + "entries": [ + 38 + ] + }, + { + "name": "minecraft:pattern_item/creeper", + "entries": [ + 36 + ] + }, + { + "name": "minecraft:pattern_item/globe", + "entries": [ + 35 + ] + }, + { + "name": "minecraft:pattern_item/mojang", + "entries": [ + 39 + ] + }, + { + "name": "minecraft:pattern_item/skull", + "entries": [ + 37 + ] + } + ] + }, + { + "registry": "minecraft:fluid", + "tags": [ + { + "name": "minecraft:lava", + "entries": [ + 4, + 3 + ] + }, + { + "name": "minecraft:water", + "entries": [ + 2, + 1 + ] + }, + { + "name": "c:water", + "entries": [ + 2, + 1 + ] + }, + { + "name": "c:lava", + "entries": [ + 4, + 3 + ] + } + ] + }, + { + "registry": "minecraft:instrument", + "tags": [ + { + "name": "minecraft:screaming_goat_horns", + "entries": [ + 4, + 5, + 6, + 7 + ] + }, + { + "name": "minecraft:goat_horns", + "entries": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ] + }, + { + "name": "minecraft:regular_goat_horns", + "entries": [ + 0, + 1, + 2, + 3 + ] + } + ] + } +] \ No newline at end of file diff --git a/extractor/src/main/java/rs/valence/extractor/Main.java b/extractor/src/main/java/rs/valence/extractor/Main.java index b6d5e2b9e..b31433111 100644 --- a/extractor/src/main/java/rs/valence/extractor/Main.java +++ b/extractor/src/main/java/rs/valence/extractor/Main.java @@ -74,9 +74,9 @@ public void onInitialize() { } ServerLifecycleEvents.SERVER_STARTING.register(server -> { - LOGGER.info("Server starting, Extracting registry codec..."); + LOGGER.info("Server starting, Running startup extractors..."); + // TODO: make `Codec` implement `Extractor` var codecExtractor = new Codec(server); - try { var out = outputDirectory.resolve(codecExtractor.fileName()); var compound = codecExtractor.extract(); @@ -92,6 +92,22 @@ public void onInitialize() { LOGGER.error("Extractor for \"" + codecExtractor.fileName() + "\" failed.", e); } + var startupExtractors = new Extractor[]{ + new Tags(server), + }; + + for (var ext : startupExtractors) { + try { + var out = outputDirectory.resolve(ext.fileName()); + var fileWriter = new FileWriter(out.toFile(), StandardCharsets.UTF_8); + gson.toJson(ext.extract(), fileWriter); + fileWriter.close(); + LOGGER.info("Wrote " + out.toAbsolutePath()); + } catch (Exception e) { + LOGGER.error("Extractor for \"" + ext.fileName() + "\" failed.", e); + } + } + LOGGER.info("Done."); server.shutdown(); }); diff --git a/extractor/src/main/java/rs/valence/extractor/extractors/Blocks.java b/extractor/src/main/java/rs/valence/extractor/extractors/Blocks.java index 6727772b1..b514ea2a3 100644 --- a/extractor/src/main/java/rs/valence/extractor/extractors/Blocks.java +++ b/extractor/src/main/java/rs/valence/extractor/extractors/Blocks.java @@ -102,8 +102,8 @@ public JsonElement extract() { for (var blockEntity : Registries.BLOCK_ENTITY_TYPE) { var blockEntityJson = new JsonObject(); blockEntityJson.addProperty("id", Registries.BLOCK_ENTITY_TYPE.getRawId(blockEntity)); - blockEntityJson.addProperty("namespace", Registries.BLOCK_ENTITY_TYPE.getId(blockEntity).getNamespace()); - blockEntityJson.addProperty("path", Registries.BLOCK_ENTITY_TYPE.getId(blockEntity).getPath()); + blockEntityJson.addProperty("ident", Registries.BLOCK_ENTITY_TYPE.getId(blockEntity).toString()); + blockEntityJson.addProperty("name", Registries.BLOCK_ENTITY_TYPE.getId(blockEntity).getPath()); blockEntitiesJson.add(blockEntityJson); } diff --git a/extractor/src/main/java/rs/valence/extractor/extractors/Tags.java b/extractor/src/main/java/rs/valence/extractor/extractors/Tags.java new file mode 100644 index 000000000..d35394fb1 --- /dev/null +++ b/extractor/src/main/java/rs/valence/extractor/extractors/Tags.java @@ -0,0 +1,90 @@ +package rs.valence.extractor.extractors; + +import com.mojang.datafixers.util.Pair; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.entity.SpawnGroup; +import net.minecraft.util.Identifier; +import net.minecraft.util.collection.Weighted; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.registry.BuiltinRegistries; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.server.MinecraftServer; +import rs.valence.extractor.Main; +import net.minecraft.registry.tag.TagKey; +import net.minecraft.registry.CombinedDynamicRegistries; +import net.minecraft.registry.ServerDynamicRegistryType; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.registry.entry.RegistryEntryList; +import net.minecraft.registry.SerializableRegistries; + +import java.util.Map; +import java.util.HashMap; +import java.util.stream.Collectors; + +public class Tags implements Main.Extractor { + private CombinedDynamicRegistries dynamicRegistryManager; + + public Tags(MinecraftServer server) { + this.dynamicRegistryManager = server.getCombinedDynamicRegistries(); + } + + @Override + public String fileName() { + return "tags.json"; + } + + @Override + public JsonElement extract() { + var tagsJson = new JsonArray(); + + Map>, Map> registryTags = + SerializableRegistries.streamRegistryManagerEntries(this.dynamicRegistryManager) + .map(registry -> Pair.of(registry.key(), serializeTags(registry.value()))) + .filter(pair -> !(pair.getSecond()).isEmpty()) + .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); + + for (var registry : registryTags.entrySet()) { + var registryIdent = registry.getKey().getValue().toString(); + var tagGroupJson = new JsonObject(); + var tagGroupTagsJson = new JsonArray(); + + for (var tag : registry.getValue().entrySet()) { + var tagJson = new JsonObject(); + var ident = tag.getKey().toString(); + var raw_ids = tag.getValue(); + + tagJson.addProperty("name", ident); + tagJson.add("entries", raw_ids); + tagGroupTagsJson.add(tagJson); + } + + tagGroupJson.addProperty("registry", registryIdent.toString()); + tagGroupJson.add("tags", tagGroupTagsJson); + tagsJson.add(tagGroupJson); + } + + return tagsJson; + } + + private static Map serializeTags(Registry registry) { + HashMap map = new HashMap(); + registry.streamTagsAndEntries().forEach(pair -> { + RegistryEntryList registryEntryList = (RegistryEntryList)pair.getSecond(); + JsonArray intList = new JsonArray(registryEntryList.size()); + for (RegistryEntry registryEntry : registryEntryList) { + if (registryEntry.getType() != RegistryEntry.Type.REFERENCE) { + throw new IllegalStateException("Can't serialize unregistered value " + registryEntry); + } + intList.add(registry.getRawId(registryEntry.value())); + } + map.put(((TagKey)pair.getFirst()).id(), intList); + }); + return map; + } +} diff --git a/tools/packet_inspector/Cargo.toml b/tools/packet_inspector/Cargo.toml index 79f81b6d2..8708a4841 100644 --- a/tools/packet_inspector/Cargo.toml +++ b/tools/packet_inspector/Cargo.toml @@ -32,4 +32,5 @@ tokio.workspace = true toml.workspace = true tracing-subscriber.workspace = true valence_nbt = { workspace = true, features = ["preserve_order"] } -valence_core = { workspace = true, features = ["compression"] } +valence.workspace = true + diff --git a/tools/packet_inspector/src/context.rs b/tools/packet_inspector/src/context.rs index 633f4269f..e8dd430fa 100644 --- a/tools/packet_inspector/src/context.rs +++ b/tools/packet_inspector/src/context.rs @@ -7,15 +7,13 @@ use owo_colors::{OwoColorize, Style}; use regex::Regex; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; -use valence_core::packet::c2s::handshake::HandshakeC2s; -use valence_core::packet::c2s::login::{LoginHelloC2s, LoginKeyC2s}; -use valence_core::packet::c2s::play::C2sPlayPacket; -use valence_core::packet::c2s::status::{QueryPingC2s, QueryRequestC2s}; -use valence_core::packet::decode::PacketDecoder; -use valence_core::packet::s2c::login::{LoginSuccessS2c, S2cLoginPacket}; -use valence_core::packet::s2c::play::S2cPlayPacket; -use valence_core::packet::s2c::status::{QueryPongS2c, QueryResponseS2c}; +use valence::network::packet::{ + HandshakeC2s, LoginHelloC2s, LoginKeyC2s, LoginSuccessS2c, QueryPingC2s, QueryPongS2c, + QueryRequestC2s, QueryResponseS2c, +}; +use valence::protocol::decode::PacketDecoder; +use crate::packet_groups::{C2sPlayPacket, S2cLoginPacket, S2cPlayPacket}; use crate::packet_widget::{systemtime_strftime, PacketDirection}; use crate::MetaPacket; @@ -115,9 +113,9 @@ impl Packet { ($packet:ident) => { match dec.try_next_packet() { Ok(Some(frame)) => { - if let Ok(pkt) = <$packet as valence_core::packet::Packet>::decode_packet( - &mut &frame[..], - ) { + if let Ok(pkt) = + <$packet as valence::protocol::Packet>::decode_packet(&mut &frame[..]) + { if formatted { format!("{pkt:#?}") } else { @@ -194,8 +192,8 @@ impl Context { pub fn clear(&self) { self.last_packet.store(0, Ordering::Relaxed); - *self.selected_packet.write().expect("Poisoned RwLock") = None; - self.packets.write().expect("Poisoned RwLock").clear(); + *self.selected_packet.write().unwrap() = None; + self.packets.write().unwrap().clear(); if let ContextMode::Gui(ctx) = &self.mode { ctx.request_repaint(); } @@ -205,7 +203,7 @@ impl Context { match &self.mode { ContextMode::Gui(ctx) => { packet.id = self.last_packet.fetch_add(1, Ordering::Relaxed); - self.packets.write().expect("Poisoned RwLock").push(packet); + self.packets.write().unwrap().push(packet); ctx.request_repaint(); } ContextMode::Cli(logger) => { @@ -250,14 +248,14 @@ impl Context { } pub fn set_selected_packets(&self, packets: BTreeMap) { - *self.visible_packets.write().expect("Poisoned RwLock") = packets; + *self.visible_packets.write().unwrap() = packets; } pub fn is_packet_hidden(&self, index: usize) -> bool { - let packets = self.packets.read().expect("Poisoned RwLock"); + let packets = self.packets.read().unwrap(); let packet = packets.get(index).expect("Packet not found"); - let visible_packets = self.visible_packets.read().expect("Poisoned RwLock"); + let visible_packets = self.visible_packets.read().unwrap(); let meta_packet: MetaPacket = (*packet).clone().into(); @@ -267,7 +265,7 @@ impl Context { } } - let filter = self.filter.read().expect("Poisoned RwLock"); + let filter = self.filter.read().unwrap(); let filter = filter.as_str(); if !filter.is_empty() && packet @@ -282,7 +280,7 @@ impl Context { } pub fn select_previous_packet(&self) { - let mut selected_packet = self.selected_packet.write().expect("Poisoned RwLock"); + let mut selected_packet = self.selected_packet.write().unwrap(); if let Some(idx) = *selected_packet { if idx > 0 { let mut new_index = idx - 1; @@ -296,7 +294,7 @@ impl Context { *selected_packet = Some(new_index); } } else { - let packets = self.packets.read().expect("Poisoned RwLock"); + let packets = self.packets.read().unwrap(); if !packets.is_empty() { *selected_packet = Some(0); } @@ -304,12 +302,12 @@ impl Context { } pub fn select_next_packet(&self) { - let mut selected_packet = self.selected_packet.write().expect("Poisoned RwLock"); + let mut selected_packet = self.selected_packet.write().unwrap(); if let Some(idx) = *selected_packet { - if idx < self.packets.read().expect("Poisoned RwLock").len() - 1 { + if idx < self.packets.read().unwrap().len() - 1 { let mut new_index = idx + 1; while self.is_packet_hidden(new_index) { - if new_index == self.packets.read().expect("Poisoned RwLock").len() - 1 { + if new_index == self.packets.read().unwrap().len() - 1 { new_index = idx; break; } @@ -319,7 +317,7 @@ impl Context { *selected_packet = Some(new_index); } } else { - let packets = self.packets.read().expect("Poisoned RwLock"); + let packets = self.packets.read().unwrap(); if !packets.is_empty() { *selected_packet = Some(1); } @@ -327,19 +325,19 @@ impl Context { } pub fn set_selected_packet(&self, idx: usize) { - *self.selected_packet.write().expect("Poisoned RwLock") = Some(idx); + *self.selected_packet.write().unwrap() = Some(idx); } pub fn set_filter(&self, filter: String) { *self.filter.write().expect("Posisoned RwLock") = filter; - *self.selected_packet.write().expect("Poisoned RwLock") = None; + *self.selected_packet.write().unwrap() = None; } pub fn save(&self, path: PathBuf) -> Result<(), std::io::Error> { let packets = self .packets .read() - .expect("Poisoned RwLock") + .unwrap() .iter() .map(|packet| { format!( diff --git a/tools/packet_inspector/src/main.rs b/tools/packet_inspector/src/main.rs index e3b7a452d..ecebebad4 100644 --- a/tools/packet_inspector/src/main.rs +++ b/tools/packet_inspector/src/main.rs @@ -19,6 +19,7 @@ mod config; mod context; mod hex_viewer; +pub mod packet_groups; mod packet_widget; mod state; mod syntax_highlighting; @@ -45,18 +46,15 @@ use tokio::net::{TcpListener, TcpStream}; use tokio::sync::Semaphore; use tokio::task::JoinHandle; use tracing_subscriber::filter::LevelFilter; -use valence_core::packet::c2s::handshake::handshake::NextState; -use valence_core::packet::c2s::handshake::HandshakeC2s; -use valence_core::packet::c2s::login::{LoginHelloC2s, LoginKeyC2s}; -use valence_core::packet::c2s::play::C2sPlayPacket; -use valence_core::packet::c2s::status::{QueryPingC2s, QueryRequestC2s}; -use valence_core::packet::decode::PacketDecoder; -use valence_core::packet::encode::PacketEncoder; -use valence_core::packet::s2c::login::{LoginSuccessS2c, S2cLoginPacket}; -use valence_core::packet::s2c::play::S2cPlayPacket; -use valence_core::packet::s2c::status::{QueryPongS2c, QueryResponseS2c}; +use valence::network::packet::{ + HandshakeC2s, HandshakeNextState, LoginHelloC2s, LoginKeyC2s, LoginSuccessS2c, QueryPingC2s, + QueryPongS2c, QueryRequestC2s, QueryResponseS2c, +}; +use valence::protocol::decode::PacketDecoder; +use valence::protocol::encode::PacketEncoder; use crate::context::{ContextMode, Stage}; +use crate::packet_groups::{C2sPlayPacket, S2cLoginPacket, S2cPlayPacket}; use crate::packet_widget::PacketDirection; use crate::state::State; @@ -226,7 +224,7 @@ async fn handle_connection( let handshake: HandshakeC2s = c2s.rw_packet(Stage::HandshakeC2s).await?; match handshake.next_state { - NextState::Status => { + HandshakeNextState::Status => { c2s.rw_packet::(Stage::QueryRequestC2s) .await?; s2c.rw_packet::(Stage::QueryResponseS2c) @@ -236,7 +234,7 @@ async fn handle_connection( Ok(()) } - NextState::Login => { + HandshakeNextState::Login => { c2s.rw_packet::(Stage::LoginHelloC2s).await?; match s2c @@ -274,7 +272,7 @@ async fn handle_connection( S2cLoginPacket::LoginSuccessS2c(_) => {} S2cLoginPacket::LoginDisconnectS2c(_) => return Ok(()), S2cLoginPacket::LoginQueryRequestS2c(_) => { - bail!("got login plugin request. Don't know how to proceed.") + bail!("Got login plugin request. Don't know how to proceed.") } } @@ -826,8 +824,8 @@ impl eframe::App for GuiApp { ui.horizontal(|ui| { ui.heading("Packets"); - let count = self.context.packet_count.read().expect("Poisoned RwLock"); - let total = self.context.packets.read().expect("Poisoned RwLock").len(); + let count = self.context.packet_count.read().unwrap(); + let total = self.context.packets.read().unwrap().len(); let all_selected = self.selected_packets.values().all(|v| *v); @@ -861,7 +859,7 @@ impl eframe::App for GuiApp { .auto_shrink([false, false]) .stick_to_bottom(true) .show(ui, |ui| { - let mut f = self.context.packets.write().expect("Poisoned RwLock"); + let mut f = self.context.packets.write().unwrap(); let f: Vec<&mut Packet> = f .iter_mut() @@ -916,15 +914,11 @@ impl eframe::App for GuiApp { }) .collect(); - *self.context.packet_count.write().expect("Poisoned RwLock") = f.len(); + *self.context.packet_count.write().unwrap() = f.len(); for packet in f { { - let selected = self - .context - .selected_packet - .read() - .expect("Poisoned RwLock"); + let selected = self.context.selected_packet.read().unwrap(); if let Some(idx) = *selected { if idx == packet.id { packet.selected(true); diff --git a/tools/packet_inspector/src/packet_groups.rs b/tools/packet_inspector/src/packet_groups.rs new file mode 100644 index 000000000..9a3aa8cee --- /dev/null +++ b/tools/packet_inspector/src/packet_groups.rs @@ -0,0 +1,249 @@ +use valence::advancement::packet::*; +use valence::client::action::*; +use valence::client::command::*; +use valence::client::custom_payload::*; +use valence::client::hand_swing::*; +use valence::client::interact_block::*; +use valence::client::interact_entity::*; +use valence::client::interact_item::*; +use valence::client::keepalive::*; +use valence::client::movement::*; +use valence::client::packet::structure_block::*; +use valence::client::packet::*; +use valence::client::resource_pack::*; +use valence::client::settings::*; +use valence::client::status::*; +use valence::client::teleport::*; +use valence::client::title::*; +use valence::entity::packet::*; +use valence::instance::packet::*; +use valence::inventory::packet::synchronize_recipes::*; +use valence::inventory::packet::*; +use valence::network::packet::*; +use valence::packet_group; +use valence::particle::*; +use valence::player_list::packet::*; +use valence::protocol::packet::boss_bar::*; +use valence::protocol::packet::chat::*; +use valence::protocol::packet::command::*; +use valence::protocol::packet::map::*; +use valence::protocol::packet::scoreboard::*; +use valence::protocol::packet::sound::*; +use valence::protocol::packet::synchronize_tags::*; + +packet_group! { + #[derive(Clone)] + pub C2sHandshakePacket<'a> { + HandshakeC2s<'a> + } +} + +packet_group! { + #[derive(Clone)] + pub S2cStatusPacket<'a> { + QueryPongS2c, + QueryResponseS2c<'a>, + } +} + +packet_group! { + #[derive(Clone)] + pub C2sStatusPacket { + QueryPingC2s, + QueryRequestC2s, + } +} + +packet_group! { + #[derive(Clone)] + pub S2cLoginPacket<'a> { + LoginCompressionS2c, + LoginDisconnectS2c<'a>, + LoginHelloS2c<'a>, + LoginQueryRequestS2c<'a>, + LoginSuccessS2c<'a>, + } +} + +packet_group! { + #[derive(Clone)] + pub C2sLoginPacket<'a> { + LoginHelloC2s<'a>, + LoginKeyC2s<'a>, + LoginQueryResponseC2s<'a>, + } +} + +packet_group! { + #[derive(Clone)] + pub S2cPlayPacket<'a> { + AdvancementUpdateS2c<'a>, + BlockBreakingProgressS2c, + BlockEntityUpdateS2c<'a>, + BlockEventS2c, + BlockUpdateS2c, + BossBarS2c, + BundleSplitterS2c, + ChatMessageS2c<'a>, + ChatSuggestionsS2c<'a>, + ChunkBiomeDataS2c<'a>, + ChunkDataS2c<'a>, + ChunkDeltaUpdateS2c<'a>, + ChunkLoadDistanceS2c, + ChunkRenderDistanceCenterS2c, + ClearTitleS2c, + CloseScreenS2c, + CommandSuggestionsS2c<'a>, + CommandTreeS2c<'a>, + CooldownUpdateS2c, + CraftFailedResponseS2c<'a>, + CustomPayloadS2c<'a>, + DamageTiltS2c, + DeathMessageS2c<'a>, + DifficultyS2c, + DisconnectS2c<'a>, + EndCombatS2c, + EnterCombatS2c, + EntitiesDestroyS2c<'a>, + EntityAnimationS2c, + EntityAttachS2c, + EntityAttributesS2c<'a>, + EntityDamageS2c, + EntityEquipmentUpdateS2c, + EntityPassengersSetS2c, + EntityPositionS2c, + EntitySetHeadYawS2c, + EntitySpawnS2c, + EntityStatusEffectS2c, + EntityStatusS2c, + EntityTrackerUpdateS2c<'a>, + EntityVelocityUpdateS2c, + ExperienceBarUpdateS2c, + ExperienceOrbSpawnS2c, + ExplosionS2c<'a>, + FeaturesS2c<'a>, + GameJoinS2c<'a>, + GameMessageS2c<'a>, + GameStateChangeS2c, + HealthUpdateS2c, + InventoryS2c<'a>, + ItemPickupAnimationS2c, + KeepAliveS2c, + LightUpdateS2c, + LookAtS2c, + MapUpdateS2c<'a>, + MoveRelativeS2c, + NbtQueryResponseS2c, + OpenHorseScreenS2c, + OpenScreenS2c<'a>, + OpenWrittenBookS2c, + OverlayMessageS2c<'a>, + ParticleS2c<'a>, + PlayerAbilitiesS2c, + PlayerActionResponseS2c, + PlayerListHeaderS2c<'a>, + PlayerListS2c<'a>, + PlayerPositionLookS2c, + PlayerRemoveS2c<'a>, + PlayerRespawnS2c<'a>, + PlayerSpawnPositionS2c, + PlayerSpawnS2c, + PlayPingS2c, + PlaySoundFromEntityS2c, + PlaySoundS2c<'a>, + ProfilelessChatMessageS2c<'a>, + RemoveEntityStatusEffectS2c, + RemoveMessageS2c<'a>, + ResourcePackSendS2c<'a>, + RotateS2c, + RotateAndMoveRelativeS2c, + ScoreboardDisplayS2c<'a>, + ScoreboardObjectiveUpdateS2c<'a>, + ScoreboardPlayerUpdateS2c<'a>, + ScreenHandlerPropertyUpdateS2c, + ScreenHandlerSlotUpdateS2c<'a>, + SelectAdvancementTabS2c<'a>, + ServerMetadataS2c<'a>, + SetCameraEntityS2c, + SetTradeOffersS2c, + SignEditorOpenS2c, + SimulationDistanceS2c, + StatisticsS2c, + StopSoundS2c<'a>, + SubtitleS2c<'a>, + SynchronizeRecipesS2c<'a>, + SynchronizeTagsS2c<'a>, + TeamS2c<'a>, + TitleFadeS2c, + TitleS2c<'a>, + UnloadChunkS2c, + UnlockRecipesS2c<'a>, + UpdateSelectedSlotS2c, + VehicleMoveS2c, + WorldBorderCenterChangedS2c, + WorldBorderInitializeS2c, + WorldBorderInterpolateSizeS2c, + WorldBorderSizeChangedS2c, + WorldBorderWarningBlocksChangedS2c, + WorldBorderWarningTimeChangedS2c, + WorldEventS2c, + WorldTimeUpdateS2c, + } +} + +packet_group! { + #[derive(Clone)] + pub C2sPlayPacket<'a> { + AdvancementTabC2s<'a>, + BoatPaddleStateC2s, + BookUpdateC2s<'a>, + ButtonClickC2s, + ChatMessageC2s<'a>, + ClickSlotC2s, + ClientCommandC2s, + ClientSettingsC2s<'a>, + ClientStatusC2s, + CloseHandledScreenC2s, + CommandExecutionC2s<'a>, + CraftRequestC2s<'a>, + CreativeInventoryActionC2s, + CustomPayloadC2s<'a>, + FullC2s, + HandSwingC2s, + JigsawGeneratingC2s, + KeepAliveC2s, + LookAndOnGroundC2s, + MessageAcknowledgmentC2s, + OnGroundOnlyC2s, + PickFromInventoryC2s, + PlayerActionC2s, + PlayerInputC2s, + PlayerInteractBlockC2s, + PlayerInteractEntityC2s, + PlayerInteractItemC2s, + PlayerSessionC2s<'a>, + PlayPongC2s, + PositionAndOnGroundC2s, + QueryBlockNbtC2s, + QueryEntityNbtC2s, + RecipeBookDataC2s<'a>, + RecipeCategoryOptionsC2s, + RenameItemC2s<'a>, + RequestCommandCompletionsC2s<'a>, + ResourcePackStatusC2s, + SelectMerchantTradeC2s, + SpectatorTeleportC2s, + TeleportConfirmC2s, + UpdateBeaconC2s, + UpdateCommandBlockC2s<'a>, + UpdateCommandBlockMinecartC2s<'a>, + UpdateDifficultyC2s, + UpdateDifficultyLockC2s, + UpdateJigsawC2s<'a>, + UpdatePlayerAbilitiesC2s, + UpdateSelectedSlotC2s, + UpdateSignC2s<'a>, + UpdateStructureBlockC2s<'a>, + VehicleMoveC2s, + } +} diff --git a/tools/packet_inspector/src/state.rs b/tools/packet_inspector/src/state.rs index 105cb2bf4..636f7c7ef 100644 --- a/tools/packet_inspector/src/state.rs +++ b/tools/packet_inspector/src/state.rs @@ -5,9 +5,9 @@ use bytes::BytesMut; use time::OffsetDateTime; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; -use valence_core::packet::decode::{decode_packet, PacketDecoder}; -use valence_core::packet::encode::PacketEncoder; -use valence_core::packet::Packet as ValencePacket; +use valence::protocol::decode::{decode_packet, PacketDecoder}; +use valence::protocol::encode::PacketEncoder; +use valence::protocol::Packet as ValencePacket; use crate::context::{Context, Packet, Stage}; use crate::packet_widget::PacketDirection; diff --git a/tools/playground/Cargo.toml b/tools/playground/Cargo.toml index 42ec02d5f..417982710 100644 --- a/tools/playground/Cargo.toml +++ b/tools/playground/Cargo.toml @@ -4,9 +4,10 @@ version.workspace = true edition.workspace = true [dependencies] -anyhow = "1.0.65" -glam = "0.23.0" -tracing = "0.1.37" -tracing-subscriber = "0.3.16" +anyhow.workspace = true +clap.workspace = true +glam.workspace = true +tracing-subscriber.workspace = true +tracing.workspace = true valence_core.workspace = true valence.workspace = true diff --git a/tools/playground/src/main.rs b/tools/playground/src/main.rs index b1498cb5a..6d9a94f48 100644 --- a/tools/playground/src/main.rs +++ b/tools/playground/src/main.rs @@ -16,6 +16,7 @@ clippy::dbg_macro )] +use clap::Parser; use tracing::Level; use valence::app::App; @@ -23,9 +24,17 @@ use valence::app::App; mod extras; mod playground; +#[derive(Parser)] +struct Args { + #[arg(short, default_value_t = Level::DEBUG)] + log_level: Level, +} + fn main() { + let args = Args::parse(); + tracing_subscriber::fmt() - .with_max_level(Level::DEBUG) + .with_max_level(args.log_level) .init(); let mut app = App::new();