azalea_client/plugins/packet/game/
mod.rs

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