azalea_client/plugins/packet/game/
mod.rs

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