1use std::borrow::Cow;
2use std::collections::BTreeSet;
3use std::fmt;
4use std::net::IpAddr;
5use std::time::Instant;
6
7use bevy_app::prelude::*;
8use bevy_ecs::prelude::*;
9use bevy_ecs::query::QueryData;
10use bevy_ecs::world::Command;
11use byteorder::{NativeEndian, ReadBytesExt};
12use bytes::{Bytes, BytesMut};
13use chunkedge_binary::Encode;
14use chunkedge_entity::attributes::{EntityAttributes, TrackedEntityAttributes};
15use chunkedge_entity::living::Health;
16use chunkedge_entity::player::{Food, PlayerEntityBundle, Saturation};
17use chunkedge_entity::query::EntityInitQuery;
18use chunkedge_entity::tracked_data::TrackedData;
19use chunkedge_entity::{
20 ClearEntityChangesSet, EntityId, EntityStatus, OldPosition, Position, Velocity,
21};
22use chunkedge_math::{DVec3, Vec3};
23use chunkedge_protocol::encode::{PacketEncoder, WritePacket};
24use chunkedge_protocol::packets::configuration::client_information_c2s::ParticleMode;
25use chunkedge_protocol::packets::play::chunks_biomes_s2c::ChunkBiome;
26use chunkedge_protocol::packets::play::client_information_c2s::{
27 ChatMode, DisplayedSkinParts, MainArm,
28};
29use chunkedge_protocol::packets::play::game_event_s2c::GameEventKind;
30use chunkedge_protocol::packets::play::level_particles_s2c::Particle;
31use chunkedge_protocol::packets::play::{
32 ChunksBiomesS2c, DisconnectS2c, EntityEventS2c, ForgetLevelChunkS2c, GameEventS2c,
33 LevelParticlesS2c, PlayerCombatKillS2c, RemoveEntitiesS2c, SetChunkCacheCenterS2c,
34 SetChunkCacheRadiusS2c, SetEntityDataS2c, SetEntityMotionS2c, SetHealthS2c, SoundS2c,
35 UpdateAttributesS2c,
36};
37use chunkedge_protocol::profile::Property;
38use chunkedge_protocol::sound::{Sound, SoundCategory, SoundDirect, SoundId};
39use chunkedge_protocol::text::{IntoText, Text};
40use chunkedge_protocol::{BlockPos, ChunkPos, GameMode, IntoTextComponent, Packet, VarInt};
41use chunkedge_registry::RegistrySet;
42use chunkedge_server_common::{Despawned, UniqueId};
43use derive_more::{Deref, DerefMut, From, Into};
44use tracing::warn;
45use uuid::Uuid;
46
47use crate::layer::{ChunkLayer, EntityLayer, UpdateLayersPostClientSet, UpdateLayersPreClientSet};
48use crate::ChunkView;
49
50pub struct ClientPlugin;
51
52#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
56pub struct FlushPacketsSet;
57
58#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
62pub struct SpawnClientsSet;
63
64#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
67pub struct UpdateClientsSet;
68
69impl Plugin for ClientPlugin {
70 fn build(&self, app: &mut App) {
71 app.add_systems(
72 PostUpdate,
73 (
74 (
75 crate::spawn::initial_join.after(RegistrySet),
76 update_chunk_load_dist,
77 handle_layer_messages.after(update_chunk_load_dist),
78 update_view_and_layers
79 .after(crate::spawn::initial_join)
80 .after(handle_layer_messages),
81 cleanup_chunks_after_client_despawn.after(update_view_and_layers),
82 crate::spawn::update_respawn_position.after(update_view_and_layers),
83 crate::spawn::respawn.after(crate::spawn::update_respawn_position),
84 update_old_view_dist.after(update_view_and_layers),
85 update_game_mode,
86 update_food_saturation_health,
87 update_tracked_data,
88 init_tracked_data,
89 update_tracked_attributes,
90 init_tracked_attributes,
91 )
92 .in_set(UpdateClientsSet),
93 flush_packets.in_set(FlushPacketsSet),
94 ),
95 )
96 .configure_sets(PreUpdate, SpawnClientsSet)
97 .configure_sets(
98 PostUpdate,
99 (
100 UpdateClientsSet
101 .after(UpdateLayersPreClientSet)
102 .before(UpdateLayersPostClientSet)
103 .before(FlushPacketsSet),
104 ClearEntityChangesSet.after(UpdateClientsSet),
105 FlushPacketsSet,
106 ),
107 )
108 .add_event::<LoadEntityForClientEvent>()
109 .add_event::<UnloadEntityForClientEvent>();
110 }
111}
112
113#[derive(Bundle)]
116pub struct ClientBundle {
117 pub marker: ClientMarker,
118 pub client: Client,
119 pub settings: crate::client_settings::ClientSettings,
120 pub entity_remove_buf: EntityRemoveBuf,
121 pub username: Username,
122 pub ip: Ip,
123 pub properties: Properties,
124 pub respawn_pos: crate::spawn::RespawnPosition,
125 pub op_level: crate::op_level::OpLevel,
126 pub action_sequence: crate::action::ActionSequence,
127 pub view_distance: ViewDistance,
128 pub old_view_distance: OldViewDistance,
129 pub visible_chunk_layer: VisibleChunkLayer,
130 pub old_visible_chunk_layer: OldVisibleChunkLayer,
131 pub visible_entity_layers: VisibleEntityLayers,
132 pub old_visible_entity_layers: OldVisibleEntityLayers,
133 pub keepalive_state: crate::keepalive::KeepaliveState,
134 pub ping: crate::keepalive::Ping,
135 pub teleport_state: crate::teleport::TeleportState,
136 pub game_mode: GameMode,
137 pub prev_game_mode: crate::spawn::PrevGameMode,
138 pub death_location: crate::spawn::DeathLocation,
139 pub is_hardcore: crate::spawn::IsHardcore,
140 pub hashed_seed: crate::spawn::HashedSeed,
141 pub reduced_debug_info: crate::spawn::ReducedDebugInfo,
142 pub has_respawn_screen: crate::spawn::HasRespawnScreen,
143 pub is_debug: crate::spawn::IsDebug,
144 pub is_flat: crate::spawn::IsFlat,
145 pub portal_cooldown: crate::spawn::PortalCooldown,
146 pub flying_speed: crate::abilities::FlyingSpeed,
147 pub fov_modifier: crate::abilities::FovModifier,
148 pub player_abilities_flags: crate::abilities::PlayerAbilitiesFlags,
149 pub player: PlayerEntityBundle,
150}
151
152impl ClientBundle {
153 pub fn new(args: ClientBundleArgs) -> Self {
154 Self {
155 marker: ClientMarker,
156 client: Client {
157 conn: args.conn,
158 enc: args.enc,
159 },
160 settings: crate::client_settings::ClientSettings {
161 locale: args.locale.into_boxed_str(),
162 chat_mode: args.chat_mode,
163 chat_colors: args.chat_colors,
164 enable_text_filtering: args.enable_text_filtering,
165 allow_server_listings: args.allow_server_listings,
166 particle_mode: args.particle_mode,
167 },
168 entity_remove_buf: Default::default(),
169 username: Username(args.username),
170 ip: Ip(args.ip),
171 properties: Properties(args.properties),
172 respawn_pos: Default::default(),
173 op_level: Default::default(),
174 action_sequence: Default::default(),
175 view_distance: ViewDistance(args.view_distance),
176 old_view_distance: OldViewDistance(2),
177 visible_chunk_layer: Default::default(),
178 old_visible_chunk_layer: OldVisibleChunkLayer(Entity::PLACEHOLDER),
179 visible_entity_layers: Default::default(),
180 old_visible_entity_layers: OldVisibleEntityLayers(BTreeSet::new()),
181 keepalive_state: crate::keepalive::KeepaliveState::new(),
182 ping: Default::default(),
183 teleport_state: crate::teleport::TeleportState::new(),
184 game_mode: GameMode::default(),
185 prev_game_mode: Default::default(),
186 death_location: Default::default(),
187 is_hardcore: Default::default(),
188 is_flat: Default::default(),
189 has_respawn_screen: Default::default(),
190 hashed_seed: Default::default(),
191 reduced_debug_info: Default::default(),
192 is_debug: Default::default(),
193 portal_cooldown: Default::default(),
194 flying_speed: Default::default(),
195 fov_modifier: Default::default(),
196 player_abilities_flags: Default::default(),
197 player: PlayerEntityBundle {
198 uuid: UniqueId(args.uuid),
199 player_player_model_parts: chunkedge_entity::player::PlayerModelParts(u8::from(
200 args.displayed_skin_parts,
201 )
202 as i8),
203 player_main_arm: chunkedge_entity::player::MainArm(args.main_arm as i8),
204 ..Default::default()
205 },
206 }
207 }
208}
209
210pub struct ClientBundleArgs {
212 pub username: String,
214 pub uuid: Uuid,
216 pub ip: IpAddr,
218 pub properties: Vec<Property>,
220 pub conn: Box<dyn ClientConnection>,
222 pub view_distance: u8,
224 pub locale: String,
226 pub chat_mode: ChatMode,
227 pub chat_colors: bool,
228 pub displayed_skin_parts: DisplayedSkinParts,
229 pub main_arm: MainArm,
230 pub enable_text_filtering: bool,
231 pub allow_server_listings: bool,
232 pub particle_mode: ParticleMode,
233 pub enc: PacketEncoder,
235}
236
237#[derive(Component, Copy, Clone)]
240pub struct ClientMarker;
241
242#[derive(Component)]
248pub struct Client {
249 conn: Box<dyn ClientConnection>,
250 pub(crate) enc: PacketEncoder,
251}
252
253pub trait ClientConnection: Send + Sync + 'static {
256 fn try_send(&mut self, bytes: BytesMut) -> anyhow::Result<()>;
259 fn try_recv(&mut self) -> anyhow::Result<Option<ReceivedPacket>>;
262 fn len(&self) -> usize;
265
266 fn is_empty(&self) -> bool {
267 self.len() == 0
268 }
269}
270
271#[derive(Clone, Debug)]
272pub struct ReceivedPacket {
273 pub timestamp: Instant,
276 pub id: i32,
278 pub body: Bytes,
280}
281
282impl Drop for Client {
283 fn drop(&mut self) {
284 _ = self.flush_packets();
285 }
286}
287
288impl WritePacket for Client {
291 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
292 where
293 P: Packet + Encode,
294 {
295 self.enc.write_packet_fallible(packet)
296 }
297
298 fn write_packet_bytes(&mut self, bytes: &[u8]) {
299 self.enc.write_packet_bytes(bytes)
300 }
301}
302
303impl Client {
304 pub fn connection(&self) -> &dyn ClientConnection {
305 self.conn.as_ref()
306 }
307
308 pub fn connection_mut(&mut self) -> &mut dyn ClientConnection {
309 self.conn.as_mut()
310 }
311
312 pub fn flush_packets(&mut self) -> anyhow::Result<()> {
320 let bytes = self.enc.take();
321 if !bytes.is_empty() {
322 self.conn.try_send(bytes)
323 } else {
324 Ok(())
325 }
326 }
327
328 pub fn kill<'a, M: IntoText<'a>>(&mut self, message: M) {
331 self.write_packet(&PlayerCombatKillS2c {
332 player_id: VarInt(0),
333 message: message.into_cow_text_component(),
334 });
335 }
336
337 pub fn win_game(&mut self, show_credits: bool) {
339 self.write_packet(&GameEventS2c {
340 kind: GameEventKind::WinGame,
341 value: if show_credits { 1.0 } else { 0.0 },
342 });
343 }
344
345 #[allow(clippy::too_many_arguments)]
347 pub fn play_particle<P, O>(
348 &mut self,
349 particle: &Particle,
350 long_distance: bool,
351 always_visible: bool,
352 position: P,
353 offset: O,
354 max_speed: f32,
355 count: i32,
356 ) where
357 P: Into<DVec3>,
358 O: Into<Vec3>,
359 {
360 self.write_packet(&LevelParticlesS2c {
361 long_distance,
362 always_visible,
363 particle: particle.clone(),
364 position: position.into(),
365 offset: offset.into(),
366 max_speed,
367 count,
368 })
369 }
370
371 pub fn play_sound<P: Into<DVec3>>(
373 &mut self,
374 sound: Sound,
375 category: SoundCategory,
376 position: P,
377 volume: f32,
378 pitch: f32,
379 ) {
380 let position = position.into();
381
382 self.write_packet(&SoundS2c {
383 id: SoundId::Inline(SoundDirect {
384 id: sound.to_ident().into(),
385 range: None,
386 }),
387 category,
388 position: (position * 8.0).as_ivec3(),
389 volume,
390 pitch,
391 seed: rand::random(),
392 });
393 }
394
395 pub fn set_velocity<V: Into<DVec3>>(&mut self, velocity: V) {
397 self.write_packet(&SetEntityMotionS2c {
398 entity_id: VarInt(0),
399 velocity: Velocity(velocity.into()).to_packet_units(),
400 });
401 }
402
403 pub fn trigger_status(&mut self, status: EntityStatus) {
407 self.write_packet(&EntityEventS2c {
408 entity_id: 0,
409 entity_status: status as u8,
410 });
411 }
412}
413
414#[derive(Clone, PartialEq, Debug)]
416pub struct DisconnectClient {
417 pub client: Entity,
418 pub reason: Text,
419}
420
421impl Command for DisconnectClient {
422 fn apply(self, world: &mut World) {
423 if let Some(mut entity) = world.get_entity_mut(self.client) {
424 if let Some(mut client) = entity.get_mut::<Client>() {
425 client.write_packet(&DisconnectS2c {
426 reason: self.reason.into_cow_text_component(),
427 });
428
429 entity.insert(Despawned);
432 }
433 }
434 }
435}
436
437#[derive(Component, Default, Debug)]
442pub struct EntityRemoveBuf(Vec<VarInt>);
443
444impl EntityRemoveBuf {
445 pub fn push(&mut self, entity_id: i32) {
446 debug_assert!(
447 entity_id != 0,
448 "removing entity with protocol ID 0 (which should be reserved for clients)"
449 );
450
451 self.0.push(VarInt(entity_id));
452 }
453
454 pub fn send_and_clear<W: WritePacket>(&mut self, mut w: W) {
457 if !self.0.is_empty() {
458 w.write_packet(&RemoveEntitiesS2c {
459 entity_ids: Cow::Borrowed(&self.0),
460 });
461
462 self.0.clear();
463 }
464 }
465}
466
467#[derive(Component, Clone, PartialEq, Eq, Default, Debug, Deref)]
468pub struct Username(pub String);
469
470impl fmt::Display for Username {
471 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472 self.0.fmt(f)
473 }
474}
475
476#[derive(Component, Clone, PartialEq, Eq, Default, Debug, Deref, DerefMut, From, Into)]
478pub struct Properties(pub Vec<Property>);
479
480impl Properties {
481 pub fn textures(&self) -> Option<&Property> {
483 self.0.iter().find(|p| p.name == "textures")
484 }
485
486 pub fn textures_mut(&mut self) -> Option<&mut Property> {
488 self.0.iter_mut().find(|p| p.name == "textures")
489 }
490
491 pub fn skin(&self) -> Option<&str> {
494 self.textures().map(|p| p.value.as_str())
495 }
496
497 pub fn set_skin<Sk: Into<String>, Si: Into<String>>(&mut self, skin: Sk, signature: Si) {
505 if let Some(prop) = self.textures_mut() {
506 prop.value = skin.into();
507 prop.signature = Some(signature.into());
508 } else {
509 self.0.push(Property {
510 name: "textures".to_owned(),
511 value: skin.into(),
512 signature: Some(signature.into()),
513 });
514 }
515 }
516}
517
518#[derive(Clone, PartialEq, Eq, Debug)]
519pub struct PropertyValue {
520 pub value: String,
521 pub signature: Option<String>,
522}
523
524#[derive(Component, Clone, PartialEq, Eq, Debug, Deref)]
525pub struct Ip(pub IpAddr);
526
527#[derive(Component, Clone, PartialEq, Eq, Debug, Deref)]
528pub struct ViewDistance(u8);
529
530impl ViewDistance {
531 pub fn new(dist: u8) -> Self {
532 let mut new = Self(0);
533 new.set(dist);
534 new
535 }
536
537 pub fn get(&self) -> u8 {
538 self.0
539 }
540
541 pub fn set(&mut self, dist: u8) {
543 self.0 = dist.clamp(2, 32);
544 }
545}
546
547impl Default for ViewDistance {
548 fn default() -> Self {
549 Self(2)
550 }
551}
552
553#[derive(Component, Clone, PartialEq, Eq, Default, Debug, Deref)]
556pub struct OldViewDistance(u8);
557
558impl OldViewDistance {
559 pub fn get(&self) -> u8 {
560 self.0
561 }
562}
563
564#[derive(QueryData, Copy, Clone, Debug)]
565pub struct View {
566 pub pos: &'static Position,
567 pub view_dist: &'static ViewDistance,
568}
569
570impl ViewItem<'_> {
571 pub fn get(&self) -> ChunkView {
572 ChunkView::new(self.pos.0.into(), self.view_dist.0)
573 }
574}
575
576#[derive(QueryData, Copy, Clone, Debug)]
577pub struct OldView {
578 pub old_pos: &'static OldPosition,
579 pub old_view_dist: &'static OldViewDistance,
580}
581
582impl OldViewItem<'_> {
583 pub fn get(&self) -> ChunkView {
584 ChunkView::new(self.old_pos.get().into(), self.old_view_dist.0)
585 }
586}
587
588#[derive(Component, Copy, Clone, PartialEq, Eq, Debug, Deref, DerefMut)]
594pub struct VisibleChunkLayer(pub Entity);
595
596impl Default for VisibleChunkLayer {
597 fn default() -> Self {
598 Self(Entity::PLACEHOLDER)
599 }
600}
601
602#[derive(Component, PartialEq, Eq, Debug, Deref)]
604pub struct OldVisibleChunkLayer(Entity);
605
606impl OldVisibleChunkLayer {
607 pub fn get(&self) -> Entity {
608 self.0
609 }
610}
611
612#[derive(Component, Default, Debug)]
620pub struct VisibleEntityLayers(pub BTreeSet<Entity>);
621
622#[derive(Component, Default, Debug, Deref)]
624pub struct OldVisibleEntityLayers(BTreeSet<Entity>);
625
626impl OldVisibleEntityLayers {
627 pub fn get(&self) -> &BTreeSet<Entity> {
628 &self.0
629 }
630}
631
632pub fn despawn_disconnected_clients(
635 mut commands: Commands,
636 mut disconnected_clients: RemovedComponents<Client>,
637) {
638 for entity in disconnected_clients.read() {
639 if let Some(mut entity) = commands.get_entity(entity) {
640 entity.insert(Despawned);
641 }
642 }
643}
644
645fn update_chunk_load_dist(
646 mut clients: Query<(&mut Client, &ViewDistance, &OldViewDistance), Changed<ViewDistance>>,
647) {
648 for (mut client, dist, old_dist) in &mut clients {
649 if client.is_added() {
650 continue;
652 }
653
654 if dist.0 != old_dist.0 {
655 client.write_packet(&SetChunkCacheRadiusS2c {
657 view_distance: VarInt(dist.0.into()),
658 });
659 }
660 }
661}
662
663fn handle_layer_messages(
664 mut clients: Query<(
665 Entity,
666 &EntityId,
667 &mut Client,
668 &mut EntityRemoveBuf,
669 OldView,
670 &OldVisibleChunkLayer,
671 &mut VisibleEntityLayers,
672 &OldVisibleEntityLayers,
673 )>,
674 chunk_layers: Query<&ChunkLayer>,
675 entity_layers: Query<&EntityLayer>,
676 entities: Query<(EntityInitQuery, &OldPosition)>,
677) {
678 clients.par_iter_mut().for_each(
679 |(
680 self_entity,
681 self_entity_id,
682 mut client,
683 mut remove_buf,
684 old_view,
685 old_visible_chunk_layer,
686 mut visible_entity_layers,
687 old_visible_entity_layers,
688 )| {
689 let block_pos = BlockPos::from(old_view.old_pos.get());
690 let old_view = old_view.get();
691
692 fn in_radius(p0: BlockPos, p1: BlockPos, radius_squared: u32) -> bool {
693 let dist_squared =
694 (p1.x - p0.x).pow(2) + (p1.y - p0.y).pow(2) + (p1.z - p0.z).pow(2);
695
696 dist_squared as u32 <= radius_squared
697 }
698
699 if let Ok(chunk_layer) = chunk_layers.get(old_visible_chunk_layer.get()) {
701 let messages = chunk_layer.messages();
702 let bytes = messages.bytes();
703
704 for (msg, range) in messages.iter_global() {
706 match msg {
707 crate::layer::chunk::GlobalMsg::Packet => {
708 client.write_packet_bytes(&bytes[range]);
709 }
710 crate::layer::chunk::GlobalMsg::PacketExcept { except } => {
711 if self_entity != except {
712 client.write_packet_bytes(&bytes[range]);
713 }
714 }
715 }
716 }
717
718 let mut chunk_biome_buf = vec![];
719
720 messages.query_local(old_view, |msg, range| match msg {
722 crate::layer::chunk::LocalMsg::PacketAt { .. } => {
723 client.write_packet_bytes(&bytes[range]);
724 }
725 crate::layer::chunk::LocalMsg::PacketAtExcept { except, .. } => {
726 if self_entity != except {
727 client.write_packet_bytes(&bytes[range]);
728 }
729 }
730 crate::layer::chunk::LocalMsg::RadiusAt {
731 center,
732 radius_squared,
733 } => {
734 if in_radius(block_pos, center, radius_squared) {
735 client.write_packet_bytes(&bytes[range]);
736 }
737 }
738 crate::layer::chunk::LocalMsg::RadiusAtExcept {
739 center,
740 radius_squared,
741 except,
742 } => {
743 if self_entity != except && in_radius(block_pos, center, radius_squared) {
744 client.write_packet_bytes(&bytes[range]);
745 }
746 }
747 crate::layer::chunk::LocalMsg::ChangeBiome { pos } => {
748 chunk_biome_buf.push(ChunkBiome {
749 pos,
750 data: &bytes[range],
751 });
752 }
753 crate::layer::chunk::LocalMsg::ChangeChunkState { pos } => {
754 match &bytes[range] {
755 [ChunkLayer::LOAD, .., ChunkLayer::UNLOAD] => {
756 debug_assert!(chunk_layer.chunk(pos).is_none());
759 }
760 [.., ChunkLayer::LOAD | ChunkLayer::OVERWRITE] => {
761 let chunk = chunk_layer.chunk(pos).expect("chunk must exist");
763 chunk.write_init_packets(&mut *client, pos, chunk_layer.info());
764 chunk.inc_viewer_count();
765 }
766 [.., ChunkLayer::UNLOAD] => {
767 client.write_packet(&ForgetLevelChunkS2c { pos });
769 debug_assert!(chunk_layer.chunk(pos).is_none());
770 }
771 _ => unreachable!("invalid message data while changing chunk state"),
772 }
773 }
774 });
775
776 if !chunk_biome_buf.is_empty() {
777 client.write_packet(&ChunksBiomesS2c {
778 chunks: chunk_biome_buf.into(),
779 });
780 }
781 }
782
783 for &layer_id in &old_visible_entity_layers.0 {
785 if let Ok(layer) = entity_layers.get(layer_id) {
786 let messages = layer.messages();
787 let bytes = messages.bytes();
788
789 for (msg, range) in messages.iter_global() {
791 match msg {
792 crate::layer::entity::GlobalMsg::Packet => {
793 client.write_packet_bytes(&bytes[range]);
794 }
795 crate::layer::entity::GlobalMsg::PacketExcept { except } => {
796 if self_entity != except {
797 client.write_packet_bytes(&bytes[range]);
798 }
799 }
800 crate::layer::entity::GlobalMsg::DespawnLayer => {
801 visible_entity_layers.0.remove(&layer_id);
805 }
806 }
807 }
808
809 messages.query_local(old_view, |msg, range| match msg {
811 crate::layer::entity::LocalMsg::DespawnEntity { dest_layer, .. } => {
812 if !old_visible_entity_layers.0.contains(&dest_layer) {
813 let mut bytes = &bytes[range];
814
815 while let Ok(id) = bytes.read_i32::<NativeEndian>() {
816 if self_entity_id.get() != id {
817 remove_buf.push(id);
818 }
819 }
820 }
821 }
822 crate::layer::entity::LocalMsg::DespawnEntityTransition {
823 dest_pos,
824 ..
825 } => {
826 if !old_view.contains(dest_pos) {
827 let mut bytes = &bytes[range];
828
829 while let Ok(id) = bytes.read_i32::<NativeEndian>() {
830 if self_entity_id.get() != id {
831 remove_buf.push(id);
832 }
833 }
834 }
835 }
836 crate::layer::entity::LocalMsg::SpawnEntity { src_layer, .. } => {
837 if !old_visible_entity_layers.0.contains(&src_layer) {
838 let mut bytes = &bytes[range];
839
840 while let Ok(u64) = bytes.read_u64::<NativeEndian>() {
841 let entity = Entity::from_bits(u64);
842
843 if self_entity != entity {
844 if let Ok((init, old_pos)) = entities.get(entity) {
845 remove_buf.send_and_clear(&mut *client);
846
847 init.write_init_packets(old_pos.get(), &mut *client);
851 }
852 }
853 }
854 }
855 }
856 crate::layer::entity::LocalMsg::SpawnEntityTransition {
857 src_pos, ..
858 } => {
859 if !old_view.contains(src_pos) {
860 let mut bytes = &bytes[range];
861
862 while let Ok(u64) = bytes.read_u64::<NativeEndian>() {
863 let entity = Entity::from_bits(u64);
864
865 if self_entity != entity {
866 if let Ok((init, old_pos)) = entities.get(entity) {
867 remove_buf.send_and_clear(&mut *client);
868
869 init.write_init_packets(old_pos.get(), &mut *client);
873 }
874 }
875 }
876 }
877 }
878 crate::layer::entity::LocalMsg::PacketAt { .. } => {
879 client.write_packet_bytes(&bytes[range]);
880 }
881 crate::layer::entity::LocalMsg::PacketAtExcept { except, .. } => {
882 if self_entity != except {
883 client.write_packet_bytes(&bytes[range]);
884 }
885 }
886 crate::layer::entity::LocalMsg::RadiusAt {
887 center,
888 radius_squared,
889 } => {
890 if in_radius(block_pos, center, radius_squared) {
891 client.write_packet_bytes(&bytes[range]);
892 }
893 }
894 crate::layer::entity::LocalMsg::RadiusAtExcept {
895 center,
896 radius_squared,
897 except,
898 } => {
899 if self_entity != except && in_radius(block_pos, center, radius_squared)
900 {
901 client.write_packet_bytes(&bytes[range]);
902 }
903 }
904 });
905
906 remove_buf.send_and_clear(&mut *client);
907 }
908 }
909 },
910 );
911}
912
913#[derive(Debug, Clone, PartialEq, Event)]
916pub struct UnloadEntityForClientEvent {
917 pub client: Entity,
919 pub entity_unloaded: Entity,
921}
922
923#[derive(Debug, Clone, PartialEq, Event)]
926pub struct LoadEntityForClientEvent {
927 pub client: Entity,
929 pub entity_loaded: Entity,
931}
932
933pub(crate) fn update_view_and_layers(
934 mut clients: Query<
935 (
936 Entity,
937 &mut Client,
938 &mut EntityRemoveBuf,
939 &VisibleChunkLayer,
940 &mut OldVisibleChunkLayer,
941 Ref<VisibleEntityLayers>,
942 &mut OldVisibleEntityLayers,
943 &Position,
944 &OldPosition,
945 &ViewDistance,
946 &OldViewDistance,
947 ),
948 Or<(
949 Changed<VisibleChunkLayer>,
950 Changed<VisibleEntityLayers>,
951 Changed<Position>,
952 Changed<ViewDistance>,
953 )>,
954 >,
955 chunk_layers: Query<&ChunkLayer>,
956 entity_layers: Query<&EntityLayer>,
957 entity_ids: Query<&EntityId>,
958 entity_init: Query<(EntityInitQuery, &Position)>,
959
960 mut unload_entity_writer: EventWriter<UnloadEntityForClientEvent>,
961 mut load_entity_writer: EventWriter<LoadEntityForClientEvent>,
962) {
963 enum ChannelEvent {
965 UnloadEntity(UnloadEntityForClientEvent),
966 LoadEntity(LoadEntityForClientEvent),
967 }
968
969 let (tx, rx) = std::sync::mpsc::channel();
970
971 (clients).par_iter_mut().for_each(
972 |(
973 self_entity,
974 mut client,
975 mut remove_buf,
976 chunk_layer,
977 mut old_chunk_layer,
978 visible_entity_layers,
979 mut old_visible_entity_layers,
980 pos,
981 old_pos,
982 view_dist,
983 old_view_dist,
984 )| {
985 let view = ChunkView::new(ChunkPos::from(pos.0), view_dist.0);
986 let old_view = ChunkView::new(ChunkPos::from(old_pos.get()), old_view_dist.0);
987
988 if old_view.pos != view.pos {
991 client.write_packet(&SetChunkCacheCenterS2c {
992 chunk_x: VarInt(view.pos.x),
993 chunk_z: VarInt(view.pos.z),
994 });
995 }
996
997 if old_chunk_layer.0 != chunk_layer.0 {
999 if let Ok(layer) = chunk_layers.get(old_chunk_layer.0) {
1002 for pos in old_view.iter() {
1003 if let Some(chunk) = layer.chunk(pos) {
1004 client.write_packet(&ForgetLevelChunkS2c { pos });
1005 chunk.dec_viewer_count();
1006 }
1007 }
1008 }
1009
1010 if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
1012 for pos in view.iter() {
1013 if let Some(chunk) = layer.chunk(pos) {
1014 chunk.write_init_packets(&mut *client, pos, layer.info());
1015 chunk.inc_viewer_count();
1016 }
1017 }
1018 }
1019
1020 for &layer in &old_visible_entity_layers.0 {
1023 if let Ok(layer) = entity_layers.get(layer) {
1024 for pos in old_view.iter() {
1025 for entity in layer.entities_at(pos) {
1026 if self_entity != entity {
1027 if let Ok(id) = entity_ids.get(entity) {
1028 tx.send(ChannelEvent::UnloadEntity(
1029 UnloadEntityForClientEvent {
1030 client: self_entity,
1031 entity_unloaded: entity,
1032 },
1033 ))
1034 .unwrap();
1035
1036 remove_buf.push(id.get());
1037 }
1038 }
1039 }
1040 }
1041 }
1042 }
1043
1044 remove_buf.send_and_clear(&mut *client);
1045
1046 for &layer in &visible_entity_layers.0 {
1048 if let Ok(layer) = entity_layers.get(layer) {
1049 for pos in view.iter() {
1050 for entity in layer.entities_at(pos) {
1051 if self_entity != entity {
1052 if let Ok((init, pos)) = entity_init.get(entity) {
1053 tx.send(ChannelEvent::LoadEntity(
1054 LoadEntityForClientEvent {
1055 client: self_entity,
1056 entity_loaded: entity,
1057 },
1058 ))
1059 .unwrap();
1060
1061 init.write_init_packets(pos.get(), &mut *client);
1062 }
1063 }
1064 }
1065 }
1066 }
1067 }
1068 } else {
1069 if visible_entity_layers.is_changed() {
1071 for &layer in old_visible_entity_layers
1073 .0
1074 .difference(&visible_entity_layers.0)
1075 {
1076 if let Ok(layer) = entity_layers.get(layer) {
1077 for pos in old_view.iter() {
1078 for entity in layer.entities_at(pos) {
1079 if self_entity != entity {
1080 if let Ok(id) = entity_ids.get(entity) {
1081 tx.send(ChannelEvent::UnloadEntity(
1082 UnloadEntityForClientEvent {
1083 client: self_entity,
1084 entity_unloaded: entity,
1085 },
1086 ))
1087 .unwrap();
1088
1089 remove_buf.push(id.get());
1090 }
1091 }
1092 }
1093 }
1094 }
1095 }
1096
1097 remove_buf.send_and_clear(&mut *client);
1098
1099 for &layer in visible_entity_layers
1101 .0
1102 .difference(&old_visible_entity_layers.0)
1103 {
1104 if let Ok(layer) = entity_layers.get(layer) {
1105 for pos in old_view.iter() {
1106 for entity in layer.entities_at(pos) {
1107 if self_entity != entity {
1108 if let Ok((init, pos)) = entity_init.get(entity) {
1109 tx.send(ChannelEvent::LoadEntity(
1110 LoadEntityForClientEvent {
1111 client: self_entity,
1112 entity_loaded: entity,
1113 },
1114 ))
1115 .unwrap();
1116
1117 init.write_init_packets(pos.get(), &mut *client);
1118 }
1119 }
1120 }
1121 }
1122 }
1123 }
1124 }
1125
1126 if old_view != view {
1128 if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
1134 for pos in old_view.diff(view) {
1135 if let Some(chunk) = layer.chunk(pos) {
1136 client.write_packet(&ForgetLevelChunkS2c { pos });
1137 chunk.dec_viewer_count();
1138 }
1139 }
1140 }
1141
1142 if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
1144 for pos in view.diff(old_view) {
1145 if let Some(chunk) = layer.chunk(pos) {
1146 chunk.write_init_packets(&mut *client, pos, layer.info());
1147 chunk.inc_viewer_count();
1148 }
1149 }
1150 }
1151
1152 for &layer in &visible_entity_layers.0 {
1154 if let Ok(layer) = entity_layers.get(layer) {
1155 for pos in old_view.diff(view) {
1156 for entity in layer.entities_at(pos) {
1157 if self_entity != entity {
1158 if let Ok(id) = entity_ids.get(entity) {
1159 tx.send(ChannelEvent::UnloadEntity(
1160 UnloadEntityForClientEvent {
1161 client: self_entity,
1162 entity_unloaded: entity,
1163 },
1164 ))
1165 .unwrap();
1166
1167 remove_buf.push(id.get());
1168 }
1169 }
1170 }
1171 }
1172 }
1173 }
1174
1175 for &layer in &visible_entity_layers.0 {
1177 if let Ok(layer) = entity_layers.get(layer) {
1178 for pos in view.diff(old_view) {
1179 for entity in layer.entities_at(pos) {
1180 if self_entity != entity {
1181 if let Ok((init, pos)) = entity_init.get(entity) {
1182 tx.send(ChannelEvent::LoadEntity(
1183 LoadEntityForClientEvent {
1184 client: self_entity,
1185 entity_loaded: entity,
1186 },
1187 ))
1188 .unwrap();
1189
1190 init.write_init_packets(pos.get(), &mut *client);
1191 }
1192 }
1193 }
1194 }
1195 }
1196 }
1197 }
1198 }
1199
1200 old_chunk_layer.0 = chunk_layer.0;
1203
1204 if visible_entity_layers.is_changed() {
1205 old_visible_entity_layers
1206 .0
1207 .clone_from(&visible_entity_layers.0);
1208 }
1209 },
1210 );
1211
1212 for event in rx.try_iter() {
1214 match event {
1215 ChannelEvent::UnloadEntity(event) => {
1216 unload_entity_writer.send(event);
1217 }
1218 ChannelEvent::LoadEntity(event) => {
1219 load_entity_writer.send(event);
1220 }
1221 };
1222 }
1223}
1224
1225pub(crate) fn update_game_mode(mut clients: Query<(&mut Client, &GameMode), Changed<GameMode>>) {
1226 for (mut client, game_mode) in &mut clients {
1227 if client.is_added() {
1228 continue;
1230 }
1231
1232 client.write_packet(&GameEventS2c {
1233 kind: GameEventKind::ChangeGameMode,
1234 value: *game_mode as i32 as f32,
1235 })
1236 }
1237}
1238
1239fn update_food_saturation_health(
1240 mut clients: Query<
1241 (&mut Client, &Food, &Saturation, &Health),
1242 Or<(Changed<Food>, Changed<Saturation>, Changed<Health>)>,
1243 >,
1244) {
1245 for (mut client, food, saturation, health) in &mut clients {
1246 client.write_packet(&SetHealthS2c {
1247 health: health.0,
1248 food: VarInt(food.0),
1249 food_saturation: saturation.0,
1250 });
1251 }
1252}
1253
1254fn update_old_view_dist(
1255 mut clients: Query<(&mut OldViewDistance, &ViewDistance), Changed<ViewDistance>>,
1256) {
1257 for (mut old_dist, dist) in &mut clients {
1258 old_dist.0 = dist.0;
1259 }
1260}
1261
1262fn flush_packets(
1263 mut clients: Query<(Entity, &mut Client), Changed<Client>>,
1264 mut commands: Commands,
1265) {
1266 for (entity, mut client) in &mut clients {
1267 if let Err(e) = client.flush_packets() {
1268 warn!("Failed to flush packet queue for client {entity:?}: {e:#}.");
1269 commands.entity(entity).remove::<Client>();
1270 }
1271 }
1272}
1273
1274fn init_tracked_data(mut clients: Query<(&mut Client, &TrackedData), Added<TrackedData>>) {
1275 for (mut client, tracked_data) in &mut clients {
1276 if let Some(init_data) = tracked_data.init_data() {
1277 client.write_packet(&SetEntityDataS2c {
1278 entity_id: VarInt(0),
1279 tracked_values: init_data.into(),
1280 });
1281 }
1282 }
1283}
1284
1285fn update_tracked_data(mut clients: Query<(&mut Client, &TrackedData)>) {
1286 for (mut client, tracked_data) in &mut clients {
1287 if let Some(update_data) = tracked_data.update_data() {
1288 client.write_packet(&SetEntityDataS2c {
1289 entity_id: VarInt(0),
1290 tracked_values: update_data.into(),
1291 });
1292 }
1293 }
1294}
1295
1296fn init_tracked_attributes(
1297 mut clients: Query<(&mut Client, &EntityAttributes), Added<EntityAttributes>>,
1298) {
1299 for (mut client, attributes) in &mut clients {
1300 client.write_packet(&UpdateAttributesS2c {
1301 entity_id: VarInt(0),
1302 properties: attributes.to_properties(),
1303 });
1304 }
1305}
1306
1307fn update_tracked_attributes(mut clients: Query<(&mut Client, &TrackedEntityAttributes)>) {
1308 for (mut client, attributes) in &mut clients {
1309 let properties = attributes.get_properties();
1310 if !properties.is_empty() {
1311 client.write_packet(&UpdateAttributesS2c {
1312 entity_id: VarInt(0),
1313 properties,
1314 });
1315 }
1316 }
1317}
1318
1319fn cleanup_chunks_after_client_despawn(
1321 mut clients: Query<(View, &VisibleChunkLayer), (With<ClientMarker>, With<Despawned>)>,
1322 chunk_layers: Query<&ChunkLayer>,
1323) {
1324 for (view, layer) in &mut clients {
1325 if let Ok(layer) = chunk_layers.get(layer.0) {
1326 for pos in view.get().iter() {
1327 if let Some(chunk) = layer.chunk(pos) {
1328 chunk.dec_viewer_count();
1329 }
1330 }
1331 }
1332 }
1333}