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::*;
29pub use events::*;
30use tracing::{debug, error, warn};
31
32use crate::{
33 ClientInformation,
34 block_update::QueuedServerBlockUpdates,
35 chunks,
36 client_chat::{ChatPacket, ChatReceivedEvent},
37 connection::RawConnection,
38 cookies::{RequestCookieEvent, StoreCookieEvent},
39 disconnect::DisconnectEvent,
40 interact::BlockStatePredictionHandler,
41 inventory::{ClientsideCloseContainerEvent, MenuOpenedEvent, SetContainerContentEvent},
42 local_player::{Experience, Hunger, PreviousGameMode, TabList, TabListResource, 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 low_disk_space_warning,
201 game_rule_values,
202 ]
203 );
204}
205
206pub struct GamePacketHandler<'a> {
207 pub ecs: &'a mut World,
208 pub player: Entity,
209}
210impl GamePacketHandler<'_> {
211 pub fn login(&mut self, p: &ClientboundLogin) {
212 debug!("Got login packet");
213
214 as_system::<(
215 Commands,
216 Query<
217 (
218 &GameProfileComponent,
219 &ClientInformation,
220 Option<&mut WorldName>,
221 Option<&mut LoadedBy>,
222 &mut EntityIdIndex,
223 &mut WorldHolder,
224 ),
225 With<LocalEntity>,
226 >,
227 MessageWriter<WorldLoadedEvent>,
228 ResMut<Worlds>,
229 ResMut<EntityUuidIndex>,
230 Query<&mut LoadedBy, Without<LocalEntity>>,
231 )>(
232 self.ecs,
233 |(
234 mut commands,
235 mut query,
236 mut world_loaded_events,
237 mut worlds,
238 mut entity_uuid_index,
239 mut loaded_by_query,
240 )| {
241 let (
242 game_profile,
243 client_information,
244 world_name,
245 loaded_by,
246 mut entity_id_index,
247 mut world_holder,
248 ) = query.get_mut(self.player).unwrap();
249
250 let new_world_name = WorldName(p.common.dimension.clone());
251
252 if let Some(mut world_name) = world_name {
253 *world_name = new_world_name.clone();
254 } else {
255 commands.entity(self.player).insert(new_world_name.clone());
256 }
257
258 let weak_world;
259 {
260 let client_registries = &world_holder.shared.read().registries;
261
262 let Some((_dimension_type, dimension_data)) =
263 p.common.dimension_type(client_registries)
264 else {
265 return;
266 };
267
268 weak_world = worlds.get_or_insert(
270 new_world_name.clone(),
271 dimension_data.height,
272 dimension_data.min_y,
273 client_registries,
274 );
275 world_loaded_events.write(WorldLoadedEvent {
276 entity: self.player,
277 name: new_world_name.clone(),
278 world: Arc::downgrade(&weak_world),
279 });
280 }
281
282 *world_holder.partial.write() = PartialWorld::new(
286 azalea_world::chunk::calculate_chunk_storage_range(
287 client_information.view_distance.into(),
288 ),
289 Some(self.player),
292 );
293 {
294 let client_registries = world_holder.shared.read().registries.clone();
295 let shared_registries = &mut weak_world.write().registries;
296 shared_registries.extend(client_registries);
298 }
299 world_holder.shared = weak_world;
300
301 let entity_bundle = EntityBundle::new(
302 game_profile.uuid,
303 Vec3::ZERO,
304 EntityKind::Player,
305 new_world_name,
306 );
307 let entity_id = p.player_id;
308 commands.entity(self.player).insert((
310 entity_id,
311 p.common.game_type,
312 PreviousGameMode(p.common.previous_game_type.into()),
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<TabListResource>,
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().into();
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<TabListResource>,
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 = world.commands();
705 let mut entity_commands = commands.entity(entity_id);
706 if let Err(e) =
707 apply_metadata(&mut entity_commands, entity_kind, packed_items)
708 {
709 warn!("{e}");
710 }
711 world.flush();
712 });
713 },
714 ));
715 });
716 }
717
718 pub fn update_attributes(&mut self, _p: &ClientboundUpdateAttributes) {
719 }
721
722 pub fn set_entity_motion(&mut self, p: &ClientboundSetEntityMotion) {
723 as_system::<(
727 Commands,
728 Query<(&EntityIdIndex, &WorldHolder)>,
729 EntityUpdateQuery,
730 )>(self.ecs, |(mut commands, query, entity_update_query)| {
731 let (entity_id_index, world_holder) = query.get(self.player).unwrap();
732
733 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
734 debug!(
738 "Got set entity motion packet for unknown entity id {}",
739 p.id
740 );
741 return;
742 };
743
744 let data = KnockbackData::Set(p.delta.to_vec3());
745
746 if should_apply_entity_update(
749 &mut commands,
750 &mut world_holder.partial.write(),
751 entity,
752 entity_update_query,
753 ) {
754 commands.trigger(KnockbackEvent { entity, data });
755 }
756 });
757 }
758
759 pub fn set_entity_link(&mut self, p: &ClientboundSetEntityLink) {
760 debug!("Got set entity link packet {p:?}");
761 }
762
763 pub fn initialize_border(&mut self, p: &ClientboundInitializeBorder) {
764 debug!("Got initialize border packet {p:?}");
765 }
766
767 pub fn set_time(&mut self, _p: &ClientboundSetTime) {
768 }
770
771 pub fn set_default_spawn_position(&mut self, p: &ClientboundSetDefaultSpawnPosition) {
772 debug!("Got set default spawn position packet {p:?}");
773 }
774
775 pub fn set_health(&mut self, p: &ClientboundSetHealth) {
776 debug!("Got set health packet {p:?}");
777
778 as_system::<Query<(&mut Health, &mut Hunger)>>(self.ecs, |mut query| {
779 let (mut health, mut hunger) = query.get_mut(self.player).unwrap();
780
781 **health = p.health;
782 (hunger.food, hunger.saturation) = (p.food, p.saturation);
783
784 });
788 }
789
790 pub fn set_experience(&mut self, p: &ClientboundSetExperience) {
791 debug!("Got set experience packet {p:?}");
792
793 as_system::<Query<&mut Experience>>(self.ecs, |mut query| {
794 let mut experience = query.get_mut(self.player).unwrap();
795 experience.progress = p.experience_progress;
796 experience.level = p.experience_level;
797 experience.total = p.total_experience;
798 });
799 }
800
801 pub fn teleport_entity(&mut self, p: &ClientboundTeleportEntity) {
802 debug!("Got teleport entity packet {p:?}");
803
804 as_system::<(Commands, Query<(&EntityIdIndex, &WorldHolder)>)>(
805 self.ecs,
806 |(mut commands, query)| {
807 let (entity_id_index, world_holder) = query.get(self.player).unwrap();
808
809 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
810 warn!("Got teleport entity packet for unknown entity id {}", p.id);
811 return;
812 };
813
814 let relative = p.relative.clone();
815 let change = p.change.clone();
816
817 commands.entity(entity).queue(RelativeEntityUpdate::new(
818 world_holder.partial.clone(),
819 move |entity| {
820 let entity_id = entity.id();
821 entity.world_scope(move |world| {
822 let mut query =
823 world.query::<(&mut Physics, &mut LookDirection, &mut Position)>();
824 let (mut physics, mut look_direction, mut position) =
825 query.get_mut(world, entity_id).unwrap();
826 let old_position = *position;
827 relative.apply(
828 &change,
829 &mut position,
830 &mut look_direction,
831 &mut physics,
832 );
833 physics.set_old_pos(old_position);
835 });
836 },
837 ));
838 },
839 );
840 }
841
842 pub fn update_advancements(&mut self, p: &ClientboundUpdateAdvancements) {
843 debug!("Got update advancements packet {p:?}");
844 }
845
846 pub fn rotate_head(&mut self, _p: &ClientboundRotateHead) {}
847
848 pub fn move_entity_pos(&mut self, p: &ClientboundMoveEntityPos) {
849 as_system::<(
850 Commands,
851 Query<(&EntityIdIndex, &WorldHolder)>,
852 MoveEntityQuery,
853 EntityUpdateQuery,
854 )>(
855 self.ecs,
856 |(commands, player_query, entity_query, entity_update_query)| {
857 move_entity(
858 self.player,
859 commands,
860 MoveEntity {
861 entity_id: p.entity_id,
862 delta: Some(p.delta),
863 look_direction: None,
864 on_ground: p.on_ground,
865 },
866 player_query,
867 entity_query,
868 entity_update_query,
869 );
870 },
871 );
872 }
873 pub fn move_entity_pos_rot(&mut self, p: &ClientboundMoveEntityPosRot) {
874 as_system::<(
875 Commands,
876 Query<(&EntityIdIndex, &WorldHolder)>,
877 MoveEntityQuery,
878 EntityUpdateQuery,
879 )>(
880 self.ecs,
881 |(commands, player_query, entity_query, entity_update_query)| {
882 move_entity(
883 self.player,
884 commands,
885 MoveEntity {
886 entity_id: p.entity_id,
887 delta: Some(p.delta),
888 look_direction: Some(p.look_direction),
889 on_ground: p.on_ground,
890 },
891 player_query,
892 entity_query,
893 entity_update_query,
894 );
895 },
896 );
897 }
898 pub fn move_entity_rot(&mut self, p: &ClientboundMoveEntityRot) {
899 as_system::<(
900 Commands,
901 Query<(&EntityIdIndex, &WorldHolder)>,
902 MoveEntityQuery,
903 EntityUpdateQuery,
904 )>(
905 self.ecs,
906 |(commands, player_query, entity_query, entity_update_query)| {
907 move_entity(
908 self.player,
909 commands,
910 MoveEntity {
911 entity_id: p.entity_id,
912 delta: None,
913 look_direction: Some(p.look_direction),
914 on_ground: p.on_ground,
915 },
916 player_query,
917 entity_query,
918 entity_update_query,
919 );
920 },
921 );
922 }
923
924 pub fn keep_alive(&mut self, p: &ClientboundKeepAlive) {
925 debug!("Got keep alive packet {p:?} for {:?}", self.player);
926
927 as_system::<Commands>(self.ecs, |mut commands| {
928 commands.trigger(KeepAliveEvent {
929 entity: self.player,
930 id: p.id,
931 });
932 commands.trigger(SendGamePacketEvent::new(
933 self.player,
934 ServerboundKeepAlive { id: p.id },
935 ));
936 });
937 }
938
939 pub fn remove_entities(&mut self, p: &ClientboundRemoveEntities) {
940 debug!("Got remove entities packet {p:?}");
941
942 as_system::<(Query<&mut EntityIdIndex>, Query<&mut LoadedBy>)>(
943 self.ecs,
944 |(mut query, mut entity_query)| {
945 let Ok(mut entity_id_index) = query.get_mut(self.player) else {
946 warn!("our local player doesn't have EntityIdIndex");
947 return;
948 };
949
950 for &id in &p.entity_ids {
951 let Some(entity) = entity_id_index.remove_by_minecraft_entity(id) else {
952 debug!(
953 "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)."
954 );
955 continue;
956 };
957 let Ok(mut loaded_by) = entity_query.get_mut(entity) else {
958 warn!(
959 "tried to despawn entity {id} but it doesn't have a LoadedBy component",
960 );
961 continue;
962 };
963
964 loaded_by.remove(&self.player);
971 }
972 },
973 );
974 }
975 pub fn player_chat(&mut self, p: &ClientboundPlayerChat) {
976 debug!("Got player chat packet {p:?}");
977
978 as_system::<MessageWriter<_>>(self.ecs, |mut events| {
979 events.write(ChatReceivedEvent {
980 entity: self.player,
981 packet: ChatPacket::Player(Arc::new(p.clone())),
982 });
983 });
984 }
985
986 pub fn system_chat(&mut self, p: &ClientboundSystemChat) {
987 debug!("Got system chat packet {p:?}");
988
989 as_system::<MessageWriter<_>>(self.ecs, |mut events| {
990 events.write(ChatReceivedEvent {
991 entity: self.player,
992 packet: ChatPacket::System(Arc::new(p.clone())),
993 });
994 });
995 }
996
997 pub fn disguised_chat(&mut self, p: &ClientboundDisguisedChat) {
998 debug!("Got disguised chat packet {p:?}");
999
1000 as_system::<MessageWriter<_>>(self.ecs, |mut events| {
1001 events.write(ChatReceivedEvent {
1002 entity: self.player,
1003 packet: ChatPacket::Disguised(Arc::new(p.clone())),
1004 });
1005 });
1006 }
1007
1008 pub fn sound(&mut self, _p: &ClientboundSound) {}
1009
1010 pub fn level_event(&mut self, p: &ClientboundLevelEvent) {
1011 debug!("Got level event packet {p:?}");
1012 }
1013
1014 pub fn block_update(&mut self, p: &ClientboundBlockUpdate) {
1015 debug!("Got block update packet {p:?}");
1016
1017 as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
1018 let mut queued = query.get_mut(self.player).unwrap();
1019 queued.list.push((p.pos, p.block_state));
1020 });
1021 }
1022
1023 pub fn animate(&mut self, p: &ClientboundAnimate) {
1024 debug!("Got animate packet {p:?}");
1025 }
1026
1027 pub fn section_blocks_update(&mut self, p: &ClientboundSectionBlocksUpdate) {
1028 debug!("Got section blocks update packet {p:?}");
1029
1030 as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
1031 let mut queued = query.get_mut(self.player).unwrap();
1032 for new_state in &p.states {
1033 let pos = p.section_pos + new_state.pos;
1034 queued.list.push((pos, new_state.state));
1035 }
1036 });
1037 }
1038
1039 pub fn game_event(&mut self, p: &ClientboundGameEvent) {
1040 use azalea_protocol::packets::game::c_game_event::EventType;
1041
1042 debug!("Got game event packet {p:?}");
1043
1044 #[allow(clippy::single_match)]
1045 match p.event {
1046 EventType::ChangeGameMode => {
1047 as_system::<Query<&mut GameMode>>(self.ecs, |mut query| {
1048 let mut game_mode = query.get_mut(self.player).unwrap();
1049 if let Some(new_game_mode) = GameMode::from_id(p.param as u8) {
1050 *game_mode = new_game_mode;
1051 }
1052 });
1053 }
1054 _ => {}
1055 }
1056 }
1057
1058 pub fn level_particles(&mut self, p: &ClientboundLevelParticles) {
1059 debug!("Got level particles packet {p:?}");
1060 }
1061
1062 pub fn server_data(&mut self, p: &ClientboundServerData) {
1063 debug!("Got server data packet {p:?}");
1064 }
1065
1066 pub fn set_equipment(&mut self, p: &ClientboundSetEquipment) {
1067 debug!("Got set equipment packet {p:?}");
1068 }
1069
1070 pub fn update_mob_effect(&mut self, p: &ClientboundUpdateMobEffect) {
1071 debug!("Got update mob effect packet {p:?}");
1072
1073 as_system::<(
1074 Commands,
1075 Query<(&EntityIdIndex, &WorldHolder)>,
1076 EntityUpdateQuery,
1077 )>(self.ecs, |(mut commands, query, entity_update_query)| {
1078 let (entity_id_index, world_holder) = query.get(self.player).unwrap();
1079
1080 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.entity_id) else {
1081 debug!(
1082 "Got update mob effect packet for unknown entity id {}",
1083 p.entity_id
1084 );
1085 return;
1086 };
1087
1088 if !should_apply_entity_update(
1089 &mut commands,
1090 &mut world_holder.partial.write(),
1091 entity,
1092 entity_update_query,
1093 ) {
1094 return;
1095 }
1096
1097 commands.trigger(AddEffectEvent {
1098 entity,
1099 id: p.mob_effect,
1100 data: p.data.clone(),
1101 });
1102 });
1103 }
1104
1105 pub fn award_stats(&mut self, _p: &ClientboundAwardStats) {}
1106
1107 pub fn block_changed_ack(&mut self, p: &ClientboundBlockChangedAck) {
1108 as_system::<Query<(&WorldHolder, &mut BlockStatePredictionHandler)>>(
1109 self.ecs,
1110 |mut query| {
1111 let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
1112 let world = local_player.shared.read();
1113 prediction_handler.end_prediction_up_to(p.seq, &world);
1114 },
1115 );
1116 }
1117
1118 pub fn block_destruction(&mut self, _p: &ClientboundBlockDestruction) {}
1119
1120 pub fn block_entity_data(&mut self, _p: &ClientboundBlockEntityData) {}
1121
1122 pub fn block_event(&mut self, p: &ClientboundBlockEvent) {
1123 debug!("Got block event packet {p:?}");
1124 }
1125
1126 pub fn boss_event(&mut self, _p: &ClientboundBossEvent) {}
1127
1128 pub fn command_suggestions(&mut self, _p: &ClientboundCommandSuggestions) {}
1129
1130 pub fn container_set_content(&mut self, p: &ClientboundContainerSetContent) {
1131 debug!("Got container set content packet {p:?}");
1132
1133 as_system::<(Commands, Query<&mut Inventory>)>(self.ecs, |(mut commands, mut query)| {
1134 let mut inventory = query.get_mut(self.player).unwrap();
1135
1136 if p.container_id == 0 {
1138 for (i, slot) in p.items.iter().enumerate() {
1140 if let Some(slot_mut) = inventory.inventory_menu.slot_mut(i) {
1141 *slot_mut = slot.clone();
1142 }
1143 }
1144 } else {
1145 commands.trigger(SetContainerContentEvent {
1146 entity: self.player,
1147 slots: p.items.clone(),
1148 container_id: p.container_id,
1149 });
1150 }
1151 });
1152 }
1153
1154 pub fn container_set_data(&mut self, p: &ClientboundContainerSetData) {
1155 debug!("Got container set data packet {p:?}");
1156
1157 }
1166
1167 pub fn container_set_slot(&mut self, p: &ClientboundContainerSetSlot) {
1168 debug!("Got container set slot packet {p:?}");
1169
1170 as_system::<Query<&mut Inventory>>(self.ecs, |mut query| {
1171 let mut inventory = query.get_mut(self.player).unwrap();
1172
1173 if p.container_id == -1 {
1174 inventory.carried = p.item_stack.clone();
1176 } else if p.container_id == -2 {
1177 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1178 *slot = p.item_stack.clone();
1179 }
1180 } else {
1181 let is_creative_mode_and_inventory_closed = false;
1182 if p.container_id == 0 && azalea_inventory::Player::is_hotbar_slot(p.slot.into()) {
1185 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1188 *slot = p.item_stack.clone();
1189 }
1190 } else if p.container_id == inventory.id
1191 && (p.container_id != 0 || !is_creative_mode_and_inventory_closed)
1192 {
1193 if let Some(slot) = inventory.menu_mut().slot_mut(p.slot.into()) {
1195 *slot = p.item_stack.clone();
1196 inventory.state_id = p.state_id;
1197 }
1198 }
1199 }
1200 });
1201 }
1202
1203 pub fn container_close(&mut self, p: &ClientboundContainerClose) {
1204 debug!("Got container close packet {p:?}");
1208
1209 as_system::<Commands>(self.ecs, |mut commands| {
1210 commands.trigger(ClientsideCloseContainerEvent {
1211 entity: self.player,
1212 });
1213 });
1214 }
1215
1216 pub fn cooldown(&mut self, _p: &ClientboundCooldown) {}
1217
1218 pub fn custom_chat_completions(&mut self, _p: &ClientboundCustomChatCompletions) {}
1219
1220 pub fn delete_chat(&mut self, _p: &ClientboundDeleteChat) {}
1221
1222 pub fn explode(&mut self, p: &ClientboundExplode) {
1223 debug!("Got explode packet {p:?}");
1224
1225 as_system::<Commands>(self.ecs, |mut knockback_events| {
1226 if let Some(knockback) = p.player_knockback {
1227 knockback_events.trigger(KnockbackEvent {
1228 entity: self.player,
1229 data: KnockbackData::Add(knockback),
1230 });
1231 }
1232 });
1233 }
1234
1235 pub fn forget_level_chunk(&mut self, p: &ClientboundForgetLevelChunk) {
1236 debug!("Got forget level chunk packet {p:?}");
1237
1238 as_system::<Query<&WorldHolder>>(self.ecs, |mut query| {
1239 let local_player = query.get_mut(self.player).unwrap();
1240
1241 let mut partial_world = local_player.partial.write();
1242
1243 partial_world.chunks.limited_set(&p.pos, None);
1244 });
1245 }
1246
1247 pub fn mount_screen_open(&mut self, _p: &ClientboundMountScreenOpen) {}
1248
1249 pub fn map_item_data(&mut self, _p: &ClientboundMapItemData) {}
1250
1251 pub fn merchant_offers(&mut self, _p: &ClientboundMerchantOffers) {}
1252
1253 pub fn move_vehicle(&mut self, _p: &ClientboundMoveVehicle) {}
1254
1255 pub fn open_book(&mut self, _p: &ClientboundOpenBook) {}
1256
1257 pub fn open_screen(&mut self, p: &ClientboundOpenScreen) {
1258 debug!("Got open screen packet {p:?}");
1259
1260 as_system::<Commands>(self.ecs, |mut commands| {
1261 commands.trigger(MenuOpenedEvent {
1262 entity: self.player,
1263 window_id: p.container_id,
1264 menu_type: p.menu_type,
1265 title: p.title.to_owned(),
1266 });
1267 });
1268 }
1269
1270 pub fn open_sign_editor(&mut self, _p: &ClientboundOpenSignEditor) {}
1271
1272 pub fn ping(&mut self, p: &ClientboundPing) {
1273 debug!("Got ping packet {p:?}");
1274
1275 as_system::<Commands>(self.ecs, |mut commands| {
1276 commands.trigger(GamePingEvent {
1277 entity: self.player,
1278 packet: p.clone(),
1279 });
1280 });
1281 }
1282
1283 pub fn place_ghost_recipe(&mut self, _p: &ClientboundPlaceGhostRecipe) {}
1284
1285 pub fn player_combat_end(&mut self, _p: &ClientboundPlayerCombatEnd) {}
1286
1287 pub fn player_combat_enter(&mut self, _p: &ClientboundPlayerCombatEnter) {}
1288
1289 pub fn player_combat_kill(&mut self, p: &ClientboundPlayerCombatKill) {
1290 debug!("Got player kill packet {p:?}");
1291
1292 as_system::<(
1293 Commands,
1294 Query<(&MinecraftEntityId, Option<&Dead>)>,
1295 MessageWriter<_>,
1296 )>(self.ecs, |(mut commands, mut query, mut events)| {
1297 let (entity_id, dead) = query.get_mut(self.player).unwrap();
1298
1299 if *entity_id == p.player_id && dead.is_none() {
1300 commands.entity(self.player).insert(Dead);
1301 events.write(DeathEvent {
1302 entity: self.player,
1303 packet: Some(p.clone()),
1304 });
1305 }
1306 });
1307 }
1308
1309 pub fn player_look_at(&mut self, _p: &ClientboundPlayerLookAt) {}
1310
1311 pub fn remove_mob_effect(&mut self, p: &ClientboundRemoveMobEffect) {
1312 debug!("Got remove mob effect packet {p:?}");
1313
1314 as_system::<(
1315 Commands,
1316 Query<(&EntityIdIndex, &WorldHolder)>,
1317 EntityUpdateQuery,
1318 )>(self.ecs, |(mut commands, query, entity_update_query)| {
1319 let (entity_id_index, world_holder) = query.get(self.player).unwrap();
1320
1321 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.entity_id) else {
1322 debug!(
1323 "Got remove mob effect packet for unknown entity id {}",
1324 p.entity_id
1325 );
1326 return;
1327 };
1328
1329 if !should_apply_entity_update(
1330 &mut commands,
1331 &mut world_holder.partial.write(),
1332 entity,
1333 entity_update_query,
1334 ) {
1335 return;
1336 }
1337
1338 commands.trigger(RemoveEffectsEvent {
1339 entity,
1340 effects: vec![p.effect],
1341 });
1342 });
1343 }
1344
1345 pub fn resource_pack_push(&mut self, p: &ClientboundResourcePackPush) {
1346 debug!("Got resource pack packet {p:?}");
1347
1348 as_system::<MessageWriter<_>>(self.ecs, |mut events| {
1349 events.write(ResourcePackEvent {
1350 entity: self.player,
1351 id: p.id,
1352 url: p.url.to_owned(),
1353 hash: p.hash.to_owned(),
1354 required: p.required,
1355 prompt: p.prompt.to_owned(),
1356 });
1357 });
1358 }
1359
1360 pub fn resource_pack_pop(&mut self, _p: &ClientboundResourcePackPop) {}
1361
1362 pub fn respawn(&mut self, p: &ClientboundRespawn) {
1363 debug!("Got respawn packet {p:?}");
1364
1365 as_system::<(
1366 Commands,
1367 Query<
1368 (
1369 &mut WorldHolder,
1370 &GameProfileComponent,
1371 &ClientInformation,
1372 Option<&mut WorldName>,
1373 ),
1374 With<LocalEntity>,
1375 >,
1376 MessageWriter<_>,
1377 ResMut<Worlds>,
1378 Query<&mut LoadedBy, Without<LocalEntity>>,
1379 )>(
1380 self.ecs,
1381 |(mut commands, mut query, mut events, mut worlds, mut loaded_by_query)| {
1382 let Ok((mut world_holder, game_profile, client_information, world_name)) =
1383 query.get_mut(self.player)
1384 else {
1385 warn!("Got respawn packet but player doesn't have the required components");
1386 return;
1387 };
1388
1389 let new_world_name = WorldName(p.common.dimension.clone());
1390
1391 if let Some(mut world_name) = world_name {
1392 *world_name = new_world_name.clone();
1393 } else {
1394 commands.entity(self.player).insert(new_world_name.clone());
1395 }
1396
1397 let weak_world;
1398 {
1399 let client_registries = &world_holder.shared.read().registries;
1400 let Some((_dimension_type, dimension_data)) =
1401 p.common.dimension_type(client_registries)
1402 else {
1403 return;
1404 };
1405
1406 weak_world = worlds.get_or_insert(
1408 new_world_name.clone(),
1409 dimension_data.height,
1410 dimension_data.min_y,
1411 client_registries,
1412 );
1413 events.write(WorldLoadedEvent {
1414 entity: self.player,
1415 name: new_world_name.clone(),
1416 world: Arc::downgrade(&weak_world),
1417 });
1418 }
1419
1420 *world_holder.partial.write() = PartialWorld::new(
1424 azalea_world::chunk::calculate_chunk_storage_range(
1425 client_information.view_distance.into(),
1426 ),
1427 Some(self.player),
1428 );
1429 world_holder.shared = weak_world;
1430
1431 for mut loaded_by in &mut loaded_by_query.iter_mut() {
1433 loaded_by.remove(&self.player);
1434 }
1435
1436 let entity_bundle = EntityBundle::new(
1438 game_profile.uuid,
1439 Vec3::ZERO,
1440 EntityKind::Player,
1441 new_world_name,
1442 );
1443 commands.entity(self.player).insert((
1445 p.common.game_type,
1446 PreviousGameMode(p.common.previous_game_type.into()),
1447 entity_bundle,
1448 ));
1449
1450 commands
1451 .entity(self.player)
1452 .remove::<(Dead, HasClientLoaded)>();
1453 },
1454 )
1455 }
1456
1457 pub fn start_configuration(&mut self, _p: &ClientboundStartConfiguration) {
1458 debug!("Got start configuration packet");
1459
1460 as_system::<(Commands, Query<(&mut RawConnection, &mut WorldHolder)>)>(
1461 self.ecs,
1462 |(mut commands, mut query)| {
1463 let Some((mut raw_conn, mut world_holder)) = query.get_mut(self.player).ok() else {
1464 warn!("Got start configuration packet but player doesn't have a RawConnection");
1465 return;
1466 };
1467 raw_conn.state = ConnectionProtocol::Configuration;
1468
1469 commands.trigger(SendGamePacketEvent::new(
1470 self.player,
1471 ServerboundConfigurationAcknowledged,
1472 ));
1473
1474 commands
1475 .entity(self.player)
1476 .insert(crate::client::InConfigState)
1477 .remove::<crate::JoinedClientBundle>()
1478 .remove::<EntityBundle>();
1479
1480 world_holder.reset();
1481 },
1482 );
1483 }
1484
1485 pub fn entity_position_sync(&mut self, p: &ClientboundEntityPositionSync) {
1486 as_system::<(
1487 Commands,
1488 Query<(&EntityIdIndex, &WorldHolder)>,
1489 Query<(
1490 &mut Physics,
1491 &mut Position,
1492 &mut LookDirection,
1493 Option<&LocalEntity>,
1494 )>,
1495 EntityUpdateQuery,
1496 )>(
1497 self.ecs,
1498 |(mut commands, mut query, mut entity_query, entity_update_query)| {
1499 let (entity_id_index, world_holder) = query.get_mut(self.player).unwrap();
1500
1501 let Some(entity) = entity_id_index.get_by_minecraft_entity(p.id) else {
1502 debug!("Got teleport entity packet for unknown entity id {}", p.id);
1503 return;
1504 };
1505
1506 let new_position = p.values.pos;
1507 let new_on_ground = p.on_ground;
1508 let new_look_direction = p.values.look_direction;
1509
1510 if !should_apply_entity_update(
1511 &mut commands,
1512 &mut world_holder.partial.write(),
1513 entity,
1514 entity_update_query,
1515 ) {
1516 return;
1517 }
1518
1519 let Ok((mut physics, mut position, mut look_direction, local_entity)) =
1520 entity_query.get_mut(entity)
1521 else {
1522 return;
1523 };
1524
1525 physics.vec_delta_codec.set_base(new_position);
1526
1527 let is_client_authoritative = local_entity.is_some();
1528 if is_client_authoritative {
1529 debug!("Ignoring entity position sync packet for local player");
1530 return;
1531 }
1532
1533 physics.set_on_ground(new_on_ground);
1534
1535 if **position != new_position {
1536 **position = new_position;
1537 }
1538
1539 if *look_direction != new_look_direction {
1540 *look_direction = new_look_direction;
1541 }
1542 },
1543 );
1544 }
1545
1546 pub fn select_advancements_tab(&mut self, _p: &ClientboundSelectAdvancementsTab) {}
1547 pub fn set_action_bar_text(&mut self, _p: &ClientboundSetActionBarText) {}
1548 pub fn set_border_center(&mut self, _p: &ClientboundSetBorderCenter) {}
1549 pub fn set_border_lerp_size(&mut self, _p: &ClientboundSetBorderLerpSize) {}
1550 pub fn set_border_size(&mut self, _p: &ClientboundSetBorderSize) {}
1551 pub fn set_border_warning_delay(&mut self, _p: &ClientboundSetBorderWarningDelay) {}
1552 pub fn set_border_warning_distance(&mut self, _p: &ClientboundSetBorderWarningDistance) {}
1553 pub fn set_camera(&mut self, _p: &ClientboundSetCamera) {}
1554 pub fn set_display_objective(&mut self, _p: &ClientboundSetDisplayObjective) {}
1555 pub fn set_objective(&mut self, _p: &ClientboundSetObjective) {}
1556 pub fn set_passengers(&mut self, _p: &ClientboundSetPassengers) {}
1557 pub fn set_player_team(&mut self, p: &ClientboundSetPlayerTeam) {
1558 debug!("Got set player team packet {p:?}");
1559 }
1560 pub fn set_score(&mut self, _p: &ClientboundSetScore) {}
1561 pub fn set_simulation_distance(&mut self, _p: &ClientboundSetSimulationDistance) {}
1562 pub fn set_subtitle_text(&mut self, _p: &ClientboundSetSubtitleText) {}
1563 pub fn set_title_text(&mut self, _p: &ClientboundSetTitleText) {}
1564 pub fn set_titles_animation(&mut self, _p: &ClientboundSetTitlesAnimation) {}
1565 pub fn clear_titles(&mut self, _p: &ClientboundClearTitles) {}
1566 pub fn sound_entity(&mut self, _p: &ClientboundSoundEntity) {}
1567 pub fn stop_sound(&mut self, _p: &ClientboundStopSound) {}
1568 pub fn tab_list(&mut self, _p: &ClientboundTabList) {}
1569 pub fn tag_query(&mut self, _p: &ClientboundTagQuery) {}
1570 pub fn take_item_entity(&mut self, _p: &ClientboundTakeItemEntity) {}
1571 pub fn bundle_delimiter(&mut self, _p: &ClientboundBundleDelimiter) {}
1572 pub fn damage_event(&mut self, _p: &ClientboundDamageEvent) {}
1573 pub fn hurt_animation(&mut self, _p: &ClientboundHurtAnimation) {}
1574 pub fn ticking_state(&mut self, _p: &ClientboundTickingState) {}
1575 pub fn ticking_step(&mut self, _p: &ClientboundTickingStep) {}
1576 pub fn reset_score(&mut self, _p: &ClientboundResetScore) {}
1577 pub fn cookie_request(&mut self, p: &ClientboundCookieRequest) {
1578 debug!("Got cookie request packet {p:?}");
1579 as_system::<Commands>(self.ecs, |mut commands| {
1580 commands.trigger(RequestCookieEvent {
1581 entity: self.player,
1582 key: p.key.clone(),
1583 });
1584 });
1585 }
1586 pub fn store_cookie(&mut self, p: &ClientboundStoreCookie) {
1587 debug!("Got store cookie packet {p:?}");
1588 as_system::<Commands>(self.ecs, |mut commands| {
1589 commands.trigger(StoreCookieEvent {
1590 entity: self.player,
1591 key: p.key.clone(),
1592 payload: p.payload.clone(),
1593 });
1594 });
1595 }
1596 pub fn debug_sample(&mut self, _p: &ClientboundDebugSample) {}
1597 pub fn pong_response(&mut self, _p: &ClientboundPongResponse) {}
1598 pub fn transfer(&mut self, _p: &ClientboundTransfer) {}
1599 pub fn move_minecart_along_track(&mut self, _p: &ClientboundMoveMinecartAlongTrack) {}
1600 pub fn set_held_slot(&mut self, p: &ClientboundSetHeldSlot) {
1601 debug!("Got set held slot packet {p:?}");
1602
1603 as_system::<Query<&mut Inventory>>(self.ecs, |mut query| {
1604 let mut inventory = query.get_mut(self.player).unwrap();
1605 if p.slot <= 8 {
1606 inventory.selected_hotbar_slot = p.slot as u8;
1607 }
1608 });
1609 }
1610 pub fn set_player_inventory(&mut self, _p: &ClientboundSetPlayerInventory) {}
1611 pub fn projectile_power(&mut self, _p: &ClientboundProjectilePower) {}
1612 pub fn custom_report_details(&mut self, _p: &ClientboundCustomReportDetails) {}
1613 pub fn server_links(&mut self, _p: &ClientboundServerLinks) {}
1614 pub fn player_rotation(&mut self, _p: &ClientboundPlayerRotation) {}
1615 pub fn recipe_book_add(&mut self, _p: &ClientboundRecipeBookAdd) {}
1616 pub fn recipe_book_remove(&mut self, _p: &ClientboundRecipeBookRemove) {}
1617 pub fn recipe_book_settings(&mut self, _p: &ClientboundRecipeBookSettings) {}
1618 pub fn test_instance_block_status(&mut self, _p: &ClientboundTestInstanceBlockStatus) {}
1619 pub fn waypoint(&mut self, _p: &ClientboundWaypoint) {}
1620
1621 pub fn clear_dialog(&mut self, p: &ClientboundClearDialog) {
1622 debug!("Got clear dialog packet {p:?}");
1623 }
1624 pub fn show_dialog(&mut self, p: &ClientboundShowDialog) {
1625 debug!("Got show dialog packet {p:?}");
1626 }
1627
1628 pub fn debug_block_value(&mut self, p: &ClientboundDebugBlockValue) {
1629 debug!("Got debug block value packet {p:?}");
1630 }
1631 pub fn debug_chunk_value(&mut self, p: &ClientboundDebugChunkValue) {
1632 debug!("Got debug chunk value packet {p:?}");
1633 }
1634 pub fn debug_entity_value(&mut self, p: &ClientboundDebugEntityValue) {
1635 debug!("Got debug entity value packet {p:?}");
1636 }
1637
1638 pub fn debug_event(&mut self, p: &ClientboundDebugEvent) {
1639 debug!("Got debug event packet {p:?}");
1640 }
1641 pub fn game_test_highlight_pos(&mut self, p: &ClientboundGameTestHighlightPos) {
1642 debug!("Got game test highlight pos packet {p:?}");
1643 }
1644
1645 pub fn low_disk_space_warning(&mut self, p: &ClientboundLowDiskSpaceWarning) {
1646 debug!("Got low disk space warning packet {p:?}");
1647 }
1648
1649 pub fn game_rule_values(&mut self, p: &ClientboundGameRuleValues) {
1650 debug!("Got game rule values packet {p:?}");
1651 }
1652}
1653
1654struct MoveEntity {
1655 pub entity_id: MinecraftEntityId,
1656 pub delta: Option<PositionDelta8>,
1657 pub look_direction: Option<CompactLookDirection>,
1658 pub on_ground: bool,
1659}
1660
1661type MoveEntityQuery<'world, 'state, 'a> =
1662 Query<'world, 'state, (&'a mut Physics, &'a mut Position, &'a mut LookDirection)>;
1663
1664fn move_entity(
1665 player_entity: Entity,
1666 mut commands: Commands,
1667 p: MoveEntity,
1668 player_query: Query<(&EntityIdIndex, &WorldHolder)>,
1669 mut entity_query: MoveEntityQuery,
1670 entity_update_query: EntityUpdateQuery,
1671) {
1672 let (entity_id_index, world_holder) = player_query.get(player_entity).unwrap();
1673
1674 let entity_id = p.entity_id;
1675 let entity = entity_id_index.get_by_minecraft_entity(entity_id);
1676
1677 let Some(entity) = entity else {
1678 debug!("Got move entity packet for unknown entity id {entity_id}");
1680 return;
1681 };
1682
1683 let Ok((mut physics, mut position, mut look_direction)) = entity_query.get_mut(entity) else {
1684 debug!("Got move entity packet for entity with missing components {entity_id}");
1685 return;
1686 };
1687
1688 if !should_apply_entity_update(
1689 &mut commands,
1690 &mut world_holder.partial.write(),
1691 entity,
1692 entity_update_query,
1693 ) {
1694 return;
1695 }
1696
1697 if let Some(new_delta) = p.delta {
1698 let new_position = physics.vec_delta_codec.decode(&new_delta);
1699 physics.vec_delta_codec.set_base(new_position);
1700
1701 if new_position != **position {
1702 **position = new_position;
1703 }
1704 }
1705
1706 if let Some(new_look_direction) = p.look_direction {
1707 let new_look_direction = new_look_direction.into();
1708 if new_look_direction != *look_direction {
1709 *look_direction = new_look_direction;
1710 }
1711 }
1712
1713 physics.set_on_ground(p.on_ground);
1714}