1use std::{
2 collections::HashSet,
3 io::Cursor,
4 ops::Add,
5 sync::{Arc, Weak},
6};
7
8use azalea_chat::FormattedText;
9use azalea_core::{
10 game_type::GameMode,
11 math,
12 position::{ChunkPos, Vec3},
13 resource_location::ResourceLocation,
14};
15use azalea_entity::{
16 indexing::{EntityIdIndex, EntityUuidIndex},
17 metadata::{apply_metadata, Health},
18 Dead, EntityBundle, EntityKind, LastSentPosition, LoadedBy, LocalEntity, LookDirection,
19 Physics, Position, RelativeEntityUpdate,
20};
21use azalea_protocol::{
22 packets::{
23 game::{
24 c_player_combat_kill::ClientboundPlayerCombatKill,
25 s_accept_teleportation::ServerboundAcceptTeleportation,
26 s_configuration_acknowledged::ServerboundConfigurationAcknowledged,
27 s_keep_alive::ServerboundKeepAlive, s_move_player_pos_rot::ServerboundMovePlayerPosRot,
28 s_pong::ServerboundPong, ClientboundGamePacket, ServerboundGamePacket,
29 },
30 Packet,
31 },
32 read::deserialize_packet,
33};
34use azalea_world::{Instance, InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
35use bevy_ecs::{prelude::*, system::SystemState};
36use parking_lot::RwLock;
37use tracing::{debug, error, trace, warn};
38use uuid::Uuid;
39
40use crate::{
41 chat::{ChatPacket, ChatReceivedEvent},
42 chunks,
43 disconnect::DisconnectEvent,
44 inventory::{
45 ClientSideCloseContainerEvent, Inventory, MenuOpenedEvent, SetContainerContentEvent,
46 },
47 local_player::{
48 GameProfileComponent, Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, TabList,
49 },
50 movement::{KnockbackEvent, KnockbackType},
51 raw_connection::RawConnection,
52 ClientInformation, PlayerInfo,
53};
54
55#[derive(Event, Debug, Clone)]
76pub struct PacketEvent {
77 pub entity: Entity,
79 pub packet: Arc<ClientboundGamePacket>,
81}
82
83#[derive(Event, Debug, Clone)]
86pub struct AddPlayerEvent {
87 pub entity: Entity,
89 pub info: PlayerInfo,
90}
91#[derive(Event, Debug, Clone)]
94pub struct RemovePlayerEvent {
95 pub entity: Entity,
97 pub info: PlayerInfo,
98}
99#[derive(Event, Debug, Clone)]
102pub struct UpdatePlayerEvent {
103 pub entity: Entity,
105 pub info: PlayerInfo,
106}
107
108#[derive(Event, Debug, Clone)]
112pub struct DeathEvent {
113 pub entity: Entity,
114 pub packet: Option<ClientboundPlayerCombatKill>,
115}
116
117#[derive(Event, Debug, Clone)]
120pub struct KeepAliveEvent {
121 pub entity: Entity,
122 pub id: u64,
125}
126
127#[derive(Event, Debug, Clone)]
128pub struct ResourcePackEvent {
129 pub entity: Entity,
130 pub id: Uuid,
133 pub url: String,
134 pub hash: String,
135 pub required: bool,
136 pub prompt: Option<FormattedText>,
137}
138
139#[derive(Event, Debug, Clone)]
144pub struct InstanceLoadedEvent {
145 pub entity: Entity,
146 pub name: ResourceLocation,
147 pub instance: Weak<RwLock<Instance>>,
148}
149
150pub fn send_packet_events(
151 query: Query<(Entity, &RawConnection), With<LocalEntity>>,
152 mut packet_events: ResMut<Events<PacketEvent>>,
153) {
154 packet_events.clear();
158 for (player_entity, raw_connection) in &query {
159 let packets_lock = raw_connection.incoming_packet_queue();
160 let mut packets = packets_lock.lock();
161 if !packets.is_empty() {
162 for raw_packet in packets.iter() {
163 let packet =
164 match deserialize_packet::<ClientboundGamePacket>(&mut Cursor::new(raw_packet))
165 {
166 Ok(packet) => packet,
167 Err(err) => {
168 error!("failed to read packet: {err:?}");
169 debug!("packet bytes: {raw_packet:?}");
170 continue;
171 }
172 };
173 packet_events.send(PacketEvent {
174 entity: player_entity,
175 packet: Arc::new(packet),
176 });
177 }
178 packets.clear();
180 }
181 }
182}
183
184pub fn process_packet_events(ecs: &mut World) {
185 let mut events_owned = Vec::<(Entity, Arc<ClientboundGamePacket>)>::new();
186 {
187 let mut system_state = SystemState::<EventReader<PacketEvent>>::new(ecs);
188 let mut events = system_state.get_mut(ecs);
189 for PacketEvent {
190 entity: player_entity,
191 packet,
192 } in events.read()
193 {
194 events_owned.push((*player_entity, packet.clone()));
196 }
197 }
198 for (player_entity, packet) in events_owned {
199 let packet_clone = packet.clone();
200 let packet_ref = packet_clone.as_ref();
201 match packet_ref {
202 ClientboundGamePacket::Login(p) => {
203 debug!("Got login packet");
204
205 #[allow(clippy::type_complexity)]
206 let mut system_state: SystemState<(
207 Commands,
208 Query<(
209 &GameProfileComponent,
210 &ClientInformation,
211 Option<&mut InstanceName>,
212 Option<&mut LoadedBy>,
213 &mut EntityIdIndex,
214 &mut InstanceHolder,
215 )>,
216 EventWriter<InstanceLoadedEvent>,
217 ResMut<InstanceContainer>,
218 ResMut<EntityUuidIndex>,
219 EventWriter<SendPacketEvent>,
220 )> = SystemState::new(ecs);
221 let (
222 mut commands,
223 mut query,
224 mut instance_loaded_events,
225 mut instance_container,
226 mut entity_uuid_index,
227 mut send_packet_events,
228 ) = system_state.get_mut(ecs);
229 let (
230 game_profile,
231 client_information,
232 instance_name,
233 loaded_by,
234 mut entity_id_index,
235 mut instance_holder,
236 ) = query.get_mut(player_entity).unwrap();
237
238 {
239 let new_instance_name = p.common.dimension.clone();
240
241 if let Some(mut instance_name) = instance_name {
242 *instance_name = instance_name.clone();
243 } else {
244 commands
245 .entity(player_entity)
246 .insert(InstanceName(new_instance_name.clone()));
247 }
248
249 let Some(dimension_type_element) =
250 instance_holder.instance.read().registries.dimension_type()
251 else {
252 error!("Server didn't send dimension type registry, can't log in");
253 continue;
254 };
255
256 let dimension_name = ResourceLocation::new(&p.common.dimension.to_string());
257
258 let Some(dimension) = dimension_type_element.map.get(&dimension_name) else {
259 error!("No dimension_type with name {dimension_name}");
260 continue;
261 };
262
263 let weak_instance = instance_container.insert(
266 new_instance_name.clone(),
267 dimension.height,
268 dimension.min_y,
269 );
270 instance_loaded_events.send(InstanceLoadedEvent {
271 entity: player_entity,
272 name: new_instance_name.clone(),
273 instance: Arc::downgrade(&weak_instance),
274 });
275
276 *instance_holder.partial_instance.write() = PartialInstance::new(
281 azalea_world::chunk_storage::calculate_chunk_storage_range(
282 client_information.view_distance.into(),
283 ),
284 Some(player_entity),
287 );
288 {
289 let map = instance_holder.instance.read().registries.map.clone();
290 let new_registries = &mut weak_instance.write().registries;
291 for (registry_name, registry) in map {
293 new_registries.map.insert(registry_name, registry);
294 }
295 }
296 instance_holder.instance = weak_instance;
297
298 let entity_bundle = EntityBundle::new(
299 game_profile.uuid,
300 Vec3::default(),
301 azalea_registry::EntityKind::Player,
302 new_instance_name,
303 );
304 let entity_id = p.player_id;
305 commands.entity(player_entity).insert((
307 entity_id,
308 LocalGameMode {
309 current: p.common.game_type,
310 previous: p.common.previous_game_type.into(),
311 },
312 entity_bundle,
313 ));
314
315 azalea_entity::indexing::add_entity_to_indexes(
316 entity_id,
317 player_entity,
318 Some(game_profile.uuid),
319 &mut entity_id_index,
320 &mut entity_uuid_index,
321 &mut instance_holder.instance.write(),
322 );
323
324 if let Some(mut loaded_by) = loaded_by {
326 loaded_by.insert(player_entity);
327 } else {
328 commands
329 .entity(player_entity)
330 .insert(LoadedBy(HashSet::from_iter(vec![player_entity])));
331 }
332 }
333
334 debug!(
336 "Sending client information because login: {:?}",
337 client_information
338 );
339 send_packet_events.send(SendPacketEvent::new(player_entity,
340 azalea_protocol::packets::game::s_client_information::ServerboundClientInformation { information: client_information.clone() },
341 ));
342
343 system_state.apply(ecs);
344 }
345 ClientboundGamePacket::SetChunkCacheRadius(p) => {
346 debug!("Got set chunk cache radius packet {p:?}");
347 }
348
349 ClientboundGamePacket::ChunkBatchStart(_p) => {
350 debug!("Got chunk batch start");
352 let mut system_state: SystemState<EventWriter<chunks::ChunkBatchStartEvent>> =
353 SystemState::new(ecs);
354 let mut chunk_batch_start_events = system_state.get_mut(ecs);
355
356 chunk_batch_start_events.send(chunks::ChunkBatchStartEvent {
357 entity: player_entity,
358 });
359 }
360 ClientboundGamePacket::ChunkBatchFinished(p) => {
361 debug!("Got chunk batch finished {p:?}");
362
363 let mut system_state: SystemState<EventWriter<chunks::ChunkBatchFinishedEvent>> =
364 SystemState::new(ecs);
365 let mut chunk_batch_start_events = system_state.get_mut(ecs);
366
367 chunk_batch_start_events.send(chunks::ChunkBatchFinishedEvent {
368 entity: player_entity,
369 batch_size: p.batch_size,
370 });
371 }
372
373 ClientboundGamePacket::CustomPayload(p) => {
374 debug!("Got custom payload packet {p:?}");
375 }
376 ClientboundGamePacket::ChangeDifficulty(p) => {
377 debug!("Got difficulty packet {p:?}");
378 }
379 ClientboundGamePacket::Commands(_p) => {
380 debug!("Got declare commands packet");
381 }
382 ClientboundGamePacket::PlayerAbilities(p) => {
383 debug!("Got player abilities packet {p:?}");
384 let mut system_state: SystemState<Query<&mut PlayerAbilities>> =
385 SystemState::new(ecs);
386 let mut query = system_state.get_mut(ecs);
387 let mut player_abilities = query.get_mut(player_entity).unwrap();
388
389 *player_abilities = PlayerAbilities::from(p);
390 }
391 ClientboundGamePacket::SetCursorItem(p) => {
392 debug!("Got set cursor item packet {p:?}");
393 }
394 ClientboundGamePacket::UpdateTags(_p) => {
395 debug!("Got update tags packet");
396 }
397 ClientboundGamePacket::Disconnect(p) => {
398 warn!("Got disconnect packet {p:?}");
399 let mut system_state: SystemState<EventWriter<DisconnectEvent>> =
400 SystemState::new(ecs);
401 let mut disconnect_events = system_state.get_mut(ecs);
402 disconnect_events.send(DisconnectEvent {
403 entity: player_entity,
404 reason: Some(p.reason.clone()),
405 });
406 }
407 ClientboundGamePacket::UpdateRecipes(_p) => {
408 debug!("Got update recipes packet");
409 }
410 ClientboundGamePacket::EntityEvent(_p) => {
411 }
413 ClientboundGamePacket::PlayerPosition(p) => {
414 debug!("Got player position packet {p:?}");
415
416 #[allow(clippy::type_complexity)]
417 let mut system_state: SystemState<(
418 Query<(
419 &mut Physics,
420 &mut LookDirection,
421 &mut Position,
422 &mut LastSentPosition,
423 )>,
424 EventWriter<SendPacketEvent>,
425 )> = SystemState::new(ecs);
426 let (mut query, mut send_packet_events) = system_state.get_mut(ecs);
427 let Ok((mut physics, mut direction, mut position, mut last_sent_position)) =
428 query.get_mut(player_entity)
429 else {
430 continue;
431 };
432
433 **last_sent_position = **position;
434
435 fn apply_change<T: Add<Output = T>>(base: T, condition: bool, change: T) -> T {
436 if condition {
437 base + change
438 } else {
439 change
440 }
441 }
442
443 let new_x = apply_change(position.x, p.relative.x, p.change.pos.x);
444 let new_y = apply_change(position.y, p.relative.y, p.change.pos.y);
445 let new_z = apply_change(position.z, p.relative.z, p.change.pos.z);
446
447 let new_y_rot = apply_change(
448 direction.y_rot,
449 p.relative.y_rot,
450 p.change.look_direction.y_rot,
451 );
452 let new_x_rot = apply_change(
453 direction.x_rot,
454 p.relative.x_rot,
455 p.change.look_direction.x_rot,
456 );
457
458 let mut new_delta_from_rotations = physics.velocity;
459 if p.relative.rotate_delta {
460 let y_rot_delta = direction.y_rot - new_y_rot;
461 let x_rot_delta = direction.x_rot - new_x_rot;
462 new_delta_from_rotations = new_delta_from_rotations
463 .x_rot(math::to_radians(x_rot_delta as f64) as f32)
464 .y_rot(math::to_radians(y_rot_delta as f64) as f32);
465 }
466
467 let new_delta = Vec3::new(
468 apply_change(
469 new_delta_from_rotations.x,
470 p.relative.delta_x,
471 p.change.delta.x,
472 ),
473 apply_change(
474 new_delta_from_rotations.y,
475 p.relative.delta_y,
476 p.change.delta.y,
477 ),
478 apply_change(
479 new_delta_from_rotations.z,
480 p.relative.delta_z,
481 p.change.delta.z,
482 ),
483 );
484
485 physics.velocity = new_delta;
488
489 (direction.y_rot, direction.x_rot) = (new_y_rot, new_x_rot);
490
491 let new_pos = Vec3::new(new_x, new_y, new_z);
492 if new_pos != **position {
493 **position = new_pos;
494 }
495
496 physics.set_old_pos(&position);
498
499 send_packet_events.send(SendPacketEvent::new(
502 player_entity,
503 ServerboundAcceptTeleportation { id: p.id },
504 ));
505 send_packet_events.send(SendPacketEvent::new(
506 player_entity,
507 ServerboundMovePlayerPosRot {
508 pos: new_pos,
509 look_direction: LookDirection::new(new_y_rot, new_x_rot),
510 on_ground: false,
512 },
513 ));
514 }
515 ClientboundGamePacket::PlayerInfoUpdate(p) => {
516 debug!("Got player info packet {p:?}");
517
518 #[allow(clippy::type_complexity)]
519 let mut system_state: SystemState<(
520 Query<&mut TabList>,
521 EventWriter<AddPlayerEvent>,
522 EventWriter<UpdatePlayerEvent>,
523 ResMut<TabList>,
524 )> = SystemState::new(ecs);
525 let (
526 mut query,
527 mut add_player_events,
528 mut update_player_events,
529 mut tab_list_resource,
530 ) = system_state.get_mut(ecs);
531 let mut tab_list = query.get_mut(player_entity).unwrap();
532
533 for updated_info in &p.entries {
534 if p.actions.add_player {
536 let info = PlayerInfo {
537 profile: updated_info.profile.clone(),
538 uuid: updated_info.profile.uuid,
539 gamemode: updated_info.game_mode,
540 latency: updated_info.latency,
541 display_name: updated_info.display_name.clone(),
542 };
543 tab_list.insert(updated_info.profile.uuid, info.clone());
544 add_player_events.send(AddPlayerEvent {
545 entity: player_entity,
546 info: info.clone(),
547 });
548 } else if let Some(info) = tab_list.get_mut(&updated_info.profile.uuid) {
549 if p.actions.update_game_mode {
552 info.gamemode = updated_info.game_mode;
553 }
554 if p.actions.update_latency {
555 info.latency = updated_info.latency;
556 }
557 if p.actions.update_display_name {
558 info.display_name.clone_from(&updated_info.display_name);
559 }
560 update_player_events.send(UpdatePlayerEvent {
561 entity: player_entity,
562 info: info.clone(),
563 });
564 } else {
565 let uuid = updated_info.profile.uuid;
566 #[cfg(debug_assertions)]
567 warn!("Ignoring PlayerInfoUpdate for unknown player {uuid}");
568 #[cfg(not(debug_assertions))]
569 debug!("Ignoring PlayerInfoUpdate for unknown player {uuid}");
570 }
571 }
572
573 *tab_list_resource = tab_list.clone();
574 }
575 ClientboundGamePacket::PlayerInfoRemove(p) => {
576 let mut system_state: SystemState<(
577 Query<&mut TabList>,
578 EventWriter<RemovePlayerEvent>,
579 ResMut<TabList>,
580 )> = SystemState::new(ecs);
581 let (mut query, mut remove_player_events, mut tab_list_resource) =
582 system_state.get_mut(ecs);
583 let mut tab_list = query.get_mut(player_entity).unwrap();
584
585 for uuid in &p.profile_ids {
586 if let Some(info) = tab_list.remove(uuid) {
587 remove_player_events.send(RemovePlayerEvent {
588 entity: player_entity,
589 info,
590 });
591 }
592 tab_list_resource.remove(uuid);
593 }
594 }
595 ClientboundGamePacket::SetChunkCacheCenter(p) => {
596 debug!("Got chunk cache center packet {p:?}");
597
598 let mut system_state: SystemState<Query<&mut InstanceHolder>> =
599 SystemState::new(ecs);
600 let mut query = system_state.get_mut(ecs);
601 let instance_holder = query.get_mut(player_entity).unwrap();
602 let mut partial_world = instance_holder.partial_instance.write();
603
604 partial_world
605 .chunks
606 .update_view_center(ChunkPos::new(p.x, p.z));
607 }
608 ClientboundGamePacket::ChunksBiomes(_) => {}
609 ClientboundGamePacket::LightUpdate(_p) => {
610 }
612 ClientboundGamePacket::LevelChunkWithLight(p) => {
613 debug!("Got chunk with light packet {} {}", p.x, p.z);
614
615 let mut system_state: SystemState<EventWriter<chunks::ReceiveChunkEvent>> =
616 SystemState::new(ecs);
617 let mut receive_chunk_events = system_state.get_mut(ecs);
618 receive_chunk_events.send(chunks::ReceiveChunkEvent {
619 entity: player_entity,
620 packet: p.clone(),
621 });
622 }
623 ClientboundGamePacket::AddEntity(p) => {
624 debug!("Got add entity packet {p:?}");
625
626 #[allow(clippy::type_complexity)]
627 let mut system_state: SystemState<(
628 Commands,
629 Query<(&mut EntityIdIndex, Option<&InstanceName>, Option<&TabList>)>,
630 Query<&mut LoadedBy>,
631 Query<Entity>,
632 Res<InstanceContainer>,
633 ResMut<EntityUuidIndex>,
634 )> = SystemState::new(ecs);
635 let (
636 mut commands,
637 mut query,
638 mut loaded_by_query,
639 entity_query,
640 instance_container,
641 mut entity_uuid_index,
642 ) = system_state.get_mut(ecs);
643 let (mut entity_id_index, instance_name, tab_list) =
644 query.get_mut(player_entity).unwrap();
645
646 let entity_id = p.id;
647
648 let Some(instance_name) = instance_name else {
649 warn!("got add player packet but we haven't gotten a login packet yet");
650 continue;
651 };
652
653 let instance = instance_container.get(instance_name).unwrap();
655 if let Some(&ecs_entity) = instance.read().entity_by_id.get(&entity_id) {
656 let Ok(mut loaded_by) = loaded_by_query.get_mut(ecs_entity) else {
658 let entity_in_ecs = entity_query.get(ecs_entity).is_ok();
662
663 if entity_in_ecs {
664 error!("LoadedBy for entity {entity_id:?} ({ecs_entity:?}) isn't in the ecs, but the entity is in entity_by_id");
665 } else {
666 error!("Entity {entity_id:?} ({ecs_entity:?}) isn't in the ecs, but the entity is in entity_by_id");
667 }
668 continue;
669 };
670 loaded_by.insert(player_entity);
671
672 entity_id_index.insert(entity_id, ecs_entity);
674
675 debug!("added to LoadedBy of entity {ecs_entity:?} with id {entity_id:?}");
676 continue;
677 };
678
679 let bundle = p.as_entity_bundle((**instance_name).clone());
682 let mut spawned =
683 commands.spawn((entity_id, LoadedBy(HashSet::from([player_entity])), bundle));
684 let ecs_entity: Entity = spawned.id();
685 debug!("spawned entity {ecs_entity:?} with id {entity_id:?}");
686
687 azalea_entity::indexing::add_entity_to_indexes(
688 entity_id,
689 ecs_entity,
690 Some(p.uuid),
691 &mut entity_id_index,
692 &mut entity_uuid_index,
693 &mut instance.write(),
694 );
695
696 if let Some(tab_list) = tab_list {
698 if let Some(player_info) = tab_list.get(&p.uuid) {
702 spawned.insert(GameProfileComponent(player_info.profile.clone()));
703 }
704 }
705
706 p.apply_metadata(&mut spawned);
709
710 system_state.apply(ecs);
711 }
712 ClientboundGamePacket::SetEntityData(p) => {
713 debug!("Got set entity data packet {p:?}");
714
715 #[allow(clippy::type_complexity)]
716 let mut system_state: SystemState<(
717 Commands,
718 Query<(&EntityIdIndex, &InstanceHolder)>,
719 Query<&EntityKind>,
720 )> = SystemState::new(ecs);
721 let (mut commands, mut query, entity_kind_query) = system_state.get_mut(ecs);
722 let (entity_id_index, instance_holder) = query.get_mut(player_entity).unwrap();
723
724 let entity = entity_id_index.get(p.id);
725
726 let Some(entity) = entity else {
727 debug!("Server sent an entity data packet for an entity id ({}) that we don't know about", p.id);
729 continue;
730 };
731 let entity_kind = *entity_kind_query.get(entity).unwrap();
732
733 let packed_items = p.packed_items.clone().to_vec();
734
735 commands.entity(entity).queue(RelativeEntityUpdate {
738 partial_world: instance_holder.partial_instance.clone(),
739 update: Box::new(move |entity| {
740 let entity_id = entity.id();
741 entity.world_scope(|world| {
742 let mut commands_system_state = SystemState::<Commands>::new(world);
743 let mut commands = commands_system_state.get_mut(world);
744 let mut entity_commands = commands.entity(entity_id);
745 if let Err(e) =
746 apply_metadata(&mut entity_commands, *entity_kind, packed_items)
747 {
748 warn!("{e}");
749 }
750 commands_system_state.apply(world);
751 });
752 }),
753 });
754
755 system_state.apply(ecs);
756 }
757 ClientboundGamePacket::UpdateAttributes(_p) => {
758 }
760 ClientboundGamePacket::SetEntityMotion(p) => {
761 let mut system_state: SystemState<(
765 Commands,
766 Query<(&EntityIdIndex, &InstanceHolder)>,
767 )> = SystemState::new(ecs);
768 let (mut commands, mut query) = system_state.get_mut(ecs);
769 let (entity_id_index, instance_holder) = query.get_mut(player_entity).unwrap();
770
771 let Some(entity) = entity_id_index.get(p.id) else {
772 debug!(
776 "Got set entity motion packet for unknown entity id {}",
777 p.id
778 );
779 continue;
780 };
781
782 let knockback = KnockbackType::Set(Vec3 {
786 x: p.xa as f64 / 8000.,
787 y: p.ya as f64 / 8000.,
788 z: p.za as f64 / 8000.,
789 });
790
791 commands.entity(entity).queue(RelativeEntityUpdate {
792 partial_world: instance_holder.partial_instance.clone(),
793 update: Box::new(move |entity_mut| {
794 entity_mut.world_scope(|world| {
795 world.send_event(KnockbackEvent { entity, knockback })
796 });
797 }),
798 });
799
800 system_state.apply(ecs);
801 }
802 ClientboundGamePacket::SetEntityLink(p) => {
803 debug!("Got set entity link packet {p:?}");
804 }
805 ClientboundGamePacket::InitializeBorder(p) => {
806 debug!("Got initialize border packet {p:?}");
807 }
808 ClientboundGamePacket::SetTime(_p) => {
809 }
811 ClientboundGamePacket::SetDefaultSpawnPosition(p) => {
812 debug!("Got set default spawn position packet {p:?}");
813 }
814 ClientboundGamePacket::SetHealth(p) => {
815 debug!("Got set health packet {p:?}");
816
817 let mut system_state: SystemState<Query<(&mut Health, &mut Hunger)>> =
818 SystemState::new(ecs);
819 let mut query = system_state.get_mut(ecs);
820 let (mut health, mut hunger) = query.get_mut(player_entity).unwrap();
821
822 **health = p.health;
823 (hunger.food, hunger.saturation) = (p.food, p.saturation);
824
825 }
829 ClientboundGamePacket::SetExperience(p) => {
830 debug!("Got set experience packet {p:?}");
831 }
832 ClientboundGamePacket::TeleportEntity(p) => {
833 let mut system_state: SystemState<(
834 Commands,
835 Query<(&EntityIdIndex, &InstanceHolder)>,
836 )> = SystemState::new(ecs);
837 let (mut commands, mut query) = system_state.get_mut(ecs);
838 let (entity_id_index, instance_holder) = query.get_mut(player_entity).unwrap();
839
840 let Some(entity) = entity_id_index.get(p.id) else {
841 warn!("Got teleport entity packet for unknown entity id {}", p.id);
842 continue;
843 };
844
845 let new_pos = p.change.pos;
846 let new_look_direction = LookDirection {
847 x_rot: (p.change.look_direction.x_rot as i32 * 360) as f32 / 256.,
848 y_rot: (p.change.look_direction.y_rot as i32 * 360) as f32 / 256.,
849 };
850 commands.entity(entity).queue(RelativeEntityUpdate {
851 partial_world: instance_holder.partial_instance.clone(),
852 update: Box::new(move |entity| {
853 let mut position = entity.get_mut::<Position>().unwrap();
854 if new_pos != **position {
855 **position = new_pos;
856 }
857 let position = *position;
858 let mut look_direction = entity.get_mut::<LookDirection>().unwrap();
859 if new_look_direction != *look_direction {
860 *look_direction = new_look_direction;
861 }
862 let mut physics = entity.get_mut::<Physics>().unwrap();
864 physics.set_old_pos(&position);
865 }),
866 });
867
868 system_state.apply(ecs);
869 }
870 ClientboundGamePacket::UpdateAdvancements(p) => {
871 debug!("Got update advancements packet {p:?}");
872 }
873 ClientboundGamePacket::RotateHead(_p) => {
874 }
876 ClientboundGamePacket::MoveEntityPos(p) => {
877 let mut system_state: SystemState<(
878 Commands,
879 Query<(&EntityIdIndex, &InstanceHolder)>,
880 )> = SystemState::new(ecs);
881 let (mut commands, mut query) = system_state.get_mut(ecs);
882 let (entity_id_index, instance_holder) = query.get_mut(player_entity).unwrap();
883
884 debug!("Got move entity pos packet {p:?}");
885
886 let Some(entity) = entity_id_index.get(p.entity_id) else {
887 debug!(
888 "Got move entity pos packet for unknown entity id {}",
889 p.entity_id
890 );
891 continue;
892 };
893
894 let new_delta = p.delta.clone();
895 let new_on_ground = p.on_ground;
896 commands.entity(entity).queue(RelativeEntityUpdate {
897 partial_world: instance_holder.partial_instance.clone(),
898 update: Box::new(move |entity_mut| {
899 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
900 let new_pos = physics.vec_delta_codec.decode(
901 new_delta.xa as i64,
902 new_delta.ya as i64,
903 new_delta.za as i64,
904 );
905 physics.vec_delta_codec.set_base(new_pos);
906 physics.set_on_ground(new_on_ground);
907
908 let mut position = entity_mut.get_mut::<Position>().unwrap();
909 if new_pos != **position {
910 **position = new_pos;
911 }
912 }),
913 });
914
915 system_state.apply(ecs);
916 }
917 ClientboundGamePacket::MoveEntityPosRot(p) => {
918 let mut system_state: SystemState<(
919 Commands,
920 Query<(&EntityIdIndex, &InstanceHolder)>,
921 )> = SystemState::new(ecs);
922 let (mut commands, mut query) = system_state.get_mut(ecs);
923 let (entity_id_index, instance_holder) = query.get_mut(player_entity).unwrap();
924
925 debug!("Got move entity pos rot packet {p:?}");
926
927 let entity = entity_id_index.get(p.entity_id);
928
929 if let Some(entity) = entity {
930 let new_delta = p.delta.clone();
931 let new_look_direction = LookDirection {
932 x_rot: (p.x_rot as i32 * 360) as f32 / 256.,
933 y_rot: (p.y_rot as i32 * 360) as f32 / 256.,
934 };
935
936 let new_on_ground = p.on_ground;
937
938 commands.entity(entity).queue(RelativeEntityUpdate {
939 partial_world: instance_holder.partial_instance.clone(),
940 update: Box::new(move |entity_mut| {
941 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
942 let new_pos = physics.vec_delta_codec.decode(
943 new_delta.xa as i64,
944 new_delta.ya as i64,
945 new_delta.za as i64,
946 );
947 physics.vec_delta_codec.set_base(new_pos);
948 physics.set_on_ground(new_on_ground);
949
950 let mut position = entity_mut.get_mut::<Position>().unwrap();
951 if new_pos != **position {
952 **position = new_pos;
953 }
954
955 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
956 if new_look_direction != *look_direction {
957 *look_direction = new_look_direction;
958 }
959 }),
960 });
961 } else {
962 debug!(
964 "Got move entity pos rot packet for unknown entity id {}",
965 p.entity_id
966 );
967 }
968
969 system_state.apply(ecs);
970 }
971
972 ClientboundGamePacket::MoveEntityRot(p) => {
973 let mut system_state: SystemState<(
974 Commands,
975 Query<(&EntityIdIndex, &InstanceHolder)>,
976 )> = SystemState::new(ecs);
977 let (mut commands, mut query) = system_state.get_mut(ecs);
978 let (entity_id_index, instance_holder) = query.get_mut(player_entity).unwrap();
979
980 let entity = entity_id_index.get(p.entity_id);
981
982 if let Some(entity) = entity {
983 let new_look_direction = LookDirection {
984 x_rot: (p.x_rot as i32 * 360) as f32 / 256.,
985 y_rot: (p.y_rot as i32 * 360) as f32 / 256.,
986 };
987 let new_on_ground = p.on_ground;
988
989 commands.entity(entity).queue(RelativeEntityUpdate {
990 partial_world: instance_holder.partial_instance.clone(),
991 update: Box::new(move |entity_mut| {
992 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
993 physics.set_on_ground(new_on_ground);
994
995 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
996 if new_look_direction != *look_direction {
997 *look_direction = new_look_direction;
998 }
999 }),
1000 });
1001 } else {
1002 warn!(
1003 "Got move entity rot packet for unknown entity id {}",
1004 p.entity_id
1005 );
1006 }
1007
1008 system_state.apply(ecs);
1009 }
1010 ClientboundGamePacket::KeepAlive(p) => {
1011 debug!("Got keep alive packet {p:?} for {player_entity:?}");
1012
1013 let mut system_state: SystemState<(
1014 EventWriter<KeepAliveEvent>,
1015 EventWriter<SendPacketEvent>,
1016 )> = SystemState::new(ecs);
1017 let (mut keepalive_events, mut send_packet_events) = system_state.get_mut(ecs);
1018
1019 keepalive_events.send(KeepAliveEvent {
1020 entity: player_entity,
1021 id: p.id,
1022 });
1023 send_packet_events.send(SendPacketEvent::new(
1024 player_entity,
1025 ServerboundKeepAlive { id: p.id },
1026 ));
1027 }
1028 ClientboundGamePacket::RemoveEntities(p) => {
1029 debug!("Got remove entities packet {p:?}");
1030
1031 let mut system_state: SystemState<(
1032 Query<&mut EntityIdIndex>,
1033 Query<&mut LoadedBy>,
1034 )> = SystemState::new(ecs);
1035
1036 let (mut query, mut entity_query) = system_state.get_mut(ecs);
1037 let Ok(mut entity_id_index) = query.get_mut(player_entity) else {
1038 warn!("our local player doesn't have EntityIdIndex");
1039 continue;
1040 };
1041
1042 for &id in &p.entity_ids {
1043 let Some(entity) = entity_id_index.remove(id) else {
1044 debug!("Tried to remove entity with id {id} but it wasn't in the EntityIdIndex");
1045 continue;
1046 };
1047 let Ok(mut loaded_by) = entity_query.get_mut(entity) else {
1048 warn!(
1049 "tried to despawn entity {id} but it doesn't have a LoadedBy component",
1050 );
1051 continue;
1052 };
1053
1054 loaded_by.remove(&player_entity);
1061 }
1062 }
1063 ClientboundGamePacket::PlayerChat(p) => {
1064 debug!("Got player chat packet {p:?}");
1065
1066 let mut system_state: SystemState<EventWriter<ChatReceivedEvent>> =
1067 SystemState::new(ecs);
1068 let mut chat_events = system_state.get_mut(ecs);
1069
1070 chat_events.send(ChatReceivedEvent {
1071 entity: player_entity,
1072 packet: ChatPacket::Player(Arc::new(p.clone())),
1073 });
1074 }
1075 ClientboundGamePacket::SystemChat(p) => {
1076 debug!("Got system chat packet {p:?}");
1077
1078 let mut system_state: SystemState<EventWriter<ChatReceivedEvent>> =
1079 SystemState::new(ecs);
1080 let mut chat_events = system_state.get_mut(ecs);
1081
1082 chat_events.send(ChatReceivedEvent {
1083 entity: player_entity,
1084 packet: ChatPacket::System(Arc::new(p.clone())),
1085 });
1086 }
1087 ClientboundGamePacket::DisguisedChat(p) => {
1088 debug!("Got disguised chat packet {p:?}");
1089
1090 let mut system_state: SystemState<EventWriter<ChatReceivedEvent>> =
1091 SystemState::new(ecs);
1092 let mut chat_events = system_state.get_mut(ecs);
1093
1094 chat_events.send(ChatReceivedEvent {
1095 entity: player_entity,
1096 packet: ChatPacket::Disguised(Arc::new(p.clone())),
1097 });
1098 }
1099 ClientboundGamePacket::Sound(_p) => {
1100 }
1102 ClientboundGamePacket::LevelEvent(p) => {
1103 debug!("Got level event packet {p:?}");
1104 }
1105 ClientboundGamePacket::BlockUpdate(p) => {
1106 debug!("Got block update packet {p:?}");
1107
1108 let mut system_state: SystemState<Query<&mut InstanceHolder>> =
1109 SystemState::new(ecs);
1110 let mut query = system_state.get_mut(ecs);
1111 let local_player = query.get_mut(player_entity).unwrap();
1112
1113 let world = local_player.instance.write();
1114
1115 world.chunks.set_block_state(&p.pos, p.block_state);
1116 }
1117 ClientboundGamePacket::Animate(p) => {
1118 debug!("Got animate packet {p:?}");
1119 }
1120 ClientboundGamePacket::SectionBlocksUpdate(p) => {
1121 debug!("Got section blocks update packet {p:?}");
1122 let mut system_state: SystemState<Query<&mut InstanceHolder>> =
1123 SystemState::new(ecs);
1124 let mut query = system_state.get_mut(ecs);
1125 let local_player = query.get_mut(player_entity).unwrap();
1126
1127 let world = local_player.instance.write();
1128
1129 for state in &p.states {
1130 world
1131 .chunks
1132 .set_block_state(&(p.section_pos + state.pos), state.state);
1133 }
1134 }
1135 ClientboundGamePacket::GameEvent(p) => {
1136 use azalea_protocol::packets::game::c_game_event::EventType;
1137
1138 debug!("Got game event packet {p:?}");
1139
1140 #[allow(clippy::single_match)]
1141 match p.event {
1142 EventType::ChangeGameMode => {
1143 let mut system_state: SystemState<Query<&mut LocalGameMode>> =
1144 SystemState::new(ecs);
1145 let mut query = system_state.get_mut(ecs);
1146 let mut local_game_mode = query.get_mut(player_entity).unwrap();
1147 if let Some(new_game_mode) = GameMode::from_id(p.param as u8) {
1148 local_game_mode.current = new_game_mode;
1149 }
1150 }
1151 _ => {}
1152 }
1153 }
1154 ClientboundGamePacket::LevelParticles(p) => {
1155 debug!("Got level particles packet {p:?}");
1156 }
1157 ClientboundGamePacket::ServerData(p) => {
1158 debug!("Got server data packet {p:?}");
1159 }
1160 ClientboundGamePacket::SetEquipment(p) => {
1161 debug!("Got set equipment packet {p:?}");
1162 }
1163 ClientboundGamePacket::UpdateMobEffect(p) => {
1164 debug!("Got update mob effect packet {p:?}");
1165 }
1166 ClientboundGamePacket::AddExperienceOrb(_) => {}
1167 ClientboundGamePacket::AwardStats(_) => {}
1168 ClientboundGamePacket::BlockChangedAck(_) => {}
1169 ClientboundGamePacket::BlockDestruction(_) => {}
1170 ClientboundGamePacket::BlockEntityData(_) => {}
1171 ClientboundGamePacket::BlockEvent(p) => {
1172 debug!("Got block event packet {p:?}");
1173 }
1174 ClientboundGamePacket::BossEvent(_) => {}
1175 ClientboundGamePacket::CommandSuggestions(_) => {}
1176 ClientboundGamePacket::ContainerSetContent(p) => {
1177 debug!("Got container set content packet {p:?}");
1178
1179 let mut system_state: SystemState<(
1180 Query<&mut Inventory>,
1181 EventWriter<SetContainerContentEvent>,
1182 )> = SystemState::new(ecs);
1183 let (mut query, mut events) = system_state.get_mut(ecs);
1184 let mut inventory = query.get_mut(player_entity).unwrap();
1185
1186 if p.container_id == 0 {
1188 for (i, slot) in p.items.iter().enumerate() {
1190 if let Some(slot_mut) = inventory.inventory_menu.slot_mut(i) {
1191 *slot_mut = slot.clone();
1192 }
1193 }
1194 } else {
1195 events.send(SetContainerContentEvent {
1196 entity: player_entity,
1197 slots: p.items.clone(),
1198 container_id: p.container_id,
1199 });
1200 }
1201 }
1202 ClientboundGamePacket::ContainerSetData(p) => {
1203 debug!("Got container set data packet {p:?}");
1204 }
1216 ClientboundGamePacket::ContainerSetSlot(p) => {
1217 debug!("Got container set slot packet {p:?}");
1218
1219 let mut system_state: SystemState<Query<&mut Inventory>> = SystemState::new(ecs);
1220 let mut query = system_state.get_mut(ecs);
1221 let mut inventory = query.get_mut(player_entity).unwrap();
1222
1223 if p.container_id == -1 {
1224 inventory.carried = p.item_stack.clone();
1226 } else if p.container_id == -2 {
1227 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1228 *slot = p.item_stack.clone();
1229 }
1230 } else {
1231 let is_creative_mode_and_inventory_closed = false;
1232 if p.container_id == 0
1235 && azalea_inventory::Player::is_hotbar_slot(p.slot.into())
1236 {
1237 if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
1240 *slot = p.item_stack.clone();
1241 }
1242 } else if p.container_id == inventory.id
1243 && (p.container_id != 0 || !is_creative_mode_and_inventory_closed)
1244 {
1245 if let Some(slot) = inventory.menu_mut().slot_mut(p.slot.into()) {
1247 *slot = p.item_stack.clone();
1248 inventory.state_id = p.state_id;
1249 }
1250 }
1251 }
1252 }
1253 ClientboundGamePacket::ContainerClose(_p) => {
1254 let mut system_state: SystemState<EventWriter<ClientSideCloseContainerEvent>> =
1256 SystemState::new(ecs);
1257 let mut client_side_close_container_events = system_state.get_mut(ecs);
1258 client_side_close_container_events.send(ClientSideCloseContainerEvent {
1259 entity: player_entity,
1260 });
1261 }
1262 ClientboundGamePacket::Cooldown(_) => {}
1263 ClientboundGamePacket::CustomChatCompletions(_) => {}
1264 ClientboundGamePacket::DeleteChat(_) => {}
1265 ClientboundGamePacket::Explode(p) => {
1266 trace!("Got explode packet {p:?}");
1267 if let Some(knockback) = p.knockback {
1268 let mut system_state: SystemState<EventWriter<KnockbackEvent>> =
1269 SystemState::new(ecs);
1270 let mut knockback_events = system_state.get_mut(ecs);
1271
1272 knockback_events.send(KnockbackEvent {
1273 entity: player_entity,
1274 knockback: KnockbackType::Set(knockback),
1275 });
1276
1277 system_state.apply(ecs);
1278 }
1279 }
1280 ClientboundGamePacket::ForgetLevelChunk(p) => {
1281 debug!("Got forget level chunk packet {p:?}");
1282
1283 let mut system_state: SystemState<Query<&mut InstanceHolder>> =
1284 SystemState::new(ecs);
1285 let mut query = system_state.get_mut(ecs);
1286 let local_player = query.get_mut(player_entity).unwrap();
1287
1288 let mut partial_instance = local_player.partial_instance.write();
1289
1290 partial_instance.chunks.limited_set(&p.pos, None);
1291 }
1292 ClientboundGamePacket::HorseScreenOpen(_) => {}
1293 ClientboundGamePacket::MapItemData(_) => {}
1294 ClientboundGamePacket::MerchantOffers(_) => {}
1295 ClientboundGamePacket::MoveVehicle(_) => {}
1296 ClientboundGamePacket::OpenBook(_) => {}
1297 ClientboundGamePacket::OpenScreen(p) => {
1298 debug!("Got open screen packet {p:?}");
1299 let mut system_state: SystemState<EventWriter<MenuOpenedEvent>> =
1300 SystemState::new(ecs);
1301 let mut menu_opened_events = system_state.get_mut(ecs);
1302 menu_opened_events.send(MenuOpenedEvent {
1303 entity: player_entity,
1304 window_id: p.container_id,
1305 menu_type: p.menu_type,
1306 title: p.title.to_owned(),
1307 });
1308 }
1309 ClientboundGamePacket::OpenSignEditor(_) => {}
1310 ClientboundGamePacket::Ping(p) => {
1311 debug!("Got ping packet {p:?}");
1312
1313 let mut system_state: SystemState<EventWriter<SendPacketEvent>> =
1314 SystemState::new(ecs);
1315 let mut send_packet_events = system_state.get_mut(ecs);
1316
1317 send_packet_events.send(SendPacketEvent::new(
1318 player_entity,
1319 ServerboundPong { id: p.id },
1320 ));
1321 }
1322 ClientboundGamePacket::PlaceGhostRecipe(_) => {}
1323 ClientboundGamePacket::PlayerCombatEnd(_) => {}
1324 ClientboundGamePacket::PlayerCombatEnter(_) => {}
1325 ClientboundGamePacket::PlayerCombatKill(p) => {
1326 debug!("Got player kill packet {p:?}");
1327
1328 #[allow(clippy::type_complexity)]
1329 let mut system_state: SystemState<(
1330 Commands,
1331 Query<(&MinecraftEntityId, Option<&Dead>)>,
1332 EventWriter<DeathEvent>,
1333 )> = SystemState::new(ecs);
1334 let (mut commands, mut query, mut death_events) = system_state.get_mut(ecs);
1335 let (entity_id, dead) = query.get_mut(player_entity).unwrap();
1336
1337 if *entity_id == p.player_id && dead.is_none() {
1338 commands.entity(player_entity).insert(Dead);
1339 death_events.send(DeathEvent {
1340 entity: player_entity,
1341 packet: Some(p.clone()),
1342 });
1343 }
1344
1345 system_state.apply(ecs);
1346 }
1347 ClientboundGamePacket::PlayerLookAt(_) => {}
1348 ClientboundGamePacket::RemoveMobEffect(_) => {}
1349 ClientboundGamePacket::ResourcePackPush(p) => {
1350 debug!("Got resource pack packet {p:?}");
1351
1352 let mut system_state: SystemState<EventWriter<ResourcePackEvent>> =
1353 SystemState::new(ecs);
1354 let mut resource_pack_events = system_state.get_mut(ecs);
1355
1356 resource_pack_events.send(ResourcePackEvent {
1357 entity: player_entity,
1358 id: p.id,
1359 url: p.url.to_owned(),
1360 hash: p.hash.to_owned(),
1361 required: p.required,
1362 prompt: p.prompt.to_owned(),
1363 });
1364
1365 system_state.apply(ecs);
1366 }
1367 ClientboundGamePacket::ResourcePackPop(_) => {}
1368 ClientboundGamePacket::Respawn(p) => {
1369 debug!("Got respawn packet {p:?}");
1370
1371 #[allow(clippy::type_complexity)]
1372 let mut system_state: SystemState<(
1373 Commands,
1374 Query<(
1375 &mut InstanceHolder,
1376 &GameProfileComponent,
1377 &ClientInformation,
1378 )>,
1379 EventWriter<InstanceLoadedEvent>,
1380 ResMut<InstanceContainer>,
1381 )> = SystemState::new(ecs);
1382 let (mut commands, mut query, mut instance_loaded_events, mut instance_container) =
1383 system_state.get_mut(ecs);
1384 let (mut instance_holder, game_profile, client_information) =
1385 query.get_mut(player_entity).unwrap();
1386
1387 {
1388 let new_instance_name = p.common.dimension.clone();
1389
1390 let Some(dimension_type_element) =
1391 instance_holder.instance.read().registries.dimension_type()
1392 else {
1393 error!("Server didn't send dimension type registry, can't log in.");
1394 continue;
1395 };
1396
1397 let dimension_name = ResourceLocation::new(&p.common.dimension.to_string());
1398
1399 let Some(dimension) = dimension_type_element.map.get(&dimension_name) else {
1400 error!("No dimension_type with name {dimension_name}");
1401 continue;
1402 };
1403
1404 let weak_instance = instance_container.insert(
1407 new_instance_name.clone(),
1408 dimension.height,
1409 dimension.min_y,
1410 );
1411 instance_loaded_events.send(InstanceLoadedEvent {
1412 entity: player_entity,
1413 name: new_instance_name.clone(),
1414 instance: Arc::downgrade(&weak_instance),
1415 });
1416
1417 *instance_holder.partial_instance.write() = PartialInstance::new(
1422 azalea_world::chunk_storage::calculate_chunk_storage_range(
1423 client_information.view_distance.into(),
1424 ),
1425 Some(player_entity),
1426 );
1427 instance_holder.instance = weak_instance;
1428
1429 let entity_bundle = EntityBundle::new(
1431 game_profile.uuid,
1432 Vec3::default(),
1433 azalea_registry::EntityKind::Player,
1434 new_instance_name,
1435 );
1436 commands.entity(player_entity).insert((
1438 LocalGameMode {
1439 current: p.common.game_type,
1440 previous: p.common.previous_game_type.into(),
1441 },
1442 entity_bundle,
1443 ));
1444 }
1445
1446 commands.entity(player_entity).remove::<Dead>();
1448
1449 system_state.apply(ecs);
1450 }
1451
1452 ClientboundGamePacket::StartConfiguration(_p) => {
1453 let mut system_state: SystemState<(Commands, EventWriter<SendPacketEvent>)> =
1454 SystemState::new(ecs);
1455 let (mut commands, mut packet_events) = system_state.get_mut(ecs);
1456
1457 packet_events.send(SendPacketEvent::new(
1458 player_entity,
1459 ServerboundConfigurationAcknowledged {},
1460 ));
1461
1462 commands
1463 .entity(player_entity)
1464 .insert(crate::client::InConfigState)
1465 .remove::<crate::JoinedClientBundle>();
1466
1467 system_state.apply(ecs);
1468 }
1469
1470 ClientboundGamePacket::EntityPositionSync(p) => {
1471 let mut system_state: SystemState<(
1472 Commands,
1473 Query<(&EntityIdIndex, &InstanceHolder)>,
1474 )> = SystemState::new(ecs);
1475 let (mut commands, mut query) = system_state.get_mut(ecs);
1476 let (entity_id_index, instance_holder) = query.get_mut(player_entity).unwrap();
1477
1478 let Some(entity) = entity_id_index.get(p.id) else {
1479 debug!("Got teleport entity packet for unknown entity id {}", p.id);
1480 continue;
1481 };
1482
1483 let new_position = p.values.pos;
1484 let new_on_ground = p.on_ground;
1485 let new_look_direction = p.values.look_direction;
1486
1487 commands.entity(entity).queue(RelativeEntityUpdate {
1488 partial_world: instance_holder.partial_instance.clone(),
1489 update: Box::new(move |entity_mut| {
1490 let is_local_entity = entity_mut.get::<LocalEntity>().is_some();
1491 let mut physics = entity_mut.get_mut::<Physics>().unwrap();
1492
1493 physics.vec_delta_codec.set_base(new_position);
1494
1495 if is_local_entity {
1496 debug!("Ignoring entity position sync packet for local player");
1497 return;
1498 }
1499
1500 physics.set_on_ground(new_on_ground);
1501
1502 let mut last_sent_position =
1503 entity_mut.get_mut::<LastSentPosition>().unwrap();
1504 **last_sent_position = new_position;
1505 let mut position = entity_mut.get_mut::<Position>().unwrap();
1506 **position = new_position;
1507
1508 let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
1509 *look_direction = new_look_direction;
1510 }),
1511 });
1512
1513 system_state.apply(ecs);
1514 }
1515
1516 ClientboundGamePacket::SelectAdvancementsTab(_) => {}
1517 ClientboundGamePacket::SetActionBarText(_) => {}
1518 ClientboundGamePacket::SetBorderCenter(_) => {}
1519 ClientboundGamePacket::SetBorderLerpSize(_) => {}
1520 ClientboundGamePacket::SetBorderSize(_) => {}
1521 ClientboundGamePacket::SetBorderWarningDelay(_) => {}
1522 ClientboundGamePacket::SetBorderWarningDistance(_) => {}
1523 ClientboundGamePacket::SetCamera(_) => {}
1524 ClientboundGamePacket::SetDisplayObjective(_) => {}
1525 ClientboundGamePacket::SetObjective(_) => {}
1526 ClientboundGamePacket::SetPassengers(_) => {}
1527 ClientboundGamePacket::SetPlayerTeam(_) => {}
1528 ClientboundGamePacket::SetScore(_) => {}
1529 ClientboundGamePacket::SetSimulationDistance(_) => {}
1530 ClientboundGamePacket::SetSubtitleText(_) => {}
1531 ClientboundGamePacket::SetTitleText(_) => {}
1532 ClientboundGamePacket::SetTitlesAnimation(_) => {}
1533 ClientboundGamePacket::ClearTitles(_) => {}
1534 ClientboundGamePacket::SoundEntity(_) => {}
1535 ClientboundGamePacket::StopSound(_) => {}
1536 ClientboundGamePacket::TabList(_) => {}
1537 ClientboundGamePacket::TagQuery(_) => {}
1538 ClientboundGamePacket::TakeItemEntity(_) => {}
1539 ClientboundGamePacket::BundleDelimiter(_) => {}
1540 ClientboundGamePacket::DamageEvent(_) => {}
1541 ClientboundGamePacket::HurtAnimation(_) => {}
1542
1543 ClientboundGamePacket::TickingState(_) => {}
1544 ClientboundGamePacket::TickingStep(_) => {}
1545
1546 ClientboundGamePacket::ResetScore(_) => {}
1547 ClientboundGamePacket::CookieRequest(_) => {}
1548 ClientboundGamePacket::DebugSample(_) => {}
1549 ClientboundGamePacket::PongResponse(_) => {}
1550 ClientboundGamePacket::StoreCookie(_) => {}
1551 ClientboundGamePacket::Transfer(_) => {}
1552 ClientboundGamePacket::MoveMinecartAlongTrack(_) => {}
1553 ClientboundGamePacket::SetHeldSlot(_) => {}
1554 ClientboundGamePacket::SetPlayerInventory(_) => {}
1555 ClientboundGamePacket::ProjectilePower(_) => {}
1556 ClientboundGamePacket::CustomReportDetails(_) => {}
1557 ClientboundGamePacket::ServerLinks(_) => {}
1558 ClientboundGamePacket::PlayerRotation(_) => {}
1559 ClientboundGamePacket::RecipeBookAdd(_) => {}
1560 ClientboundGamePacket::RecipeBookRemove(_) => {}
1561 ClientboundGamePacket::RecipeBookSettings(_) => {}
1562 }
1563 }
1564}
1565
1566#[derive(Event)]
1568pub struct SendPacketEvent {
1569 pub sent_by: Entity,
1570 pub packet: ServerboundGamePacket,
1571}
1572impl SendPacketEvent {
1573 pub fn new(sent_by: Entity, packet: impl Packet<ServerboundGamePacket>) -> Self {
1574 let packet = packet.into_variant();
1575 Self { sent_by, packet }
1576 }
1577}
1578
1579pub fn handle_send_packet_event(
1580 mut send_packet_events: EventReader<SendPacketEvent>,
1581 mut query: Query<&mut RawConnection>,
1582) {
1583 for event in send_packet_events.read() {
1584 if let Ok(raw_connection) = query.get_mut(event.sent_by) {
1585 if let Err(e) = raw_connection.write_packet(event.packet.clone()) {
1587 error!("Failed to send packet: {e}");
1588 }
1589 }
1590 }
1591}