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