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