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