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, LastSentPosition, LoadedBy, LocalEntity,
11 LookDirection, Physics, 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 loading::HasClientLoaded,
37 local_player::{Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, TabList},
38 movement::{KnockbackEvent, KnockbackType},
39 packet::as_system,
40 player::{GameProfileComponent, PlayerInfo},
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 ));
303
304 azalea_entity::indexing::add_entity_to_indexes(
305 entity_id,
306 self.player,
307 Some(game_profile.uuid),
308 &mut entity_id_index,
309 &mut entity_uuid_index,
310 &mut instance_holder.instance.write(),
311 );
312
313 for mut loaded_by in &mut loaded_by_query.iter_mut() {
315 loaded_by.remove(&self.player);
316 }
317
318 if let Some(mut loaded_by) = loaded_by {
320 loaded_by.insert(self.player);
321 } else {
322 commands
323 .entity(self.player)
324 .insert(LoadedBy(HashSet::from_iter(vec![self.player])));
325 }
326 },
327 );
328 }
329
330 pub fn set_chunk_cache_radius(&mut self, p: &ClientboundSetChunkCacheRadius) {
331 debug!("Got set chunk cache radius packet {p:?}");
332 }
333
334 pub fn chunk_batch_start(&mut self, _p: &ClientboundChunkBatchStart) {
335 debug!("Got chunk batch start");
338
339 as_system::<EventWriter<_>>(self.ecs, |mut events| {
340 events.write(chunks::ChunkBatchStartEvent {
341 entity: self.player,
342 });
343 });
344 }
345
346 pub fn chunk_batch_finished(&mut self, p: &ClientboundChunkBatchFinished) {
347 debug!("Got chunk batch finished {p:?}");
348
349 as_system::<EventWriter<_>>(self.ecs, |mut events| {
350 events.write(chunks::ChunkBatchFinishedEvent {
351 entity: self.player,
352 batch_size: p.batch_size,
353 });
354 });
355 }
356
357 pub fn custom_payload(&mut self, p: &ClientboundCustomPayload) {
358 debug!("Got custom payload packet {p:?}");
359 }
360
361 pub fn change_difficulty(&mut self, p: &ClientboundChangeDifficulty) {
362 debug!("Got difficulty packet {p:?}");
363 }
364
365 pub fn commands(&mut self, _p: &ClientboundCommands) {
366 debug!("Got declare commands packet");
367 }
368
369 pub fn player_abilities(&mut self, p: &ClientboundPlayerAbilities) {
370 debug!("Got player abilities packet {p:?}");
371
372 as_system::<Query<&mut PlayerAbilities>>(self.ecs, |mut query| {
373 let mut player_abilities = query.get_mut(self.player).unwrap();
374
375 *player_abilities = PlayerAbilities::from(p);
376 });
377 }
378
379 pub fn set_cursor_item(&mut self, p: &ClientboundSetCursorItem) {
380 debug!("Got set cursor item packet {p:?}");
381 }
382
383 pub fn update_tags(&mut self, _p: &ClientboundUpdateTags) {
384 debug!("Got update tags packet");
385 }
386
387 pub fn disconnect(&mut self, p: &ClientboundDisconnect) {
388 warn!("Got disconnect packet {p:?}");
389
390 as_system::<EventWriter<_>>(self.ecs, |mut events| {
391 events.write(DisconnectEvent {
392 entity: self.player,
393 reason: Some(p.reason.clone()),
394 });
395 });
396 }
397
398 pub fn update_recipes(&mut self, _p: &ClientboundUpdateRecipes) {
399 debug!("Got update recipes packet");
400 }
401
402 pub fn entity_event(&mut self, _p: &ClientboundEntityEvent) {
403 }
405
406 pub fn player_position(&mut self, p: &ClientboundPlayerPosition) {
407 debug!("Got player position packet {p:?}");
408
409 as_system::<(
410 Query<(
411 &mut Physics,
412 &mut LookDirection,
413 &mut Position,
414 &mut LastSentPosition,
415 )>,
416 Commands,
417 )>(self.ecs, |(mut query, mut commands)| {
418 let Ok((mut physics, mut direction, mut position, mut last_sent_position)) =
419 query.get_mut(self.player)
420 else {
421 return;
422 };
423
424 **last_sent_position = **position;
425
426 p.relative
427 .apply(&p.change, &mut position, &mut direction, &mut physics);
428 physics.set_old_pos(*position);
430
431 commands.trigger(SendPacketEvent::new(
433 self.player,
434 ServerboundAcceptTeleportation { id: p.id },
435 ));
436 commands.trigger(SendPacketEvent::new(
437 self.player,
438 ServerboundMovePlayerPosRot {
439 pos: **position,
440 look_direction: *direction,
441 flags: MoveFlags::default(),
442 },
443 ));
444 });
445 }
446
447 pub fn player_info_update(&mut self, p: &ClientboundPlayerInfoUpdate) {
448 debug!("Got player info packet {p:?}");
449
450 as_system::<(
451 Query<&mut TabList>,
452 EventWriter<AddPlayerEvent>,
453 EventWriter<UpdatePlayerEvent>,
454 ResMut<TabList>,
455 )>(
456 self.ecs,
457 |(
458 mut query,
459 mut add_player_events,
460 mut update_player_events,
461 mut tab_list_resource,
462 )| {
463 let mut tab_list = query.get_mut(self.player).unwrap();
464
465 for updated_info in &p.entries {
466 if p.actions.add_player {
468 let info = PlayerInfo {
469 profile: updated_info.profile.clone(),
470 uuid: updated_info.profile.uuid,
471 gamemode: updated_info.game_mode,
472 latency: updated_info.latency,
473 display_name: updated_info.display_name.clone(),
474 };
475 tab_list.insert(updated_info.profile.uuid, info.clone());
476 add_player_events.write(AddPlayerEvent {
477 entity: self.player,
478 info,
479 });
480 } else if let Some(info) = tab_list.get_mut(&updated_info.profile.uuid) {
481 if p.actions.update_game_mode {
484 info.gamemode = updated_info.game_mode;
485 }
486 if p.actions.update_latency {
487 info.latency = updated_info.latency;
488 }
489 if p.actions.update_display_name {
490 info.display_name.clone_from(&updated_info.display_name);
491 }
492 update_player_events.write(UpdatePlayerEvent {
493 entity: self.player,
494 info: info.clone(),
495 });
496 } else {
497 let uuid = updated_info.profile.uuid;
498 debug!("Ignoring PlayerInfoUpdate for unknown player {uuid}");
499 }
500 }
501
502 *tab_list_resource = tab_list.clone();
503 },
504 );
505 }
506
507 pub fn player_info_remove(&mut self, p: &ClientboundPlayerInfoRemove) {
508 debug!("Got chunk cache center packet {p:?}");
509
510 as_system::<(
511 Query<&mut TabList>,
512 EventWriter<RemovePlayerEvent>,
513 ResMut<TabList>,
514 )>(
515 self.ecs,
516 |(mut query, mut remove_player_events, mut tab_list_resource)| {
517 let mut tab_list = query.get_mut(self.player).unwrap();
518
519 for uuid in &p.profile_ids {
520 if let Some(info) = tab_list.remove(uuid) {
521 remove_player_events.write(RemovePlayerEvent {
522 entity: self.player,
523 info,
524 });
525 }
526 tab_list_resource.remove(uuid);
527 }
528 },
529 );
530 }
531
532 pub fn set_chunk_cache_center(&mut self, p: &ClientboundSetChunkCacheCenter) {
533 debug!("Got chunk cache center packet {p:?}");
534
535 as_system::<Query<&InstanceHolder>>(self.ecs, |mut query| {
536 let instance_holder = query.get_mut(self.player).unwrap();
537 let mut partial_world = instance_holder.partial_instance.write();
538
539 partial_world
540 .chunks
541 .update_view_center(ChunkPos::new(p.x, p.z));
542 });
543 }
544
545 pub fn chunks_biomes(&mut self, _p: &ClientboundChunksBiomes) {}
546
547 pub fn light_update(&mut self, _p: &ClientboundLightUpdate) {
548 }
550
551 pub fn level_chunk_with_light(&mut self, p: &ClientboundLevelChunkWithLight) {
552 debug!("Got chunk with light packet {} {}", p.x, p.z);
553
554 as_system::<EventWriter<_>>(self.ecs, |mut events| {
555 events.write(chunks::ReceiveChunkEvent {
556 entity: self.player,
557 packet: p.clone(),
558 });
559 });
560 }
561
562 pub fn add_entity(&mut self, p: &ClientboundAddEntity) {
563 debug!("Got add entity packet {p:?}");
564
565 as_system::<(
566 Commands,
567 Query<(&mut EntityIdIndex, Option<&InstanceName>, Option<&TabList>)>,
568 Query<&mut LoadedBy>,
569 Query<Entity>,
570 Res<InstanceContainer>,
571 ResMut<EntityUuidIndex>,
572 )>(
573 self.ecs,
574 |(
575 mut commands,
576 mut query,
577 mut loaded_by_query,
578 entity_query,
579 instance_container,
580 mut entity_uuid_index,
581 )| {
582 let (mut entity_id_index, instance_name, tab_list) =
583 query.get_mut(self.player).unwrap();
584
585 let entity_id = p.id;
586
587 let Some(instance_name) = instance_name else {
588 warn!("got add player packet but we haven't gotten a login packet yet");
589 return;
590 };
591
592 let instance = instance_container.get(instance_name).unwrap();
594 if let Some(&ecs_entity) = instance.read().entity_by_id.get(&entity_id) {
595 let Ok(mut loaded_by) = loaded_by_query.get_mut(ecs_entity) else {
597 let entity_in_ecs = entity_query.get(ecs_entity).is_ok();
601
602 if entity_in_ecs {
603 error!(
604 "LoadedBy for entity {entity_id:?} ({ecs_entity:?}) isn't in the ecs, but the entity is in entity_by_id"
605 );
606 } else {
607 error!(
608 "Entity {entity_id:?} ({ecs_entity:?}) isn't in the ecs, but the entity is in entity_by_id"
609 );
610 }
611 return;
612 };
613 loaded_by.insert(self.player);
614
615 entity_id_index.insert(entity_id, ecs_entity);
617
618 debug!("added to LoadedBy of entity {ecs_entity:?} with id {entity_id:?}");
619 return;
620 };
621
622 let bundle = p.as_entity_bundle((**instance_name).clone());
625 let mut spawned =
626 commands.spawn((entity_id, LoadedBy(HashSet::from([self.player])), bundle));
627 let ecs_entity: Entity = spawned.id();
628 debug!(
629 "spawned entity {ecs_entity:?} with id {entity_id:?} at {pos:?}",
630 pos = p.position
631 );
632
633 azalea_entity::indexing::add_entity_to_indexes(
634 entity_id,
635 ecs_entity,
636 Some(p.uuid),
637 &mut entity_id_index,
638 &mut entity_uuid_index,
639 &mut instance.write(),
640 );
641
642 if let Some(tab_list) = tab_list {
644 if let Some(player_info) = tab_list.get(&p.uuid) {
648 spawned.insert(GameProfileComponent(player_info.profile.clone()));
649 }
650 }
651
652 p.apply_metadata(&mut spawned);
655 },
656 );
657 }
658
659 pub fn set_entity_data(&mut self, p: &ClientboundSetEntityData) {
660 as_system::<(
661 Commands,
662 Query<(&EntityIdIndex, &InstanceHolder)>,
663 Query<&EntityKindComponent>,
666 )>(self.ecs, |(mut commands, query, entity_kind_query)| {
667 let (entity_id_index, instance_holder) = query.get(self.player).unwrap();
668
669 let entity = entity_id_index.get_by_minecraft_entity(p.id);
670
671 let Some(entity) = entity else {
672 debug!(
674 "Server sent an entity data packet for an entity id ({}) that we don't know about",
675 p.id
676 );
677 return;
678 };
679
680 let Ok(entity_kind) = entity_kind_query.get(entity) else {
681 debug!(
682 "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?",
683 p.id
684 );
685 return;
686 };
687 let entity_kind = **entity_kind;
688
689 debug!("Got set entity data packet {p:?} for entity of kind {entity_kind:?}");
690
691 let packed_items = p.packed_items.clone().to_vec();
692
693 commands.entity(entity).queue(RelativeEntityUpdate::new(
696 instance_holder.partial_instance.clone(),
697 move |entity| {
698 let entity_id = entity.id();
699 entity.world_scope(|world| {
700 let mut commands_system_state = SystemState::<Commands>::new(world);
701 let mut commands = commands_system_state.get_mut(world);
702 let mut entity_commands = commands.entity(entity_id);
703 if let Err(e) =
704 apply_metadata(&mut entity_commands, entity_kind, packed_items)
705 {
706 warn!("{e}");
707 }
708 commands_system_state.apply(world);
709 });
710 },
711 ));
712 });
713 }
714
715 pub fn update_attributes(&mut self, _p: &ClientboundUpdateAttributes) {
716 }
718
719 pub fn set_entity_motion(&mut self, p: &ClientboundSetEntityMotion) {
720 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
724 self.ecs,
725 |(mut commands, query)| {
726 let (entity_id_index, instance_holder) = query.get(self.player).unwrap();
727
728 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
729 debug!(
733 "Got set entity motion packet for unknown entity id {}",
734 p.id
735 );
736 return;
737 };
738
739 let knockback = KnockbackType::Set(Vec3 {
743 x: p.delta.xa as f64 / 8000.,
744 y: p.delta.ya as f64 / 8000.,
745 z: p.delta.za as f64 / 8000.,
746 });
747
748 commands.entity(entity).queue(RelativeEntityUpdate::new(
749 instance_holder.partial_instance.clone(),
750 move |entity_mut| {
751 entity_mut.world_scope(|world| {
752 world.send_event(KnockbackEvent { entity, knockback })
753 });
754 },
755 ));
756 },
757 );
758 }
759
760 pub fn set_entity_link(&mut self, p: &ClientboundSetEntityLink) {
761 debug!("Got set entity link packet {p:?}");
762 }
763
764 pub fn initialize_border(&mut self, p: &ClientboundInitializeBorder) {
765 debug!("Got initialize border packet {p:?}");
766 }
767
768 pub fn set_time(&mut self, _p: &ClientboundSetTime) {
769 }
771
772 pub fn set_default_spawn_position(&mut self, p: &ClientboundSetDefaultSpawnPosition) {
773 debug!("Got set default spawn position packet {p:?}");
774 }
775
776 pub fn set_health(&mut self, p: &ClientboundSetHealth) {
777 debug!("Got set health packet {p:?}");
778
779 as_system::<Query<(&mut Health, &mut Hunger)>>(self.ecs, |mut query| {
780 let (mut health, mut hunger) = query.get_mut(self.player).unwrap();
781
782 **health = p.health;
783 (hunger.food, hunger.saturation) = (p.food, p.saturation);
784
785 });
789 }
790
791 pub fn set_experience(&mut self, p: &ClientboundSetExperience) {
792 debug!("Got set experience packet {p:?}");
793 }
794
795 pub fn teleport_entity(&mut self, p: &ClientboundTeleportEntity) {
796 debug!("Got teleport entity packet {p:?}");
797
798 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
799 self.ecs,
800 |(mut commands, mut query)| {
801 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
802
803 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
804 warn!("Got teleport entity packet for unknown entity id {}", p.id);
805 return;
806 };
807
808 let relative = p.relative.clone();
809 let change = p.change.clone();
810
811 commands.entity(entity).queue(RelativeEntityUpdate::new(
812 instance_holder.partial_instance.clone(),
813 move |entity| {
814 let entity_id = entity.id();
815 entity.world_scope(move |world| {
816 let mut query =
817 world.query::<(&mut Physics, &mut LookDirection, &mut Position)>();
818 let (mut physics, mut look_direction, mut position) =
819 query.get_mut(world, entity_id).unwrap();
820 let old_position = *position;
821 relative.apply(
822 &change,
823 &mut position,
824 &mut look_direction,
825 &mut physics,
826 );
827 physics.set_old_pos(old_position);
829 });
830 },
831 ));
832 },
833 );
834 }
835
836 pub fn update_advancements(&mut self, p: &ClientboundUpdateAdvancements) {
837 debug!("Got update advancements packet {p:?}");
838 }
839
840 pub fn rotate_head(&mut self, _p: &ClientboundRotateHead) {}
841
842 pub fn move_entity_pos(&mut self, p: &ClientboundMoveEntityPos) {
843 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
844 self.ecs,
845 |(mut commands, mut query)| {
846 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
847
848 debug!("Got move entity pos packet {p:?}");
849
850 let entity_id = p.entity_id;
851 let Some(entity) = entity_id_index.get_by_minecraft_entity(entity_id) else {
852 debug!("Got move entity pos packet for unknown entity id {entity_id}");
853 return;
854 };
855
856 let new_delta = p.delta.clone();
857 let new_on_ground = p.on_ground;
858 commands.entity(entity).queue(RelativeEntityUpdate::new(
859 instance_holder.partial_instance.clone(),
860 move |entity_mut| {
861 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
862 let new_pos = physics.vec_delta_codec.decode(&new_delta);
863 physics.vec_delta_codec.set_base(new_pos);
864 physics.set_on_ground(new_on_ground);
865
866 let mut position = entity_mut.get_mut::<Position>().unwrap();
867 if new_pos != **position {
868 **position = new_pos;
869 }
870
871 trace!(
872 "Applied movement update for {entity_id} / {entity}",
873 entity = entity_mut.id()
874 );
875 },
876 ));
877 },
878 );
879 }
880
881 pub fn move_entity_pos_rot(&mut self, p: &ClientboundMoveEntityPosRot) {
882 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
883 self.ecs,
884 |(mut commands, mut query)| {
885 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
886
887 debug!("Got move entity pos rot packet {p:?}");
888
889 let entity = entity_id_index.get_by_minecraft_entity(p.entity_id);
890
891 let Some(entity) = entity else {
892 debug!(
894 "Got move entity pos rot packet for unknown entity id {}",
895 p.entity_id
896 );
897 return;
898 };
899
900 let new_delta = p.delta.clone();
901 let new_look_direction = LookDirection {
902 x_rot: (p.x_rot as i32 * 360) as f32 / 256.,
903 y_rot: (p.y_rot as i32 * 360) as f32 / 256.,
904 };
905
906 let new_on_ground = p.on_ground;
907
908 commands.entity(entity).queue(RelativeEntityUpdate::new(
909 instance_holder.partial_instance.clone(),
910 move |entity_mut| {
911 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
912 let new_position = physics.vec_delta_codec.decode(&new_delta);
913 physics.vec_delta_codec.set_base(new_position);
914 physics.set_on_ground(new_on_ground);
915
916 let mut position = entity_mut.get_mut::<Position>().unwrap();
917 if new_position != **position {
918 **position = new_position;
919 }
920
921 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
922 if new_look_direction != *look_direction {
923 *look_direction = new_look_direction;
924 }
925 },
926 ));
927 },
928 );
929 }
930
931 pub fn move_entity_rot(&mut self, p: &ClientboundMoveEntityRot) {
932 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
933 self.ecs,
934 |(mut commands, mut query)| {
935 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
936
937 let entity = entity_id_index.get_by_minecraft_entity(p.entity_id);
938 if let Some(entity) = entity {
939 let new_look_direction = LookDirection {
940 x_rot: (p.x_rot as i32 * 360) as f32 / 256.,
941 y_rot: (p.y_rot as i32 * 360) as f32 / 256.,
942 };
943 let new_on_ground = p.on_ground;
944
945 commands.entity(entity).queue(RelativeEntityUpdate::new(
946 instance_holder.partial_instance.clone(),
947 move |entity_mut| {
948 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
949 physics.set_on_ground(new_on_ground);
950
951 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
952 if new_look_direction != *look_direction {
953 *look_direction = new_look_direction;
954 }
955 },
956 ));
957 } else {
958 warn!(
959 "Got move entity rot packet for unknown entity id {}",
960 p.entity_id
961 );
962 }
963 },
964 );
965 }
966 pub fn keep_alive(&mut self, p: &ClientboundKeepAlive) {
967 debug!("Got keep alive packet {p:?} for {:?}", self.player);
968
969 as_system::<(EventWriter<KeepAliveEvent>, Commands)>(
970 self.ecs,
971 |(mut keepalive_events, mut commands)| {
972 keepalive_events.write(KeepAliveEvent {
973 entity: self.player,
974 id: p.id,
975 });
976 commands.trigger(SendPacketEvent::new(
977 self.player,
978 ServerboundKeepAlive { id: p.id },
979 ));
980 },
981 );
982 }
983
984 pub fn remove_entities(&mut self, p: &ClientboundRemoveEntities) {
985 debug!("Got remove entities packet {p:?}");
986
987 as_system::<(Query<&mut EntityIdIndex>, Query<&mut LoadedBy>)>(
988 self.ecs,
989 |(mut query, mut entity_query)| {
990 let Ok(mut entity_id_index) = query.get_mut(self.player) else {
991 warn!("our local player doesn't have EntityIdIndex");
992 return;
993 };
994
995 for &id in &p.entity_ids {
996 let Some(entity) = entity_id_index.remove_by_minecraft_entity(id) else {
997 debug!(
998 "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)."
999 );
1000 continue;
1001 };
1002 let Ok(mut loaded_by) = entity_query.get_mut(entity) else {
1003 warn!(
1004 "tried to despawn entity {id} but it doesn't have a LoadedBy component",
1005 );
1006 continue;
1007 };
1008
1009 loaded_by.remove(&self.player);
1016 }
1017 },
1018 );
1019 }
1020 pub fn player_chat(&mut self, p: &ClientboundPlayerChat) {
1021 debug!("Got player chat packet {p:?}");
1022
1023 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1024 events.write(ChatReceivedEvent {
1025 entity: self.player,
1026 packet: ChatPacket::Player(Arc::new(p.clone())),
1027 });
1028 });
1029 }
1030
1031 pub fn system_chat(&mut self, p: &ClientboundSystemChat) {
1032 debug!("Got system chat packet {p:?}");
1033
1034 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1035 events.write(ChatReceivedEvent {
1036 entity: self.player,
1037 packet: ChatPacket::System(Arc::new(p.clone())),
1038 });
1039 });
1040 }
1041
1042 pub fn disguised_chat(&mut self, p: &ClientboundDisguisedChat) {
1043 debug!("Got disguised chat packet {p:?}");
1044
1045 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1046 events.write(ChatReceivedEvent {
1047 entity: self.player,
1048 packet: ChatPacket::Disguised(Arc::new(p.clone())),
1049 });
1050 });
1051 }
1052
1053 pub fn sound(&mut self, _p: &ClientboundSound) {}
1054
1055 pub fn level_event(&mut self, p: &ClientboundLevelEvent) {
1056 debug!("Got level event packet {p:?}");
1057 }
1058
1059 pub fn block_update(&mut self, p: &ClientboundBlockUpdate) {
1060 debug!("Got block update packet {p:?}");
1061
1062 as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
1063 let mut queued = query.get_mut(self.player).unwrap();
1064 queued.list.push((p.pos, p.block_state));
1065 });
1066 }
1067
1068 pub fn animate(&mut self, p: &ClientboundAnimate) {
1069 debug!("Got animate packet {p:?}");
1070 }
1071
1072 pub fn section_blocks_update(&mut self, p: &ClientboundSectionBlocksUpdate) {
1073 debug!("Got section blocks update packet {p:?}");
1074
1075 as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
1076 let mut queued = query.get_mut(self.player).unwrap();
1077 for new_state in &p.states {
1078 let pos = p.section_pos + new_state.pos;
1079 queued.list.push((pos, new_state.state));
1080 }
1081 });
1082 }
1083
1084 pub fn game_event(&mut self, p: &ClientboundGameEvent) {
1085 use azalea_protocol::packets::game::c_game_event::EventType;
1086
1087 debug!("Got game event packet {p:?}");
1088
1089 #[allow(clippy::single_match)]
1090 match p.event {
1091 EventType::ChangeGameMode => {
1092 as_system::<Query<&mut LocalGameMode>>(self.ecs, |mut query| {
1093 let mut local_game_mode = query.get_mut(self.player).unwrap();
1094 if let Some(new_game_mode) = GameMode::from_id(p.param as u8) {
1095 local_game_mode.current = new_game_mode;
1096 }
1097 });
1098 }
1099 _ => {}
1100 }
1101 }
1102
1103 pub fn level_particles(&mut self, p: &ClientboundLevelParticles) {
1104 debug!("Got level particles packet {p:?}");
1105 }
1106
1107 pub fn server_data(&mut self, p: &ClientboundServerData) {
1108 debug!("Got server data packet {p:?}");
1109 }
1110
1111 pub fn set_equipment(&mut self, p: &ClientboundSetEquipment) {
1112 debug!("Got set equipment packet {p:?}");
1113 }
1114
1115 pub fn update_mob_effect(&mut self, p: &ClientboundUpdateMobEffect) {
1116 debug!("Got update mob effect packet {p:?}");
1117 }
1118
1119 pub fn award_stats(&mut self, _p: &ClientboundAwardStats) {}
1120
1121 pub fn block_changed_ack(&mut self, p: &ClientboundBlockChangedAck) {
1122 as_system::<Query<(&InstanceHolder, &mut BlockStatePredictionHandler)>>(
1123 self.ecs,
1124 |mut query| {
1125 let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
1126 let world = local_player.instance.read();
1127 prediction_handler.end_prediction_up_to(p.seq, &world);
1128 },
1129 );
1130 }
1131
1132 pub fn block_destruction(&mut self, _p: &ClientboundBlockDestruction) {}
1133
1134 pub fn block_entity_data(&mut self, _p: &ClientboundBlockEntityData) {}
1135
1136 pub fn block_event(&mut self, p: &ClientboundBlockEvent) {
1137 debug!("Got block event packet {p:?}");
1138 }
1139
1140 pub fn boss_event(&mut self, _p: &ClientboundBossEvent) {}
1141
1142 pub fn command_suggestions(&mut self, _p: &ClientboundCommandSuggestions) {}
1143
1144 pub fn container_set_content(&mut self, p: &ClientboundContainerSetContent) {
1145 debug!("Got container set content packet {p:?}");
1146
1147 as_system::<(Query<&mut Inventory>, EventWriter<_>)>(
1148 self.ecs,
1149 |(mut query, mut events)| {
1150 let mut inventory = query.get_mut(self.player).unwrap();
1151
1152 if p.container_id == 0 {
1154 for (i, slot) in p.items.iter().enumerate() {
1156 if let Some(slot_mut) = inventory.inventory_menu.slot_mut(i) {
1157 *slot_mut = slot.clone();
1158 }
1159 }
1160 } else {
1161 events.write(SetContainerContentEvent {
1162 entity: self.player,
1163 slots: p.items.clone(),
1164 container_id: p.container_id,
1165 });
1166 }
1167 },
1168 );
1169 }
1170
1171 pub fn container_set_data(&mut self, p: &ClientboundContainerSetData) {
1172 debug!("Got container set data packet {p:?}");
1173
1174 }
1183
1184 pub fn container_set_slot(&mut self, p: &ClientboundContainerSetSlot) {
1185 debug!("Got container set slot packet {p:?}");
1186
1187 as_system::<Query<&mut Inventory>>(self.ecs, |mut query| {
1188 let mut inventory = query.get_mut(self.player).unwrap();
1189
1190 if p.container_id == -1 {
1191 inventory.carried = p.item_stack.clone();
1193 } else if p.container_id == -2 {
1194 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1195 *slot = p.item_stack.clone();
1196 }
1197 } else {
1198 let is_creative_mode_and_inventory_closed = false;
1199 if p.container_id == 0 && azalea_inventory::Player::is_hotbar_slot(p.slot.into()) {
1202 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1205 *slot = p.item_stack.clone();
1206 }
1207 } else if p.container_id == inventory.id
1208 && (p.container_id != 0 || !is_creative_mode_and_inventory_closed)
1209 {
1210 if let Some(slot) = inventory.menu_mut().slot_mut(p.slot.into()) {
1212 *slot = p.item_stack.clone();
1213 inventory.state_id = p.state_id;
1214 }
1215 }
1216 }
1217 });
1218 }
1219
1220 pub fn container_close(&mut self, p: &ClientboundContainerClose) {
1221 debug!("Got container close packet {p:?}");
1225
1226 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1227 events.write(ClientSideCloseContainerEvent {
1228 entity: self.player,
1229 });
1230 });
1231 }
1232
1233 pub fn cooldown(&mut self, _p: &ClientboundCooldown) {}
1234
1235 pub fn custom_chat_completions(&mut self, _p: &ClientboundCustomChatCompletions) {}
1236
1237 pub fn delete_chat(&mut self, _p: &ClientboundDeleteChat) {}
1238
1239 pub fn explode(&mut self, p: &ClientboundExplode) {
1240 trace!("Got explode packet {p:?}");
1241
1242 as_system::<EventWriter<_>>(self.ecs, |mut knockback_events| {
1243 if let Some(knockback) = p.knockback {
1244 knockback_events.write(KnockbackEvent {
1245 entity: self.player,
1246 knockback: KnockbackType::Set(knockback),
1247 });
1248 }
1249 });
1250 }
1251
1252 pub fn forget_level_chunk(&mut self, p: &ClientboundForgetLevelChunk) {
1253 debug!("Got forget level chunk packet {p:?}");
1254
1255 as_system::<Query<&InstanceHolder>>(self.ecs, |mut query| {
1256 let local_player = query.get_mut(self.player).unwrap();
1257
1258 let mut partial_instance = local_player.partial_instance.write();
1259
1260 partial_instance.chunks.limited_set(&p.pos, None);
1261 });
1262 }
1263
1264 pub fn horse_screen_open(&mut self, _p: &ClientboundHorseScreenOpen) {}
1265
1266 pub fn map_item_data(&mut self, _p: &ClientboundMapItemData) {}
1267
1268 pub fn merchant_offers(&mut self, _p: &ClientboundMerchantOffers) {}
1269
1270 pub fn move_vehicle(&mut self, _p: &ClientboundMoveVehicle) {}
1271
1272 pub fn open_book(&mut self, _p: &ClientboundOpenBook) {}
1273
1274 pub fn open_screen(&mut self, p: &ClientboundOpenScreen) {
1275 debug!("Got open screen packet {p:?}");
1276
1277 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1278 events.write(MenuOpenedEvent {
1279 entity: self.player,
1280 window_id: p.container_id,
1281 menu_type: p.menu_type,
1282 title: p.title.to_owned(),
1283 });
1284 });
1285 }
1286
1287 pub fn open_sign_editor(&mut self, _p: &ClientboundOpenSignEditor) {}
1288
1289 pub fn ping(&mut self, p: &ClientboundPing) {
1290 debug!("Got ping packet {p:?}");
1291
1292 as_system::<Commands>(self.ecs, |mut commands| {
1293 commands.trigger_targets(PingEvent(p.clone()), self.player);
1294 });
1295 }
1296
1297 pub fn place_ghost_recipe(&mut self, _p: &ClientboundPlaceGhostRecipe) {}
1298
1299 pub fn player_combat_end(&mut self, _p: &ClientboundPlayerCombatEnd) {}
1300
1301 pub fn player_combat_enter(&mut self, _p: &ClientboundPlayerCombatEnter) {}
1302
1303 pub fn player_combat_kill(&mut self, p: &ClientboundPlayerCombatKill) {
1304 debug!("Got player kill packet {p:?}");
1305
1306 as_system::<(
1307 Commands,
1308 Query<(&MinecraftEntityId, Option<&Dead>)>,
1309 EventWriter<_>,
1310 )>(self.ecs, |(mut commands, mut query, mut events)| {
1311 let (entity_id, dead) = query.get_mut(self.player).unwrap();
1312
1313 if *entity_id == p.player_id && dead.is_none() {
1314 commands.entity(self.player).insert(Dead);
1315 events.write(DeathEvent {
1316 entity: self.player,
1317 packet: Some(p.clone()),
1318 });
1319 }
1320 });
1321 }
1322
1323 pub fn player_look_at(&mut self, _p: &ClientboundPlayerLookAt) {}
1324
1325 pub fn remove_mob_effect(&mut self, _p: &ClientboundRemoveMobEffect) {}
1326
1327 pub fn resource_pack_push(&mut self, p: &ClientboundResourcePackPush) {
1328 debug!("Got resource pack packet {p:?}");
1329
1330 as_system::<EventWriter<_>>(self.ecs, |mut events| {
1331 events.write(ResourcePackEvent {
1332 entity: self.player,
1333 id: p.id,
1334 url: p.url.to_owned(),
1335 hash: p.hash.to_owned(),
1336 required: p.required,
1337 prompt: p.prompt.to_owned(),
1338 });
1339 });
1340 }
1341
1342 pub fn resource_pack_pop(&mut self, _p: &ClientboundResourcePackPop) {}
1343
1344 pub fn respawn(&mut self, p: &ClientboundRespawn) {
1345 debug!("Got respawn packet {p:?}");
1346
1347 as_system::<(
1348 Commands,
1349 Query<
1350 (
1351 &mut InstanceHolder,
1352 &GameProfileComponent,
1353 &ClientInformation,
1354 Option<&mut InstanceName>,
1355 ),
1356 With<LocalEntity>,
1357 >,
1358 EventWriter<_>,
1359 ResMut<InstanceContainer>,
1360 Query<&mut LoadedBy, Without<LocalEntity>>,
1361 )>(
1362 self.ecs,
1363 |(mut commands, mut query, mut events, mut instance_container, mut loaded_by_query)| {
1364 let Ok((mut instance_holder, game_profile, client_information, instance_name)) =
1365 query.get_mut(self.player)
1366 else {
1367 warn!("Got respawn packet but player doesn't have the required components");
1368 return;
1369 };
1370
1371 let new_instance_name = p.common.dimension.clone();
1372
1373 if let Some(mut instance_name) = instance_name {
1374 **instance_name = new_instance_name.clone();
1375 } else {
1376 commands
1377 .entity(self.player)
1378 .insert(InstanceName(new_instance_name.clone()));
1379 }
1380
1381 let Some((_dimension_type, dimension_data)) = p
1382 .common
1383 .dimension_type(&instance_holder.instance.read().registries)
1384 else {
1385 return;
1386 };
1387
1388 let weak_instance = instance_container.get_or_insert(
1391 new_instance_name.clone(),
1392 dimension_data.height,
1393 dimension_data.min_y,
1394 &instance_holder.instance.read().registries,
1395 );
1396 events.write(InstanceLoadedEvent {
1397 entity: self.player,
1398 name: new_instance_name.clone(),
1399 instance: Arc::downgrade(&weak_instance),
1400 });
1401
1402 *instance_holder.partial_instance.write() = PartialInstance::new(
1407 azalea_world::chunk_storage::calculate_chunk_storage_range(
1408 client_information.view_distance.into(),
1409 ),
1410 Some(self.player),
1411 );
1412 instance_holder.instance = weak_instance;
1413
1414 for mut loaded_by in &mut loaded_by_query.iter_mut() {
1416 loaded_by.remove(&self.player);
1417 }
1418
1419 let entity_bundle = EntityBundle::new(
1421 game_profile.uuid,
1422 Vec3::ZERO,
1423 azalea_registry::EntityKind::Player,
1424 new_instance_name,
1425 );
1426 commands.entity(self.player).insert((
1428 LocalGameMode {
1429 current: p.common.game_type,
1430 previous: p.common.previous_game_type.into(),
1431 },
1432 entity_bundle,
1433 ));
1434
1435 commands
1436 .entity(self.player)
1437 .remove::<(Dead, HasClientLoaded)>();
1438 },
1439 )
1440 }
1441
1442 pub fn start_configuration(&mut self, _p: &ClientboundStartConfiguration) {
1443 debug!("Got start configuration packet");
1444
1445 as_system::<(Commands, Query<(&mut RawConnection, &mut InstanceHolder)>)>(
1446 self.ecs,
1447 |(mut commands, mut query)| {
1448 let Some((mut raw_conn, mut instance_holder)) = query.get_mut(self.player).ok()
1449 else {
1450 warn!("Got start configuration packet but player doesn't have a RawConnection");
1451 return;
1452 };
1453 raw_conn.state = ConnectionProtocol::Configuration;
1454
1455 commands.trigger(SendPacketEvent::new(
1456 self.player,
1457 ServerboundConfigurationAcknowledged,
1458 ));
1459
1460 commands
1461 .entity(self.player)
1462 .insert(crate::client::InConfigState)
1463 .remove::<crate::JoinedClientBundle>()
1464 .remove::<EntityBundle>();
1465
1466 instance_holder.reset();
1467 },
1468 );
1469 }
1470
1471 pub fn entity_position_sync(&mut self, p: &ClientboundEntityPositionSync) {
1472 as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
1473 self.ecs,
1474 |(mut commands, mut query)| {
1475 let (entity_id_index, instance_holder) = query.get_mut(self.player).unwrap();
1476
1477 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
1478 debug!("Got teleport entity packet for unknown entity id {}", p.id);
1479 return;
1480 };
1481
1482 let new_position = p.values.pos;
1483 let new_on_ground = p.on_ground;
1484 let new_look_direction = p.values.look_direction;
1485
1486 commands.entity(entity).queue(RelativeEntityUpdate::new(
1487 instance_holder.partial_instance.clone(),
1488 move |entity_mut| {
1489 let is_local_entity = entity_mut.get::<LocalEntity>().is_some();
1490 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
1491
1492 physics.vec_delta_codec.set_base(new_position);
1493
1494 if is_local_entity {
1495 debug!("Ignoring entity position sync packet for local player");
1496 return;
1497 }
1498
1499 physics.set_on_ground(new_on_ground);
1500
1501 let mut last_sent_position =
1502 entity_mut.get_mut::<LastSentPosition>().unwrap();
1503 **last_sent_position = new_position;
1504 let mut position = entity_mut.get_mut::<Position>().unwrap();
1505 **position = new_position;
1506
1507 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
1508 *look_direction = new_look_direction;
1509 },
1510 ));
1511 },
1512 );
1513 }
1514
1515 pub fn select_advancements_tab(&mut self, _p: &ClientboundSelectAdvancementsTab) {}
1516 pub fn set_action_bar_text(&mut self, _p: &ClientboundSetActionBarText) {}
1517 pub fn set_border_center(&mut self, _p: &ClientboundSetBorderCenter) {}
1518 pub fn set_border_lerp_size(&mut self, _p: &ClientboundSetBorderLerpSize) {}
1519 pub fn set_border_size(&mut self, _p: &ClientboundSetBorderSize) {}
1520 pub fn set_border_warning_delay(&mut self, _p: &ClientboundSetBorderWarningDelay) {}
1521 pub fn set_border_warning_distance(&mut self, _p: &ClientboundSetBorderWarningDistance) {}
1522 pub fn set_camera(&mut self, _p: &ClientboundSetCamera) {}
1523 pub fn set_display_objective(&mut self, _p: &ClientboundSetDisplayObjective) {}
1524 pub fn set_objective(&mut self, _p: &ClientboundSetObjective) {}
1525 pub fn set_passengers(&mut self, _p: &ClientboundSetPassengers) {}
1526 pub fn set_player_team(&mut self, p: &ClientboundSetPlayerTeam) {
1527 debug!("Got set player team packet {p:?}");
1528 }
1529 pub fn set_score(&mut self, _p: &ClientboundSetScore) {}
1530 pub fn set_simulation_distance(&mut self, _p: &ClientboundSetSimulationDistance) {}
1531 pub fn set_subtitle_text(&mut self, _p: &ClientboundSetSubtitleText) {}
1532 pub fn set_title_text(&mut self, _p: &ClientboundSetTitleText) {}
1533 pub fn set_titles_animation(&mut self, _p: &ClientboundSetTitlesAnimation) {}
1534 pub fn clear_titles(&mut self, _p: &ClientboundClearTitles) {}
1535 pub fn sound_entity(&mut self, _p: &ClientboundSoundEntity) {}
1536 pub fn stop_sound(&mut self, _p: &ClientboundStopSound) {}
1537 pub fn tab_list(&mut self, _p: &ClientboundTabList) {}
1538 pub fn tag_query(&mut self, _p: &ClientboundTagQuery) {}
1539 pub fn take_item_entity(&mut self, _p: &ClientboundTakeItemEntity) {}
1540 pub fn bundle_delimiter(&mut self, _p: &ClientboundBundleDelimiter) {}
1541 pub fn damage_event(&mut self, _p: &ClientboundDamageEvent) {}
1542 pub fn hurt_animation(&mut self, _p: &ClientboundHurtAnimation) {}
1543 pub fn ticking_state(&mut self, _p: &ClientboundTickingState) {}
1544 pub fn ticking_step(&mut self, _p: &ClientboundTickingStep) {}
1545 pub fn reset_score(&mut self, _p: &ClientboundResetScore) {}
1546 pub fn cookie_request(&mut self, _p: &ClientboundCookieRequest) {}
1547 pub fn debug_sample(&mut self, _p: &ClientboundDebugSample) {}
1548 pub fn pong_response(&mut self, _p: &ClientboundPongResponse) {}
1549 pub fn store_cookie(&mut self, _p: &ClientboundStoreCookie) {}
1550 pub fn transfer(&mut self, _p: &ClientboundTransfer) {}
1551 pub fn move_minecart_along_track(&mut self, _p: &ClientboundMoveMinecartAlongTrack) {}
1552 pub fn set_held_slot(&mut self, p: &ClientboundSetHeldSlot) {
1553 debug!("Got set held slot packet {p:?}");
1554
1555 as_system::<Query<&mut Inventory>>(self.ecs, |mut query| {
1556 let mut inventory = query.get_mut(self.player).unwrap();
1557 if p.slot <= 8 {
1558 inventory.selected_hotbar_slot = p.slot as u8;
1559 }
1560 });
1561 }
1562 pub fn set_player_inventory(&mut self, _p: &ClientboundSetPlayerInventory) {}
1563 pub fn projectile_power(&mut self, _p: &ClientboundProjectilePower) {}
1564 pub fn custom_report_details(&mut self, _p: &ClientboundCustomReportDetails) {}
1565 pub fn server_links(&mut self, _p: &ClientboundServerLinks) {}
1566 pub fn player_rotation(&mut self, _p: &ClientboundPlayerRotation) {}
1567 pub fn recipe_book_add(&mut self, _p: &ClientboundRecipeBookAdd) {}
1568 pub fn recipe_book_remove(&mut self, _p: &ClientboundRecipeBookRemove) {}
1569 pub fn recipe_book_settings(&mut self, _p: &ClientboundRecipeBookSettings) {}
1570 pub fn test_instance_block_status(&mut self, _p: &ClientboundTestInstanceBlockStatus) {}
1571 pub fn waypoint(&mut self, _p: &ClientboundWaypoint) {}
1572
1573 pub fn clear_dialog(&mut self, p: &ClientboundClearDialog) {
1574 debug!("Got clear dialog packet {p:?}");
1575 }
1576 pub fn show_dialog(&mut self, p: &ClientboundShowDialog) {
1577 debug!("Got show dialog packet {p:?}");
1578 }
1579}