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