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