1mod events;
2
3use std::{collections::HashSet, sync::Arc};
4
5use azalea_core::{
6 game_type::GameMode,
7 position::{ChunkPos, Vec3},
8};
9use azalea_entity::{
10 Dead, EntityBundle, EntityKindComponent, HasClientLoaded, LoadedBy, LocalEntity, LookDirection,
11 Physics, PlayerAbilities, Position, RelativeEntityUpdate,
12 indexing::{EntityIdIndex, EntityUuidIndex},
13 metadata::{Health, apply_metadata},
14};
15use azalea_protocol::{
16 common::movements::MoveFlags,
17 packets::{ConnectionProtocol, game::*},
18};
19use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
20use bevy_ecs::{prelude::*, system::SystemState};
21pub use events::*;
22use tracing::{debug, error, trace, warn};
23
24use crate::{
25 ClientInformation,
26 block_update::QueuedServerBlockUpdates,
27 chat::{ChatPacket, ChatReceivedEvent},
28 chunks,
29 connection::RawConnection,
30 declare_packet_handlers,
31 disconnect::DisconnectEvent,
32 interact::BlockStatePredictionHandler,
33 inventory::{
34 ClientSideCloseContainerEvent, Inventory, MenuOpenedEvent, SetContainerContentEvent,
35 },
36 local_player::{Hunger, InstanceHolder, LocalGameMode, TabList},
37 movement::{KnockbackEvent, KnockbackType},
38 packet::as_system,
39 player::{GameProfileComponent, PlayerInfo},
40 tick_counter::TicksConnected,
41};
42
43pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundGamePacket) {
44 let mut handler = GamePacketHandler { player, ecs };
45
46 declare_packet_handlers!(
48 ClientboundGamePacket,
49 packet,
50 handler,
51 [
52 login,
53 set_chunk_cache_radius,
54 chunk_batch_start,
55 chunk_batch_finished,
56 custom_payload,
57 change_difficulty,
58 commands,
59 player_abilities,
60 set_cursor_item,
61 update_tags,
62 disconnect,
63 update_recipes,
64 entity_event,
65 player_position,
66 player_info_update,
67 player_info_remove,
68 set_chunk_cache_center,
69 chunks_biomes,
70 light_update,
71 level_chunk_with_light,
72 add_entity,
73 set_entity_data,
74 update_attributes,
75 set_entity_motion,
76 set_entity_link,
77 initialize_border,
78 set_time,
79 set_default_spawn_position,
80 set_health,
81 set_experience,
82 teleport_entity,
83 update_advancements,
84 rotate_head,
85 move_entity_pos,
86 move_entity_pos_rot,
87 move_entity_rot,
88 keep_alive,
89 remove_entities,
90 player_chat,
91 system_chat,
92 disguised_chat,
93 sound,
94 level_event,
95 block_update,
96 animate,
97 section_blocks_update,
98 game_event,
99 level_particles,
100 server_data,
101 set_equipment,
102 update_mob_effect,
103 award_stats,
104 block_changed_ack,
105 block_destruction,
106 block_entity_data,
107 block_event,
108 boss_event,
109 command_suggestions,
110 container_set_content,
111 container_set_data,
112 container_set_slot,
113 container_close,
114 cooldown,
115 custom_chat_completions,
116 delete_chat,
117 explode,
118 forget_level_chunk,
119 horse_screen_open,
120 map_item_data,
121 merchant_offers,
122 move_vehicle,
123 open_book,
124 open_screen,
125 open_sign_editor,
126 ping,
127 place_ghost_recipe,
128 player_combat_end,
129 player_combat_enter,
130 player_combat_kill,
131 player_look_at,
132 remove_mob_effect,
133 resource_pack_push,
134 resource_pack_pop,
135 respawn,
136 start_configuration,
137 entity_position_sync,
138 select_advancements_tab,
139 set_action_bar_text,
140 set_border_center,
141 set_border_lerp_size,
142 set_border_size,
143 set_border_warning_delay,
144 set_border_warning_distance,
145 set_camera,
146 set_display_objective,
147 set_objective,
148 set_passengers,
149 set_player_team,
150 set_score,
151 set_simulation_distance,
152 set_subtitle_text,
153 set_title_text,
154 set_titles_animation,
155 clear_titles,
156 sound_entity,
157 stop_sound,
158 tab_list,
159 tag_query,
160 take_item_entity,
161 bundle_delimiter,
162 damage_event,
163 hurt_animation,
164 ticking_state,
165 ticking_step,
166 reset_score,
167 cookie_request,
168 debug_sample,
169 pong_response,
170 store_cookie,
171 transfer,
172 move_minecart_along_track,
173 set_held_slot,
174 set_player_inventory,
175 projectile_power,
176 custom_report_details,
177 server_links,
178 player_rotation,
179 recipe_book_add,
180 recipe_book_remove,
181 recipe_book_settings,
182 test_instance_block_status,
183 waypoint,
184 clear_dialog,
185 show_dialog,
186 ]
187 );
188}
189
190pub struct GamePacketHandler<'a> {
191 pub ecs: &'a mut World,
192 pub player: Entity,
193}
194impl GamePacketHandler<'_> {
195 pub fn login(&mut self, p: &ClientboundLogin) {
196 debug!("Got login packet");
197
198 as_system::<(
199 Commands,
200 Query<
201 (
202 &GameProfileComponent,
203 &ClientInformation,
204 Option<&mut InstanceName>,
205 Option<&mut LoadedBy>,
206 &mut EntityIdIndex,
207 &mut InstanceHolder,
208 ),
209 With<LocalEntity>,
210 >,
211 EventWriter<InstanceLoadedEvent>,
212 ResMut<InstanceContainer>,
213 ResMut<EntityUuidIndex>,
214 Query<&mut LoadedBy, Without<LocalEntity>>,
215 )>(
216 self.ecs,
217 |(
218 mut commands,
219 mut query,
220 mut instance_loaded_events,
221 mut instance_container,
222 mut entity_uuid_index,
223 mut loaded_by_query,
224 )| {
225 let (
226 game_profile,
227 client_information,
228 instance_name,
229 loaded_by,
230 mut entity_id_index,
231 mut instance_holder,
232 ) = query.get_mut(self.player).unwrap();
233
234 let new_instance_name = p.common.dimension.clone();
235
236 if let Some(mut instance_name) = instance_name {
237 **instance_name = new_instance_name.clone();
238 } else {
239 commands
240 .entity(self.player)
241 .insert(InstanceName(new_instance_name.clone()));
242 }
243
244 let Some((_dimension_type, dimension_data)) = p
245 .common
246 .dimension_type(&instance_holder.instance.read().registries)
247 else {
248 return;
249 };
250
251 let weak_instance = instance_container.get_or_insert(
254 new_instance_name.clone(),
255 dimension_data.height,
256 dimension_data.min_y,
257 &instance_holder.instance.read().registries,
258 );
259 instance_loaded_events.write(InstanceLoadedEvent {
260 entity: self.player,
261 name: new_instance_name.clone(),
262 instance: Arc::downgrade(&weak_instance),
263 });
264
265 *instance_holder.partial_instance.write() = PartialInstance::new(
270 azalea_world::chunk_storage::calculate_chunk_storage_range(
271 client_information.view_distance.into(),
272 ),
273 Some(self.player),
276 );
277 {
278 let map = instance_holder.instance.read().registries.map.clone();
279 let new_registries = &mut weak_instance.write().registries;
280 for (registry_name, registry) in map {
282 new_registries.map.insert(registry_name, registry);
283 }
284 }
285 instance_holder.instance = weak_instance;
286
287 let entity_bundle = EntityBundle::new(
288 game_profile.uuid,
289 Vec3::ZERO,
290 azalea_registry::EntityKind::Player,
291 new_instance_name,
292 );
293 let entity_id = p.player_id;
294 commands.entity(self.player).insert((
296 entity_id,
297 LocalGameMode {
298 current: p.common.game_type,
299 previous: p.common.previous_game_type.into(),
300 },
301 entity_bundle,
302 TicksConnected(0),
303 ));
304
305 azalea_entity::indexing::add_entity_to_indexes(
306 entity_id,
307 self.player,
308 Some(game_profile.uuid),
309 &mut entity_id_index,
310 &mut entity_uuid_index,
311 &mut instance_holder.instance.write(),
312 );
313
314 for mut loaded_by in &mut loaded_by_query.iter_mut() {
316 loaded_by.remove(&self.player);
317 }
318
319 if let Some(mut loaded_by) = loaded_by {
321 loaded_by.insert(self.player);
322 } else {
323 commands
324 .entity(self.player)
325 .insert(LoadedBy(HashSet::from_iter(vec![self.player])));
326 }
327 },
328 );
329 }
330
331 pub fn set_chunk_cache_radius(&mut self, p: &ClientboundSetChunkCacheRadius) {
332 debug!("Got set chunk cache radius packet {p:?}");
333 }
334
335 pub fn chunk_batch_start(&mut self, _p: &ClientboundChunkBatchStart) {
336 debug!("Got chunk batch start");
339
340 as_system::<EventWriter<_>>(self.ecs, |mut events| {
341 events.write(chunks::ChunkBatchStartEvent {
342 entity: self.player,
343 });
344 });
345 }
346
347 pub fn chunk_batch_finished(&mut self, p: &ClientboundChunkBatchFinished) {
348 debug!("Got chunk batch finished {p:?}");
349
350 as_system::<EventWriter<_>>(self.ecs, |mut events| {
351 events.write(chunks::ChunkBatchFinishedEvent {
352 entity: self.player,
353 batch_size: p.batch_size,
354 });
355 });
356 }
357
358 pub fn custom_payload(&mut self, p: &ClientboundCustomPayload) {
359 debug!("Got custom payload packet {p:?}");
360 }
361
362 pub fn change_difficulty(&mut self, p: &ClientboundChangeDifficulty) {
363 debug!("Got difficulty packet {p:?}");
364 }
365
366 pub fn commands(&mut self, _p: &ClientboundCommands) {
367 debug!("Got declare commands packet");
368 }
369
370 pub fn player_abilities(&mut self, p: &ClientboundPlayerAbilities) {
371 debug!("Got player abilities packet {p:?}");
372
373 as_system::<Query<&mut PlayerAbilities>>(self.ecs, |mut query| {
374 let mut player_abilities = query.get_mut(self.player).unwrap();
375
376 *player_abilities = PlayerAbilities::from(p);
377 });
378 }
379
380 pub fn set_cursor_item(&mut self, p: &ClientboundSetCursorItem) {
381 debug!("Got set cursor item packet {p:?}");
382 }
383
384 pub fn update_tags(&mut self, _p: &ClientboundUpdateTags) {
385 debug!("Got update tags packet");
386 }
387
388 pub fn disconnect(&mut self, p: &ClientboundDisconnect) {
389 warn!("Got disconnect packet {p:?}");
390
391 as_system::<EventWriter<_>>(self.ecs, |mut events| {
392 events.write(DisconnectEvent {
393 entity: self.player,
394 reason: Some(p.reason.clone()),
395 });
396 });
397 }
398
399 pub fn update_recipes(&mut self, _p: &ClientboundUpdateRecipes) {
400 debug!("Got update recipes packet");
401 }
402
403 pub fn entity_event(&mut self, _p: &ClientboundEntityEvent) {
404 }
406
407 pub fn player_position(&mut self, p: &ClientboundPlayerPosition) {
408 debug!("Got player position packet {p:?}");
409
410 as_system::<(
411 Query<(&mut Physics, &mut LookDirection, &mut Position)>,
412 Commands,
413 )>(self.ecs, |(mut query, mut commands)| {
414 let Ok((mut physics, mut direction, mut position)) = query.get_mut(self.player) else {
415 return;
416 };
417
418 p.relative
419 .apply(&p.change, &mut position, &mut direction, &mut physics);
420 physics.set_old_pos(*position);
422
423 commands.trigger(SendPacketEvent::new(
425 self.player,
426 ServerboundAcceptTeleportation { id: p.id },
427 ));
428 commands.trigger(SendPacketEvent::new(
429 self.player,
430 ServerboundMovePlayerPosRot {
431 pos: **position,
432 look_direction: *direction,
433 flags: MoveFlags::default(),
434 },
435 ));
436 });
437 }
438
439 pub fn player_info_update(&mut self, p: &ClientboundPlayerInfoUpdate) {
440 debug!("Got player info packet {p:?}");
441
442 as_system::<(
443 Query<&mut TabList>,
444 EventWriter<AddPlayerEvent>,
445 EventWriter<UpdatePlayerEvent>,
446 ResMut<TabList>,
447 )>(
448 self.ecs,
449 |(
450 mut query,
451 mut add_player_events,
452 mut update_player_events,
453 mut tab_list_resource,
454 )| {
455 let mut tab_list = query.get_mut(self.player).unwrap();
456
457 for updated_info in &p.entries {
458 if p.actions.add_player {
460 let info = PlayerInfo {
461 profile: updated_info.profile.clone(),
462 uuid: updated_info.profile.uuid,
463 gamemode: updated_info.game_mode,
464 latency: updated_info.latency,
465 display_name: updated_info.display_name.clone(),
466 };
467 tab_list.insert(updated_info.profile.uuid, info.clone());
468 add_player_events.write(AddPlayerEvent {
469 entity: self.player,
470 info,
471 });
472 } else if let Some(info) = tab_list.get_mut(&updated_info.profile.uuid) {
473 if p.actions.update_game_mode {
476 info.gamemode = updated_info.game_mode;
477 }
478 if p.actions.update_latency {
479 info.latency = updated_info.latency;
480 }
481 if p.actions.update_display_name {
482 info.display_name.clone_from(&updated_info.display_name);
483 }
484 update_player_events.write(UpdatePlayerEvent {
485 entity: self.player,
486 info: info.clone(),
487 });
488 } else {
489 let uuid = updated_info.profile.uuid;
490 debug!("Ignoring PlayerInfoUpdate for unknown player {uuid}");
491 }
492 }
493
494 *tab_list_resource = tab_list.clone();
495 },
496 );
497 }
498
499 pub fn player_info_remove(&mut self, p: &ClientboundPlayerInfoRemove) {
500 debug!("Got chunk cache center packet {p:?}");
501
502 as_system::<(
503 Query<&mut TabList>,
504 EventWriter<RemovePlayerEvent>,
505 ResMut<TabList>,
506 )>(
507 self.ecs,
508 |(mut query, mut remove_player_events, mut tab_list_resource)| {
509 let mut tab_list = query.get_mut(self.player).unwrap();
510
511 for uuid in &p.profile_ids {
512 if let Some(info) = tab_list.remove(uuid) {
513 remove_player_events.write(RemovePlayerEvent {
514 entity: self.player,
515 info,
516 });
517 }
518 tab_list_resource.remove(uuid);
519 }
520 },
521 );
522 }
523
524 pub fn set_chunk_cache_center(&mut self, p: &ClientboundSetChunkCacheCenter) {
525 debug!("Got chunk cache center packet {p:?}");
526
527 as_system::<Query<&InstanceHolder>>(self.ecs, |mut query| {
528 let instance_holder = query.get_mut(self.player).unwrap();
529 let mut partial_world = instance_holder.partial_instance.write();
530
531 partial_world
532 .chunks
533 .update_view_center(ChunkPos::new(p.x, p.z));
534 });
535 }
536
537 pub fn chunks_biomes(&mut self, _p: &ClientboundChunksBiomes) {}
538
539 pub fn light_update(&mut self, _p: &ClientboundLightUpdate) {
540 }
542
543 pub fn level_chunk_with_light(&mut self, p: &ClientboundLevelChunkWithLight) {
544 debug!("Got chunk with light packet {} {}", p.x, p.z);
545
546 as_system::<EventWriter<_>>(self.ecs, |mut events| {
547 events.write(chunks::ReceiveChunkEvent {
548 entity: self.player,
549 packet: p.clone(),
550 });
551 });
552 }
553
554 pub fn add_entity(&mut self, p: &ClientboundAddEntity) {
555 debug!("Got add entity packet {p:?}");
556
557 as_system::<(
558 Commands,
559 Query<(&mut EntityIdIndex, Option<&InstanceName>, Option<&TabList>)>,
560 Query<&mut LoadedBy>,
561 Query<Entity>,
562 Res<InstanceContainer>,
563 ResMut<EntityUuidIndex>,
564 )>(
565 self.ecs,
566 |(
567 mut commands,
568 mut query,
569 mut loaded_by_query,
570 entity_query,
571 instance_container,
572 mut entity_uuid_index,
573 )| {
574 let (mut entity_id_index, instance_name, tab_list) =
575 query.get_mut(self.player).unwrap();
576
577 let entity_id = p.id;
578
579 let Some(instance_name) = instance_name else {
580 warn!("got add player packet but we haven't gotten a login packet yet");
581 return;
582 };
583
584 let instance = instance_container.get(instance_name).unwrap();
586 if let Some(&ecs_entity) = instance.read().entity_by_id.get(&entity_id) {
587 let Ok(mut loaded_by) = loaded_by_query.get_mut(ecs_entity) else {
589 let entity_in_ecs = entity_query.get(ecs_entity).is_ok();
593
594 if entity_in_ecs {
595 error!(
596 "LoadedBy for entity {entity_id:?} ({ecs_entity:?}) isn't in the ecs, but the entity is in entity_by_id"
597 );
598 } else {
599 error!(
600 "Entity {entity_id:?} ({ecs_entity:?}) isn't in the ecs, but the entity is in entity_by_id"
601 );
602 }
603 return;
604 };
605 loaded_by.insert(self.player);
606
607 entity_id_index.insert(entity_id, ecs_entity);
609
610 debug!("added to LoadedBy of entity {ecs_entity:?} with id {entity_id:?}");
611 return;
612 };
613
614 let bundle = p.as_entity_bundle((**instance_name).clone());
617 let mut spawned =
618 commands.spawn((entity_id, LoadedBy(HashSet::from([self.player])), bundle));
619 let ecs_entity: Entity = spawned.id();
620 debug!(
621 "spawned entity {ecs_entity:?} with id {entity_id:?} at {pos:?}",
622 pos = p.position
623 );
624
625 azalea_entity::indexing::add_entity_to_indexes(
626 entity_id,
627 ecs_entity,
628 Some(p.uuid),
629 &mut entity_id_index,
630 &mut entity_uuid_index,
631 &mut instance.write(),
632 );
633
634 if let Some(tab_list) = tab_list {
636 if let Some(player_info) = tab_list.get(&p.uuid) {
640 spawned.insert(GameProfileComponent(player_info.profile.clone()));
641 }
642 }
643
644 p.apply_metadata(&mut spawned);
647 },
648 );
649 }
650
651 pub fn set_entity_data(&mut self, p: &ClientboundSetEntityData) {
652 as_system::<(
653 Commands,
654 Query<(&EntityIdIndex, &InstanceHolder)>,
655 Query<&EntityKindComponent>,
658 )>(self.ecs, |(mut commands, query, entity_kind_query)| {
659 let (entity_id_index, instance_holder) = query.get(self.player).unwrap();
660
661 let entity = entity_id_index.get_by_minecraft_entity(p.id);
662
663 let Some(entity) = entity else {
664 debug!(
666 "Server sent an entity data packet for an entity id ({}) that we don't know about",
667 p.id
668 );
669 return;
670 };
671
672 let Ok(entity_kind) = entity_kind_query.get(entity) else {
673 debug!(
674 "Server sent an entity data packet for an entity id ({}) that we have indexed as {entity} but they don't have EntityKind. Maybe a second local client that just disconnected?",
675 p.id
676 );
677 return;
678 };
679 let entity_kind = **entity_kind;
680
681 debug!("Got set entity data packet {p:?} for entity of kind {entity_kind:?}");
682
683 let packed_items = p.packed_items.clone().to_vec();
684
685 commands.entity(entity).queue(RelativeEntityUpdate::new(
688 instance_holder.partial_instance.clone(),
689 move |entity| {
690 let entity_id = entity.id();
691 entity.world_scope(|world| {
692 let mut commands_system_state = SystemState::<Commands>::new(world);
693 let mut commands = commands_system_state.get_mut(world);
694 let mut entity_commands = commands.entity(entity_id);
695 if let Err(e) =
696 apply_metadata(&mut entity_commands, entity_kind, packed_items)
697 {
698 warn!("{e}");
699 }
700 commands_system_state.apply(world);
701 });
702 },
703 ));
704 });
705 }
706
707 pub fn update_attributes(&mut self, _p: &ClientboundUpdateAttributes) {
708 }
710
711 pub fn set_entity_motion(&mut self, p: &ClientboundSetEntityMotion) {
712 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
716 self.ecs,
717 |(mut commands, query)| {
718 let (entity_id_index, instance_holder) = query.get(self.player).unwrap();
719
720 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
721 debug!(
725 "Got set entity motion packet for unknown entity id {}",
726 p.id
727 );
728 return;
729 };
730
731 let knockback = KnockbackType::Set(Vec3 {
735 x: p.delta.xa as f64 / 8000.,
736 y: p.delta.ya as f64 / 8000.,
737 z: p.delta.za as f64 / 8000.,
738 });
739
740 commands.entity(entity).queue(RelativeEntityUpdate::new(
741 instance_holder.partial_instance.clone(),
742 move |entity_mut| {
743 entity_mut.world_scope(|world| {
744 world.send_event(KnockbackEvent { entity, knockback })
745 });
746 },
747 ));
748 },
749 );
750 }
751
752 pub fn set_entity_link(&mut self, p: &ClientboundSetEntityLink) {
753 debug!("Got set entity link packet {p:?}");
754 }
755
756 pub fn initialize_border(&mut self, p: &ClientboundInitializeBorder) {
757 debug!("Got initialize border packet {p:?}");
758 }
759
760 pub fn set_time(&mut self, _p: &ClientboundSetTime) {
761 }
763
764 pub fn set_default_spawn_position(&mut self, p: &ClientboundSetDefaultSpawnPosition) {
765 debug!("Got set default spawn position packet {p:?}");
766 }
767
768 pub fn set_health(&mut self, p: &ClientboundSetHealth) {
769 debug!("Got set health packet {p:?}");
770
771 as_system::<Query<(&mut Health, &mut Hunger)>>(self.ecs, |mut query| {
772 let (mut health, mut hunger) = query.get_mut(self.player).unwrap();
773
774 **health = p.health;
775 (hunger.food, hunger.saturation) = (p.food, p.saturation);
776
777 });
781 }
782
783 pub fn set_experience(&mut self, p: &ClientboundSetExperience) {
784 debug!("Got set experience packet {p:?}");
785 }
786
787 pub fn teleport_entity(&mut self, p: &ClientboundTeleportEntity) {
788 debug!("Got teleport entity packet {p:?}");
789
790 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
791 self.ecs,
792 |(mut commands, mut query)| {
793 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
794
795 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
796 warn!("Got teleport entity packet for unknown entity id {}", p.id);
797 return;
798 };
799
800 let relative = p.relative.clone();
801 let change = p.change.clone();
802
803 commands.entity(entity).queue(RelativeEntityUpdate::new(
804 instance_holder.partial_instance.clone(),
805 move |entity| {
806 let entity_id = entity.id();
807 entity.world_scope(move |world| {
808 let mut query =
809 world.query::<(&mut Physics, &mut LookDirection, &mut Position)>();
810 let (mut physics, mut look_direction, mut position) =
811 query.get_mut(world, entity_id).unwrap();
812 let old_position = *position;
813 relative.apply(
814 &change,
815 &mut position,
816 &mut look_direction,
817 &mut physics,
818 );
819 physics.set_old_pos(old_position);
821 });
822 },
823 ));
824 },
825 );
826 }
827
828 pub fn update_advancements(&mut self, p: &ClientboundUpdateAdvancements) {
829 debug!("Got update advancements packet {p:?}");
830 }
831
832 pub fn rotate_head(&mut self, _p: &ClientboundRotateHead) {}
833
834 pub fn move_entity_pos(&mut self, p: &ClientboundMoveEntityPos) {
835 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
836 self.ecs,
837 |(mut commands, mut query)| {
838 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
839
840 debug!("Got move entity pos packet {p:?}");
841
842 let entity_id = p.entity_id;
843 let Some(entity) = entity_id_index.get_by_minecraft_entity(entity_id) else {
844 debug!("Got move entity pos packet for unknown entity id {entity_id}");
845 return;
846 };
847
848 let new_delta = p.delta.clone();
849 let new_on_ground = p.on_ground;
850 commands.entity(entity).queue(RelativeEntityUpdate::new(
851 instance_holder.partial_instance.clone(),
852 move |entity_mut| {
853 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
854 let new_pos = physics.vec_delta_codec.decode(&new_delta);
855 physics.vec_delta_codec.set_base(new_pos);
856 physics.set_on_ground(new_on_ground);
857
858 let mut position = entity_mut.get_mut::<Position>().unwrap();
859 if new_pos != **position {
860 **position = new_pos;
861 }
862
863 trace!(
864 "Applied movement update for {entity_id} / {entity}",
865 entity = entity_mut.id()
866 );
867 },
868 ));
869 },
870 );
871 }
872
873 pub fn move_entity_pos_rot(&mut self, p: &ClientboundMoveEntityPosRot) {
874 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
875 self.ecs,
876 |(mut commands, mut query)| {
877 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
878
879 debug!("Got move entity pos rot packet {p:?}");
880
881 let entity = entity_id_index.get_by_minecraft_entity(p.entity_id);
882
883 let Some(entity) = entity else {
884 debug!(
886 "Got move entity pos rot packet for unknown entity id {}",
887 p.entity_id
888 );
889 return;
890 };
891
892 let new_delta = p.delta.clone();
893 let new_look_direction = LookDirection::new(
894 (p.y_rot as i32 * 360) as f32 / 256.,
895 (p.x_rot as i32 * 360) as f32 / 256.,
896 );
897
898 let new_on_ground = p.on_ground;
899
900 commands.entity(entity).queue(RelativeEntityUpdate::new(
901 instance_holder.partial_instance.clone(),
902 move |entity_mut| {
903 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
904 let new_position = physics.vec_delta_codec.decode(&new_delta);
905 physics.vec_delta_codec.set_base(new_position);
906 physics.set_on_ground(new_on_ground);
907
908 let mut position = entity_mut.get_mut::<Position>().unwrap();
909 if new_position != **position {
910 **position = new_position;
911 }
912
913 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
914 if new_look_direction != *look_direction {
915 *look_direction = new_look_direction;
916 }
917 },
918 ));
919 },
920 );
921 }
922
923 pub fn move_entity_rot(&mut self, p: &ClientboundMoveEntityRot) {
924 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
925 self.ecs,
926 |(mut commands, mut query)| {
927 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
928
929 let entity = entity_id_index.get_by_minecraft_entity(p.entity_id);
930 if let Some(entity) = entity {
931 let new_look_direction = LookDirection::new(
932 (p.y_rot as i32 * 360) as f32 / 256.,
933 (p.x_rot as i32 * 360) as f32 / 256.,
934 );
935 let new_on_ground = p.on_ground;
936
937 commands.entity(entity).queue(RelativeEntityUpdate::new(
938 instance_holder.partial_instance.clone(),
939 move |entity_mut| {
940 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
941 physics.set_on_ground(new_on_ground);
942
943 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
944 if new_look_direction != *look_direction {
945 *look_direction = new_look_direction;
946 }
947 },
948 ));
949 } else {
950 warn!(
951 "Got move entity rot packet for unknown entity id {}",
952 p.entity_id
953 );
954 }
955 },
956 );
957 }
958 pub fn keep_alive(&mut self, p: &ClientboundKeepAlive) {
959 debug!("Got keep alive packet {p:?} for {:?}", self.player);
960
961 as_system::<(EventWriter<KeepAliveEvent>, Commands)>(
962 self.ecs,
963 |(mut keepalive_events, mut commands)| {
964 keepalive_events.write(KeepAliveEvent {
965 entity: self.player,
966 id: p.id,
967 });
968 commands.trigger(SendPacketEvent::new(
969 self.player,
970 ServerboundKeepAlive { id: p.id },
971 ));
972 },
973 );
974 }
975
976 pub fn remove_entities(&mut self, p: &ClientboundRemoveEntities) {
977 debug!("Got remove entities packet {p:?}");
978
979 as_system::<(Query<&mut EntityIdIndex>, Query<&mut LoadedBy>)>(
980 self.ecs,
981 |(mut query, mut entity_query)| {
982 let Ok(mut entity_id_index) = query.get_mut(self.player) else {
983 warn!("our local player doesn't have EntityIdIndex");
984 return;
985 };
986
987 for &id in &p.entity_ids {
988 let Some(entity) = entity_id_index.remove_by_minecraft_entity(id) else {
989 debug!(
990 "Tried to remove entity with id {id} but it wasn't in the EntityIdIndex. This may be expected on certain server setups (like if they're using VeryManyPlayers)."
991 );
992 continue;
993 };
994 let Ok(mut loaded_by) = entity_query.get_mut(entity) else {
995 warn!(
996 "tried to despawn entity {id} but it doesn't have a LoadedBy component",
997 );
998 continue;
999 };
1000
1001 loaded_by.remove(&self.player);
1008 }
1009 },
1010 );
1011 }
1012 pub fn player_chat(&mut self, p: &ClientboundPlayerChat) {
1013 debug!("Got player chat packet {p:?}");
1014
1015 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1016 events.write(ChatReceivedEvent {
1017 entity: self.player,
1018 packet: ChatPacket::Player(Arc::new(p.clone())),
1019 });
1020 });
1021 }
1022
1023 pub fn system_chat(&mut self, p: &ClientboundSystemChat) {
1024 debug!("Got system chat packet {p:?}");
1025
1026 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1027 events.write(ChatReceivedEvent {
1028 entity: self.player,
1029 packet: ChatPacket::System(Arc::new(p.clone())),
1030 });
1031 });
1032 }
1033
1034 pub fn disguised_chat(&mut self, p: &ClientboundDisguisedChat) {
1035 debug!("Got disguised chat packet {p:?}");
1036
1037 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1038 events.write(ChatReceivedEvent {
1039 entity: self.player,
1040 packet: ChatPacket::Disguised(Arc::new(p.clone())),
1041 });
1042 });
1043 }
1044
1045 pub fn sound(&mut self, _p: &ClientboundSound) {}
1046
1047 pub fn level_event(&mut self, p: &ClientboundLevelEvent) {
1048 debug!("Got level event packet {p:?}");
1049 }
1050
1051 pub fn block_update(&mut self, p: &ClientboundBlockUpdate) {
1052 debug!("Got block update packet {p:?}");
1053
1054 as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
1055 let mut queued = query.get_mut(self.player).unwrap();
1056 queued.list.push((p.pos, p.block_state));
1057 });
1058 }
1059
1060 pub fn animate(&mut self, p: &ClientboundAnimate) {
1061 debug!("Got animate packet {p:?}");
1062 }
1063
1064 pub fn section_blocks_update(&mut self, p: &ClientboundSectionBlocksUpdate) {
1065 debug!("Got section blocks update packet {p:?}");
1066
1067 as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
1068 let mut queued = query.get_mut(self.player).unwrap();
1069 for new_state in &p.states {
1070 let pos = p.section_pos + new_state.pos;
1071 queued.list.push((pos, new_state.state));
1072 }
1073 });
1074 }
1075
1076 pub fn game_event(&mut self, p: &ClientboundGameEvent) {
1077 use azalea_protocol::packets::game::c_game_event::EventType;
1078
1079 debug!("Got game event packet {p:?}");
1080
1081 #[allow(clippy::single_match)]
1082 match p.event {
1083 EventType::ChangeGameMode => {
1084 as_system::<Query<&mut LocalGameMode>>(self.ecs, |mut query| {
1085 let mut local_game_mode = query.get_mut(self.player).unwrap();
1086 if let Some(new_game_mode) = GameMode::from_id(p.param as u8) {
1087 local_game_mode.current = new_game_mode;
1088 }
1089 });
1090 }
1091 _ => {}
1092 }
1093 }
1094
1095 pub fn level_particles(&mut self, p: &ClientboundLevelParticles) {
1096 debug!("Got level particles packet {p:?}");
1097 }
1098
1099 pub fn server_data(&mut self, p: &ClientboundServerData) {
1100 debug!("Got server data packet {p:?}");
1101 }
1102
1103 pub fn set_equipment(&mut self, p: &ClientboundSetEquipment) {
1104 debug!("Got set equipment packet {p:?}");
1105 }
1106
1107 pub fn update_mob_effect(&mut self, p: &ClientboundUpdateMobEffect) {
1108 debug!("Got update mob effect packet {p:?}");
1109 }
1110
1111 pub fn award_stats(&mut self, _p: &ClientboundAwardStats) {}
1112
1113 pub fn block_changed_ack(&mut self, p: &ClientboundBlockChangedAck) {
1114 as_system::<Query<(&InstanceHolder, &mut BlockStatePredictionHandler)>>(
1115 self.ecs,
1116 |mut query| {
1117 let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
1118 let world = local_player.instance.read();
1119 prediction_handler.end_prediction_up_to(p.seq, &world);
1120 },
1121 );
1122 }
1123
1124 pub fn block_destruction(&mut self, _p: &ClientboundBlockDestruction) {}
1125
1126 pub fn block_entity_data(&mut self, _p: &ClientboundBlockEntityData) {}
1127
1128 pub fn block_event(&mut self, p: &ClientboundBlockEvent) {
1129 debug!("Got block event packet {p:?}");
1130 }
1131
1132 pub fn boss_event(&mut self, _p: &ClientboundBossEvent) {}
1133
1134 pub fn command_suggestions(&mut self, _p: &ClientboundCommandSuggestions) {}
1135
1136 pub fn container_set_content(&mut self, p: &ClientboundContainerSetContent) {
1137 debug!("Got container set content packet {p:?}");
1138
1139 as_system::<(Query<&mut Inventory>, EventWriter<_>)>(
1140 self.ecs,
1141 |(mut query, mut events)| {
1142 let mut inventory = query.get_mut(self.player).unwrap();
1143
1144 if p.container_id == 0 {
1146 for (i, slot) in p.items.iter().enumerate() {
1148 if let Some(slot_mut) = inventory.inventory_menu.slot_mut(i) {
1149 *slot_mut = slot.clone();
1150 }
1151 }
1152 } else {
1153 events.write(SetContainerContentEvent {
1154 entity: self.player,
1155 slots: p.items.clone(),
1156 container_id: p.container_id,
1157 });
1158 }
1159 },
1160 );
1161 }
1162
1163 pub fn container_set_data(&mut self, p: &ClientboundContainerSetData) {
1164 debug!("Got container set data packet {p:?}");
1165
1166 }
1175
1176 pub fn container_set_slot(&mut self, p: &ClientboundContainerSetSlot) {
1177 debug!("Got container set slot packet {p:?}");
1178
1179 as_system::<Query<&mut Inventory>>(self.ecs, |mut query| {
1180 let mut inventory = query.get_mut(self.player).unwrap();
1181
1182 if p.container_id == -1 {
1183 inventory.carried = p.item_stack.clone();
1185 } else if p.container_id == -2 {
1186 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1187 *slot = p.item_stack.clone();
1188 }
1189 } else {
1190 let is_creative_mode_and_inventory_closed = false;
1191 if p.container_id == 0 && azalea_inventory::Player::is_hotbar_slot(p.slot.into()) {
1194 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1197 *slot = p.item_stack.clone();
1198 }
1199 } else if p.container_id == inventory.id
1200 && (p.container_id != 0 || !is_creative_mode_and_inventory_closed)
1201 {
1202 if let Some(slot) = inventory.menu_mut().slot_mut(p.slot.into()) {
1204 *slot = p.item_stack.clone();
1205 inventory.state_id = p.state_id;
1206 }
1207 }
1208 }
1209 });
1210 }
1211
1212 pub fn container_close(&mut self, p: &ClientboundContainerClose) {
1213 debug!("Got container close packet {p:?}");
1217
1218 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1219 events.write(ClientSideCloseContainerEvent {
1220 entity: self.player,
1221 });
1222 });
1223 }
1224
1225 pub fn cooldown(&mut self, _p: &ClientboundCooldown) {}
1226
1227 pub fn custom_chat_completions(&mut self, _p: &ClientboundCustomChatCompletions) {}
1228
1229 pub fn delete_chat(&mut self, _p: &ClientboundDeleteChat) {}
1230
1231 pub fn explode(&mut self, p: &ClientboundExplode) {
1232 trace!("Got explode packet {p:?}");
1233
1234 as_system::<EventWriter<_>>(self.ecs, |mut knockback_events| {
1235 if let Some(knockback) = p.knockback {
1236 knockback_events.write(KnockbackEvent {
1237 entity: self.player,
1238 knockback: KnockbackType::Set(knockback),
1239 });
1240 }
1241 });
1242 }
1243
1244 pub fn forget_level_chunk(&mut self, p: &ClientboundForgetLevelChunk) {
1245 debug!("Got forget level chunk packet {p:?}");
1246
1247 as_system::<Query<&InstanceHolder>>(self.ecs, |mut query| {
1248 let local_player = query.get_mut(self.player).unwrap();
1249
1250 let mut partial_instance = local_player.partial_instance.write();
1251
1252 partial_instance.chunks.limited_set(&p.pos, None);
1253 });
1254 }
1255
1256 pub fn horse_screen_open(&mut self, _p: &ClientboundHorseScreenOpen) {}
1257
1258 pub fn map_item_data(&mut self, _p: &ClientboundMapItemData) {}
1259
1260 pub fn merchant_offers(&mut self, _p: &ClientboundMerchantOffers) {}
1261
1262 pub fn move_vehicle(&mut self, _p: &ClientboundMoveVehicle) {}
1263
1264 pub fn open_book(&mut self, _p: &ClientboundOpenBook) {}
1265
1266 pub fn open_screen(&mut self, p: &ClientboundOpenScreen) {
1267 debug!("Got open screen packet {p:?}");
1268
1269 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1270 events.write(MenuOpenedEvent {
1271 entity: self.player,
1272 window_id: p.container_id,
1273 menu_type: p.menu_type,
1274 title: p.title.to_owned(),
1275 });
1276 });
1277 }
1278
1279 pub fn open_sign_editor(&mut self, _p: &ClientboundOpenSignEditor) {}
1280
1281 pub fn ping(&mut self, p: &ClientboundPing) {
1282 debug!("Got ping packet {p:?}");
1283
1284 as_system::<Commands>(self.ecs, |mut commands| {
1285 commands.trigger_targets(PingEvent(p.clone()), self.player);
1286 });
1287 }
1288
1289 pub fn place_ghost_recipe(&mut self, _p: &ClientboundPlaceGhostRecipe) {}
1290
1291 pub fn player_combat_end(&mut self, _p: &ClientboundPlayerCombatEnd) {}
1292
1293 pub fn player_combat_enter(&mut self, _p: &ClientboundPlayerCombatEnter) {}
1294
1295 pub fn player_combat_kill(&mut self, p: &ClientboundPlayerCombatKill) {
1296 debug!("Got player kill packet {p:?}");
1297
1298 as_system::<(
1299 Commands,
1300 Query<(&MinecraftEntityId, Option<&Dead>)>,
1301 EventWriter<_>,
1302 )>(self.ecs, |(mut commands, mut query, mut events)| {
1303 let (entity_id, dead) = query.get_mut(self.player).unwrap();
1304
1305 if *entity_id == p.player_id && dead.is_none() {
1306 commands.entity(self.player).insert(Dead);
1307 events.write(DeathEvent {
1308 entity: self.player,
1309 packet: Some(p.clone()),
1310 });
1311 }
1312 });
1313 }
1314
1315 pub fn player_look_at(&mut self, _p: &ClientboundPlayerLookAt) {}
1316
1317 pub fn remove_mob_effect(&mut self, _p: &ClientboundRemoveMobEffect) {}
1318
1319 pub fn resource_pack_push(&mut self, p: &ClientboundResourcePackPush) {
1320 debug!("Got resource pack packet {p:?}");
1321
1322 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1323 events.write(ResourcePackEvent {
1324 entity: self.player,
1325 id: p.id,
1326 url: p.url.to_owned(),
1327 hash: p.hash.to_owned(),
1328 required: p.required,
1329 prompt: p.prompt.to_owned(),
1330 });
1331 });
1332 }
1333
1334 pub fn resource_pack_pop(&mut self, _p: &ClientboundResourcePackPop) {}
1335
1336 pub fn respawn(&mut self, p: &ClientboundRespawn) {
1337 debug!("Got respawn packet {p:?}");
1338
1339 as_system::<(
1340 Commands,
1341 Query<
1342 (
1343 &mut InstanceHolder,
1344 &GameProfileComponent,
1345 &ClientInformation,
1346 Option<&mut InstanceName>,
1347 ),
1348 With<LocalEntity>,
1349 >,
1350 EventWriter<_>,
1351 ResMut<InstanceContainer>,
1352 Query<&mut LoadedBy, Without<LocalEntity>>,
1353 )>(
1354 self.ecs,
1355 |(mut commands, mut query, mut events, mut instance_container, mut loaded_by_query)| {
1356 let Ok((mut instance_holder, game_profile, client_information, instance_name)) =
1357 query.get_mut(self.player)
1358 else {
1359 warn!("Got respawn packet but player doesn't have the required components");
1360 return;
1361 };
1362
1363 let new_instance_name = p.common.dimension.clone();
1364
1365 if let Some(mut instance_name) = instance_name {
1366 **instance_name = new_instance_name.clone();
1367 } else {
1368 commands
1369 .entity(self.player)
1370 .insert(InstanceName(new_instance_name.clone()));
1371 }
1372
1373 let Some((_dimension_type, dimension_data)) = p
1374 .common
1375 .dimension_type(&instance_holder.instance.read().registries)
1376 else {
1377 return;
1378 };
1379
1380 let weak_instance = instance_container.get_or_insert(
1383 new_instance_name.clone(),
1384 dimension_data.height,
1385 dimension_data.min_y,
1386 &instance_holder.instance.read().registries,
1387 );
1388 events.write(InstanceLoadedEvent {
1389 entity: self.player,
1390 name: new_instance_name.clone(),
1391 instance: Arc::downgrade(&weak_instance),
1392 });
1393
1394 *instance_holder.partial_instance.write() = PartialInstance::new(
1399 azalea_world::chunk_storage::calculate_chunk_storage_range(
1400 client_information.view_distance.into(),
1401 ),
1402 Some(self.player),
1403 );
1404 instance_holder.instance = weak_instance;
1405
1406 for mut loaded_by in &mut loaded_by_query.iter_mut() {
1408 loaded_by.remove(&self.player);
1409 }
1410
1411 let entity_bundle = EntityBundle::new(
1413 game_profile.uuid,
1414 Vec3::ZERO,
1415 azalea_registry::EntityKind::Player,
1416 new_instance_name,
1417 );
1418 commands.entity(self.player).insert((
1420 LocalGameMode {
1421 current: p.common.game_type,
1422 previous: p.common.previous_game_type.into(),
1423 },
1424 entity_bundle,
1425 ));
1426
1427 commands
1428 .entity(self.player)
1429 .remove::<(Dead, HasClientLoaded)>();
1430 },
1431 )
1432 }
1433
1434 pub fn start_configuration(&mut self, _p: &ClientboundStartConfiguration) {
1435 debug!("Got start configuration packet");
1436
1437 as_system::<(Commands, Query<(&mut RawConnection, &mut InstanceHolder)>)>(
1438 self.ecs,
1439 |(mut commands, mut query)| {
1440 let Some((mut raw_conn, mut instance_holder)) = query.get_mut(self.player).ok()
1441 else {
1442 warn!("Got start configuration packet but player doesn't have a RawConnection");
1443 return;
1444 };
1445 raw_conn.state = ConnectionProtocol::Configuration;
1446
1447 commands.trigger(SendPacketEvent::new(
1448 self.player,
1449 ServerboundConfigurationAcknowledged,
1450 ));
1451
1452 commands
1453 .entity(self.player)
1454 .insert(crate::client::InConfigState)
1455 .remove::<crate::JoinedClientBundle>()
1456 .remove::<EntityBundle>();
1457
1458 instance_holder.reset();
1459 },
1460 );
1461 }
1462
1463 pub fn entity_position_sync(&mut self, p: &ClientboundEntityPositionSync) {
1464 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
1465 self.ecs,
1466 |(mut commands, mut query)| {
1467 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
1468
1469 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
1470 debug!("Got teleport entity packet for unknown entity id {}", p.id);
1471 return;
1472 };
1473
1474 let new_position = p.values.pos;
1475 let new_on_ground = p.on_ground;
1476 let new_look_direction = p.values.look_direction;
1477
1478 commands.entity(entity).queue(RelativeEntityUpdate::new(
1479 instance_holder.partial_instance.clone(),
1480 move |entity_mut| {
1481 let is_local_entity = entity_mut.get::<LocalEntity>().is_some();
1482 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
1483
1484 physics.vec_delta_codec.set_base(new_position);
1485
1486 if is_local_entity {
1487 debug!("Ignoring entity position sync packet for local player");
1488 return;
1489 }
1490
1491 physics.set_on_ground(new_on_ground);
1492
1493 let mut position = entity_mut.get_mut::<Position>().unwrap();
1494 **position = new_position;
1495
1496 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
1497 *look_direction = new_look_direction;
1498 },
1499 ));
1500 },
1501 );
1502 }
1503
1504 pub fn select_advancements_tab(&mut self, _p: &ClientboundSelectAdvancementsTab) {}
1505 pub fn set_action_bar_text(&mut self, _p: &ClientboundSetActionBarText) {}
1506 pub fn set_border_center(&mut self, _p: &ClientboundSetBorderCenter) {}
1507 pub fn set_border_lerp_size(&mut self, _p: &ClientboundSetBorderLerpSize) {}
1508 pub fn set_border_size(&mut self, _p: &ClientboundSetBorderSize) {}
1509 pub fn set_border_warning_delay(&mut self, _p: &ClientboundSetBorderWarningDelay) {}
1510 pub fn set_border_warning_distance(&mut self, _p: &ClientboundSetBorderWarningDistance) {}
1511 pub fn set_camera(&mut self, _p: &ClientboundSetCamera) {}
1512 pub fn set_display_objective(&mut self, _p: &ClientboundSetDisplayObjective) {}
1513 pub fn set_objective(&mut self, _p: &ClientboundSetObjective) {}
1514 pub fn set_passengers(&mut self, _p: &ClientboundSetPassengers) {}
1515 pub fn set_player_team(&mut self, p: &ClientboundSetPlayerTeam) {
1516 debug!("Got set player team packet {p:?}");
1517 }
1518 pub fn set_score(&mut self, _p: &ClientboundSetScore) {}
1519 pub fn set_simulation_distance(&mut self, _p: &ClientboundSetSimulationDistance) {}
1520 pub fn set_subtitle_text(&mut self, _p: &ClientboundSetSubtitleText) {}
1521 pub fn set_title_text(&mut self, _p: &ClientboundSetTitleText) {}
1522 pub fn set_titles_animation(&mut self, _p: &ClientboundSetTitlesAnimation) {}
1523 pub fn clear_titles(&mut self, _p: &ClientboundClearTitles) {}
1524 pub fn sound_entity(&mut self, _p: &ClientboundSoundEntity) {}
1525 pub fn stop_sound(&mut self, _p: &ClientboundStopSound) {}
1526 pub fn tab_list(&mut self, _p: &ClientboundTabList) {}
1527 pub fn tag_query(&mut self, _p: &ClientboundTagQuery) {}
1528 pub fn take_item_entity(&mut self, _p: &ClientboundTakeItemEntity) {}
1529 pub fn bundle_delimiter(&mut self, _p: &ClientboundBundleDelimiter) {}
1530 pub fn damage_event(&mut self, _p: &ClientboundDamageEvent) {}
1531 pub fn hurt_animation(&mut self, _p: &ClientboundHurtAnimation) {}
1532 pub fn ticking_state(&mut self, _p: &ClientboundTickingState) {}
1533 pub fn ticking_step(&mut self, _p: &ClientboundTickingStep) {}
1534 pub fn reset_score(&mut self, _p: &ClientboundResetScore) {}
1535 pub fn cookie_request(&mut self, _p: &ClientboundCookieRequest) {}
1536 pub fn debug_sample(&mut self, _p: &ClientboundDebugSample) {}
1537 pub fn pong_response(&mut self, _p: &ClientboundPongResponse) {}
1538 pub fn store_cookie(&mut self, _p: &ClientboundStoreCookie) {}
1539 pub fn transfer(&mut self, _p: &ClientboundTransfer) {}
1540 pub fn move_minecart_along_track(&mut self, _p: &ClientboundMoveMinecartAlongTrack) {}
1541 pub fn set_held_slot(&mut self, p: &ClientboundSetHeldSlot) {
1542 debug!("Got set held slot packet {p:?}");
1543
1544 as_system::<Query<&mut Inventory>>(self.ecs, |mut query| {
1545 let mut inventory = query.get_mut(self.player).unwrap();
1546 if p.slot <= 8 {
1547 inventory.selected_hotbar_slot = p.slot as u8;
1548 }
1549 });
1550 }
1551 pub fn set_player_inventory(&mut self, _p: &ClientboundSetPlayerInventory) {}
1552 pub fn projectile_power(&mut self, _p: &ClientboundProjectilePower) {}
1553 pub fn custom_report_details(&mut self, _p: &ClientboundCustomReportDetails) {}
1554 pub fn server_links(&mut self, _p: &ClientboundServerLinks) {}
1555 pub fn player_rotation(&mut self, _p: &ClientboundPlayerRotation) {}
1556 pub fn recipe_book_add(&mut self, _p: &ClientboundRecipeBookAdd) {}
1557 pub fn recipe_book_remove(&mut self, _p: &ClientboundRecipeBookRemove) {}
1558 pub fn recipe_book_settings(&mut self, _p: &ClientboundRecipeBookSettings) {}
1559 pub fn test_instance_block_status(&mut self, _p: &ClientboundTestInstanceBlockStatus) {}
1560 pub fn waypoint(&mut self, _p: &ClientboundWaypoint) {}
1561
1562 pub fn clear_dialog(&mut self, p: &ClientboundClearDialog) {
1563 debug!("Got clear dialog packet {p:?}");
1564 }
1565 pub fn show_dialog(&mut self, p: &ClientboundShowDialog) {
1566 debug!("Got show dialog packet {p:?}");
1567 }
1568}