1use std::borrow::Cow;
2use std::collections::BTreeSet;
3use std::fmt;
4use std::net::IpAddr;
5use std::ops::Deref;
6use std::time::Instant;
7
8use bevy_app::prelude::*;
9use bevy_ecs::prelude::*;
10use bevy_ecs::query::WorldQuery;
11use bevy_ecs::system::Command;
12use byteorder::{NativeEndian, ReadBytesExt};
13use bytes::{Bytes, BytesMut};
14use tracing::warn;
15use uuid::Uuid;
16use valence_entity::player::PlayerEntityBundle;
17use valence_entity::query::EntityInitQuery;
18use valence_entity::tracked_data::TrackedData;
19use valence_entity::{
20 ClearEntityChangesSet, EntityId, EntityStatus, OldPosition, Position, Velocity,
21};
22use valence_math::{DVec3, Vec3};
23use valence_protocol::encode::{PacketEncoder, WritePacket};
24use valence_protocol::packets::play::chunk_biome_data_s2c::ChunkBiome;
25use valence_protocol::packets::play::game_state_change_s2c::GameEventKind;
26use valence_protocol::packets::play::particle_s2c::Particle;
27use valence_protocol::packets::play::{
28 ChunkBiomeDataS2c, ChunkLoadDistanceS2c, ChunkRenderDistanceCenterS2c, DeathMessageS2c,
29 DisconnectS2c, EntitiesDestroyS2c, EntityStatusS2c, EntityTrackerUpdateS2c,
30 EntityVelocityUpdateS2c, GameStateChangeS2c, ParticleS2c, PlaySoundS2c, UnloadChunkS2c,
31};
32use valence_protocol::sound::{Sound, SoundCategory, SoundId};
33use valence_protocol::text::{IntoText, Text};
34use valence_protocol::var_int::VarInt;
35use valence_protocol::{BlockPos, ChunkPos, Encode, GameMode, Packet, Property};
36use valence_registry::RegistrySet;
37use valence_server_common::{Despawned, UniqueId};
38
39use crate::layer::{ChunkLayer, EntityLayer, UpdateLayersPostClientSet, UpdateLayersPreClientSet};
40use crate::ChunkView;
41
42pub struct ClientPlugin;
43
44#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
48pub struct FlushPacketsSet;
49
50#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
54pub struct SpawnClientsSet;
55
56#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
59pub struct UpdateClientsSet;
60
61impl Plugin for ClientPlugin {
62 fn build(&self, app: &mut App) {
63 app.add_systems(
64 PostUpdate,
65 (
66 (
67 crate::spawn::initial_join.after(RegistrySet),
68 update_chunk_load_dist,
69 handle_layer_messages.after(update_chunk_load_dist),
70 update_view_and_layers
71 .after(crate::spawn::initial_join)
72 .after(handle_layer_messages),
73 cleanup_chunks_after_client_despawn.after(update_view_and_layers),
74 crate::spawn::update_respawn_position.after(update_view_and_layers),
75 crate::spawn::respawn.after(crate::spawn::update_respawn_position),
76 update_old_view_dist.after(update_view_and_layers),
77 update_game_mode,
78 update_tracked_data,
79 init_tracked_data,
80 )
81 .in_set(UpdateClientsSet),
82 flush_packets.in_set(FlushPacketsSet),
83 ),
84 )
85 .configure_set(PreUpdate, SpawnClientsSet)
86 .configure_sets(
87 PostUpdate,
88 (
89 UpdateClientsSet
90 .after(UpdateLayersPreClientSet)
91 .before(UpdateLayersPostClientSet)
92 .before(FlushPacketsSet),
93 ClearEntityChangesSet.after(UpdateClientsSet),
94 FlushPacketsSet,
95 ),
96 );
97 }
98}
99
100#[derive(Bundle)]
103pub struct ClientBundle {
104 pub marker: ClientMarker,
105 pub client: Client,
106 pub settings: crate::client_settings::ClientSettings,
107 pub entity_remove_buf: EntityRemoveBuf,
108 pub username: Username,
109 pub ip: Ip,
110 pub properties: Properties,
111 pub respawn_pos: crate::spawn::RespawnPosition,
112 pub op_level: crate::op_level::OpLevel,
113 pub action_sequence: crate::action::ActionSequence,
114 pub view_distance: ViewDistance,
115 pub old_view_distance: OldViewDistance,
116 pub visible_chunk_layer: VisibleChunkLayer,
117 pub old_visible_chunk_layer: OldVisibleChunkLayer,
118 pub visible_entity_layers: VisibleEntityLayers,
119 pub old_visible_entity_layers: OldVisibleEntityLayers,
120 pub keepalive_state: crate::keepalive::KeepaliveState,
121 pub ping: crate::keepalive::Ping,
122 pub teleport_state: crate::teleport::TeleportState,
123 pub game_mode: GameMode,
124 pub prev_game_mode: crate::spawn::PrevGameMode,
125 pub death_location: crate::spawn::DeathLocation,
126 pub is_hardcore: crate::spawn::IsHardcore,
127 pub hashed_seed: crate::spawn::HashedSeed,
128 pub reduced_debug_info: crate::spawn::ReducedDebugInfo,
129 pub has_respawn_screen: crate::spawn::HasRespawnScreen,
130 pub is_debug: crate::spawn::IsDebug,
131 pub is_flat: crate::spawn::IsFlat,
132 pub portal_cooldown: crate::spawn::PortalCooldown,
133 pub flying_speed: crate::abilities::FlyingSpeed,
134 pub fov_modifier: crate::abilities::FovModifier,
135 pub player_abilities_flags: crate::abilities::PlayerAbilitiesFlags,
136 pub player: PlayerEntityBundle,
137}
138
139impl ClientBundle {
140 pub fn new(args: ClientBundleArgs) -> Self {
141 Self {
142 marker: ClientMarker,
143 client: Client {
144 conn: args.conn,
145 enc: args.enc,
146 },
147 settings: Default::default(),
148 entity_remove_buf: Default::default(),
149 username: Username(args.username),
150 ip: Ip(args.ip),
151 properties: Properties(args.properties),
152 respawn_pos: Default::default(),
153 op_level: Default::default(),
154 action_sequence: Default::default(),
155 view_distance: Default::default(),
156 old_view_distance: OldViewDistance(2),
157 visible_chunk_layer: Default::default(),
158 old_visible_chunk_layer: OldVisibleChunkLayer(Entity::PLACEHOLDER),
159 visible_entity_layers: Default::default(),
160 old_visible_entity_layers: OldVisibleEntityLayers(BTreeSet::new()),
161 keepalive_state: crate::keepalive::KeepaliveState::new(),
162 ping: Default::default(),
163 teleport_state: crate::teleport::TeleportState::new(),
164 game_mode: GameMode::default(),
165 prev_game_mode: Default::default(),
166 death_location: Default::default(),
167 is_hardcore: Default::default(),
168 is_flat: Default::default(),
169 has_respawn_screen: Default::default(),
170 hashed_seed: Default::default(),
171 reduced_debug_info: Default::default(),
172 is_debug: Default::default(),
173 portal_cooldown: Default::default(),
174 flying_speed: Default::default(),
175 fov_modifier: Default::default(),
176 player_abilities_flags: Default::default(),
177 player: PlayerEntityBundle {
178 uuid: UniqueId(args.uuid),
179 ..Default::default()
180 },
181 }
182 }
183}
184
185pub struct ClientBundleArgs {
187 pub username: String,
189 pub uuid: Uuid,
190 pub ip: IpAddr,
191 pub properties: Vec<Property>,
192 pub conn: Box<dyn ClientConnection>,
193 pub enc: PacketEncoder,
195}
196
197#[derive(Component, Copy, Clone)]
200pub struct ClientMarker;
201
202#[derive(Component)]
208pub struct Client {
209 conn: Box<dyn ClientConnection>,
210 pub(crate) enc: PacketEncoder,
211}
212
213pub trait ClientConnection: Send + Sync + 'static {
216 fn try_send(&mut self, bytes: BytesMut) -> anyhow::Result<()>;
219 fn try_recv(&mut self) -> anyhow::Result<Option<ReceivedPacket>>;
222 fn len(&self) -> usize;
225
226 fn is_empty(&self) -> bool {
227 self.len() == 0
228 }
229}
230
231#[derive(Clone, Debug)]
232pub struct ReceivedPacket {
233 pub timestamp: Instant,
236 pub id: i32,
238 pub body: Bytes,
240}
241
242impl Drop for Client {
243 fn drop(&mut self) {
244 _ = self.flush_packets();
245 }
246}
247
248impl WritePacket for Client {
251 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
252 where
253 P: Packet + Encode,
254 {
255 self.enc.write_packet_fallible(packet)
256 }
257
258 fn write_packet_bytes(&mut self, bytes: &[u8]) {
259 self.enc.write_packet_bytes(bytes)
260 }
261}
262
263impl Client {
264 pub fn connection(&self) -> &dyn ClientConnection {
265 self.conn.as_ref()
266 }
267
268 pub fn connection_mut(&mut self) -> &mut dyn ClientConnection {
269 self.conn.as_mut()
270 }
271
272 pub fn flush_packets(&mut self) -> anyhow::Result<()> {
280 let bytes = self.enc.take();
281 if !bytes.is_empty() {
282 self.conn.try_send(bytes)
283 } else {
284 Ok(())
285 }
286 }
287
288 pub fn kill<'a>(&mut self, message: impl IntoText<'a>) {
291 self.write_packet(&DeathMessageS2c {
292 player_id: VarInt(0),
293 message: message.into_cow_text(),
294 });
295 }
296
297 pub fn win_game(&mut self, show_credits: bool) {
299 self.write_packet(&GameStateChangeS2c {
300 kind: GameEventKind::WinGame,
301 value: if show_credits { 1.0 } else { 0.0 },
302 });
303 }
304
305 pub fn play_particle(
307 &mut self,
308 particle: &Particle,
309 long_distance: bool,
310 position: impl Into<DVec3>,
311 offset: impl Into<Vec3>,
312 max_speed: f32,
313 count: i32,
314 ) {
315 self.write_packet(&ParticleS2c {
316 particle: Cow::Borrowed(particle),
317 long_distance,
318 position: position.into(),
319 offset: offset.into(),
320 max_speed,
321 count,
322 })
323 }
324
325 pub fn play_sound(
327 &mut self,
328 sound: Sound,
329 category: SoundCategory,
330 position: impl Into<DVec3>,
331 volume: f32,
332 pitch: f32,
333 ) {
334 let position = position.into();
335
336 self.write_packet(&PlaySoundS2c {
337 id: SoundId::Direct {
338 id: sound.to_ident().into(),
339 range: None,
340 },
341 category,
342 position: (position * 8.0).as_ivec3(),
343 volume,
344 pitch,
345 seed: rand::random(),
346 });
347 }
348
349 pub fn set_velocity(&mut self, velocity: impl Into<Vec3>) {
351 self.write_packet(&EntityVelocityUpdateS2c {
352 entity_id: VarInt(0),
353 velocity: Velocity(velocity.into()).to_packet_units(),
354 });
355 }
356
357 pub fn trigger_status(&mut self, status: EntityStatus) {
361 self.write_packet(&EntityStatusS2c {
362 entity_id: 0,
363 entity_status: status as u8,
364 });
365 }
366}
367
368#[derive(Clone, PartialEq, Debug)]
370pub struct DisconnectClient {
371 pub client: Entity,
372 pub reason: Text,
373}
374
375impl Command for DisconnectClient {
376 fn apply(self, world: &mut World) {
377 if let Some(mut entity) = world.get_entity_mut(self.client) {
378 if let Some(mut client) = entity.get_mut::<Client>() {
379 client.write_packet(&DisconnectS2c {
380 reason: self.reason.into(),
381 });
382
383 entity.remove::<Client>();
384 }
385 }
386 }
387}
388
389#[derive(Component, Default, Debug)]
394pub struct EntityRemoveBuf(Vec<VarInt>);
395
396impl EntityRemoveBuf {
397 pub fn push(&mut self, entity_id: i32) {
398 debug_assert!(
399 entity_id != 0,
400 "removing entity with protocol ID 0 (which should be reserved for clients)"
401 );
402
403 self.0.push(VarInt(entity_id));
404 }
405
406 pub fn send_and_clear(&mut self, mut w: impl WritePacket) {
409 if !self.0.is_empty() {
410 w.write_packet(&EntitiesDestroyS2c {
411 entity_ids: Cow::Borrowed(&self.0),
412 });
413
414 self.0.clear();
415 }
416 }
417}
418
419#[derive(Component, Clone, PartialEq, Eq, Default, Debug)]
420pub struct Username(pub String);
421
422impl fmt::Display for Username {
423 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424 self.0.fmt(f)
425 }
426}
427
428#[derive(Component, Clone, PartialEq, Eq, Default, Debug)]
429pub struct Properties(pub Vec<Property>);
430
431impl Properties {
432 pub fn textures(&self) -> Option<&Property> {
434 self.0.iter().find(|prop| prop.name == "textures")
435 }
436
437 pub fn textures_mut(&mut self) -> Option<&mut Property> {
439 self.0.iter_mut().find(|prop| prop.name == "textures")
440 }
441}
442
443impl From<Vec<Property>> for Properties {
444 fn from(value: Vec<Property>) -> Self {
445 Self(value)
446 }
447}
448
449impl Deref for Properties {
450 type Target = [Property];
451
452 fn deref(&self) -> &Self::Target {
453 &self.0
454 }
455}
456
457#[derive(Component, Clone, PartialEq, Eq, Debug)]
458pub struct Ip(pub IpAddr);
459
460#[derive(Component, Clone, PartialEq, Eq, Debug)]
461pub struct ViewDistance(u8);
462
463impl ViewDistance {
464 pub fn new(dist: u8) -> Self {
465 let mut new = Self(0);
466 new.set(dist);
467 new
468 }
469
470 pub fn get(&self) -> u8 {
471 self.0
472 }
473
474 pub fn set(&mut self, dist: u8) {
476 self.0 = dist.clamp(2, 32);
477 }
478}
479
480impl Default for ViewDistance {
481 fn default() -> Self {
482 Self(2)
483 }
484}
485
486#[derive(Component, Clone, PartialEq, Eq, Default, Debug)]
489
490pub struct OldViewDistance(u8);
491
492impl OldViewDistance {
493 pub fn get(&self) -> u8 {
494 self.0
495 }
496}
497
498#[derive(WorldQuery, Copy, Clone, Debug)]
499pub struct View {
500 pub pos: &'static Position,
501 pub view_dist: &'static ViewDistance,
502}
503
504impl ViewItem<'_> {
505 pub fn get(&self) -> ChunkView {
506 ChunkView::new(self.pos.to_chunk_pos(), self.view_dist.0)
507 }
508}
509
510#[derive(WorldQuery, Copy, Clone, Debug)]
511pub struct OldView {
512 pub old_pos: &'static OldPosition,
513 pub old_view_dist: &'static OldViewDistance,
514}
515
516impl OldViewItem<'_> {
517 pub fn get(&self) -> ChunkView {
518 ChunkView::new(self.old_pos.chunk_pos(), self.old_view_dist.0)
519 }
520}
521
522#[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
528pub struct VisibleChunkLayer(pub Entity);
529
530impl Default for VisibleChunkLayer {
531 fn default() -> Self {
532 Self(Entity::PLACEHOLDER)
533 }
534}
535
536#[derive(Component, PartialEq, Eq, Debug)]
538pub struct OldVisibleChunkLayer(Entity);
539
540impl OldVisibleChunkLayer {
541 pub fn get(&self) -> Entity {
542 self.0
543 }
544}
545
546#[derive(Component, Default, Debug)]
554pub struct VisibleEntityLayers(pub BTreeSet<Entity>);
555
556#[derive(Component, Default, Debug)]
558pub struct OldVisibleEntityLayers(BTreeSet<Entity>);
559
560impl OldVisibleEntityLayers {
561 pub fn get(&self) -> &BTreeSet<Entity> {
562 &self.0
563 }
564}
565
566pub fn despawn_disconnected_clients(
569 mut commands: Commands,
570 mut disconnected_clients: RemovedComponents<Client>,
571) {
572 for entity in disconnected_clients.iter() {
573 if let Some(mut entity) = commands.get_entity(entity) {
574 entity.insert(Despawned);
575 }
576 }
577}
578
579fn update_chunk_load_dist(
580 mut clients: Query<(&mut Client, &ViewDistance, &OldViewDistance), Changed<ViewDistance>>,
581) {
582 for (mut client, dist, old_dist) in &mut clients {
583 if client.is_added() {
584 continue;
586 }
587
588 if dist.0 != old_dist.0 {
589 client.write_packet(&ChunkLoadDistanceS2c {
591 view_distance: VarInt(dist.0.into()),
592 });
593 }
594 }
595}
596
597fn handle_layer_messages(
598 mut clients: Query<(
599 Entity,
600 &EntityId,
601 &mut Client,
602 &mut EntityRemoveBuf,
603 OldView,
604 &OldVisibleChunkLayer,
605 &mut VisibleEntityLayers,
606 &OldVisibleEntityLayers,
607 )>,
608 chunk_layers: Query<&ChunkLayer>,
609 entity_layers: Query<&EntityLayer>,
610 entities: Query<(EntityInitQuery, &OldPosition)>,
611) {
612 clients.par_iter_mut().for_each_mut(
613 |(
614 self_entity,
615 self_entity_id,
616 mut client,
617 mut remove_buf,
618 old_view,
619 old_visible_chunk_layer,
620 mut visible_entity_layers,
621 old_visible_entity_layers,
622 )| {
623 let block_pos = BlockPos::from_pos(old_view.old_pos.get());
624 let old_view = old_view.get();
625
626 fn in_radius(p0: BlockPos, p1: BlockPos, radius_squared: u32) -> bool {
627 let dist_squared =
628 (p1.x - p0.x).pow(2) + (p1.y - p0.y).pow(2) + (p1.z - p0.z).pow(2);
629
630 dist_squared as u32 <= radius_squared
631 }
632
633 if let Ok(chunk_layer) = chunk_layers.get(old_visible_chunk_layer.get()) {
635 let messages = chunk_layer.messages();
636 let bytes = messages.bytes();
637
638 for (msg, range) in messages.iter_global() {
640 match msg {
641 crate::layer::chunk::GlobalMsg::Packet => {
642 client.write_packet_bytes(&bytes[range]);
643 }
644 crate::layer::chunk::GlobalMsg::PacketExcept { except } => {
645 if self_entity != except {
646 client.write_packet_bytes(&bytes[range]);
647 }
648 }
649 }
650 }
651
652 let mut chunk_biome_buf = vec![];
653
654 messages.query_local(old_view, |msg, range| match msg {
656 crate::layer::chunk::LocalMsg::PacketAt { .. } => {
657 client.write_packet_bytes(&bytes[range]);
658 }
659 crate::layer::chunk::LocalMsg::PacketAtExcept { except, .. } => {
660 if self_entity != except {
661 client.write_packet_bytes(&bytes[range]);
662 }
663 }
664 crate::layer::chunk::LocalMsg::RadiusAt {
665 center,
666 radius_squared,
667 } => {
668 if in_radius(block_pos, center, radius_squared) {
669 client.write_packet_bytes(&bytes[range]);
670 }
671 }
672 crate::layer::chunk::LocalMsg::RadiusAtExcept {
673 center,
674 radius_squared,
675 except,
676 } => {
677 if self_entity != except && in_radius(block_pos, center, radius_squared) {
678 client.write_packet_bytes(&bytes[range]);
679 }
680 }
681 crate::layer::chunk::LocalMsg::ChangeBiome { pos } => {
682 chunk_biome_buf.push(ChunkBiome {
683 pos,
684 data: &bytes[range],
685 });
686 }
687 crate::layer::chunk::LocalMsg::ChangeChunkState { pos } => {
688 match &bytes[range] {
689 [ChunkLayer::LOAD, .., ChunkLayer::UNLOAD] => {
690 debug_assert!(chunk_layer.chunk(pos).is_none());
693 }
694 [.., ChunkLayer::LOAD | ChunkLayer::OVERWRITE] => {
695 let chunk = chunk_layer.chunk(pos).expect("chunk must exist");
697 chunk.write_init_packets(&mut *client, pos, chunk_layer.info());
698 chunk.inc_viewer_count();
699 }
700 [.., ChunkLayer::UNLOAD] => {
701 client.write_packet(&UnloadChunkS2c { pos });
703 debug_assert!(chunk_layer.chunk(pos).is_none());
704 }
705 _ => unreachable!("invalid message data while changing chunk state"),
706 }
707 }
708 });
709
710 if !chunk_biome_buf.is_empty() {
711 client.write_packet(&ChunkBiomeDataS2c {
712 chunks: chunk_biome_buf.into(),
713 });
714 }
715 }
716
717 for &layer_id in &old_visible_entity_layers.0 {
719 if let Ok(layer) = entity_layers.get(layer_id) {
720 let messages = layer.messages();
721 let bytes = messages.bytes();
722
723 for (msg, range) in messages.iter_global() {
725 match msg {
726 crate::layer::entity::GlobalMsg::Packet => {
727 client.write_packet_bytes(&bytes[range]);
728 }
729 crate::layer::entity::GlobalMsg::PacketExcept { except } => {
730 if self_entity != except {
731 client.write_packet_bytes(&bytes[range]);
732 }
733 }
734 crate::layer::entity::GlobalMsg::DespawnLayer => {
735 visible_entity_layers.0.remove(&layer_id);
739 }
740 }
741 }
742
743 messages.query_local(old_view, |msg, range| match msg {
745 crate::layer::entity::LocalMsg::DespawnEntity { pos: _, dest_layer } => {
746 if !old_visible_entity_layers.0.contains(&dest_layer) {
747 let mut bytes = &bytes[range];
748
749 while let Ok(id) = bytes.read_i32::<NativeEndian>() {
750 if self_entity_id.get() != id {
751 remove_buf.push(id);
752 }
753 }
754 }
755 }
756 crate::layer::entity::LocalMsg::DespawnEntityTransition {
757 pos: _,
758 dest_pos,
759 } => {
760 if !old_view.contains(dest_pos) {
761 let mut bytes = &bytes[range];
762
763 while let Ok(id) = bytes.read_i32::<NativeEndian>() {
764 if self_entity_id.get() != id {
765 remove_buf.push(id);
766 }
767 }
768 }
769 }
770 crate::layer::entity::LocalMsg::SpawnEntity { pos: _, src_layer } => {
771 if !old_visible_entity_layers.0.contains(&src_layer) {
772 let mut bytes = &bytes[range];
773
774 while let Ok(u64) = bytes.read_u64::<NativeEndian>() {
775 let entity = Entity::from_bits(u64);
776
777 if self_entity != entity {
778 if let Ok((init, old_pos)) = entities.get(entity) {
779 remove_buf.send_and_clear(&mut *client);
780
781 init.write_init_packets(old_pos.get(), &mut *client);
785 }
786 }
787 }
788 }
789 }
790 crate::layer::entity::LocalMsg::SpawnEntityTransition {
791 pos: _,
792 src_pos,
793 } => {
794 if !old_view.contains(src_pos) {
795 let mut bytes = &bytes[range];
796
797 while let Ok(u64) = bytes.read_u64::<NativeEndian>() {
798 let entity = Entity::from_bits(u64);
799
800 if self_entity != entity {
801 if let Ok((init, old_pos)) = entities.get(entity) {
802 remove_buf.send_and_clear(&mut *client);
803
804 init.write_init_packets(old_pos.get(), &mut *client);
808 }
809 }
810 }
811 }
812 }
813 crate::layer::entity::LocalMsg::PacketAt { pos: _ } => {
814 client.write_packet_bytes(&bytes[range]);
815 }
816 crate::layer::entity::LocalMsg::PacketAtExcept { pos: _, except } => {
817 if self_entity != except {
818 client.write_packet_bytes(&bytes[range]);
819 }
820 }
821 crate::layer::entity::LocalMsg::RadiusAt {
822 center,
823 radius_squared,
824 } => {
825 if in_radius(block_pos, center, radius_squared) {
826 client.write_packet_bytes(&bytes[range]);
827 }
828 }
829 crate::layer::entity::LocalMsg::RadiusAtExcept {
830 center,
831 radius_squared,
832 except,
833 } => {
834 if self_entity != except && in_radius(block_pos, center, radius_squared)
835 {
836 client.write_packet_bytes(&bytes[range]);
837 }
838 }
839 });
840
841 remove_buf.send_and_clear(&mut *client);
842 }
843 }
844 },
845 );
846}
847
848pub(crate) fn update_view_and_layers(
849 mut clients: Query<
850 (
851 Entity,
852 &mut Client,
853 &mut EntityRemoveBuf,
854 &VisibleChunkLayer,
855 &mut OldVisibleChunkLayer,
856 Ref<VisibleEntityLayers>,
857 &mut OldVisibleEntityLayers,
858 &Position,
859 &OldPosition,
860 &ViewDistance,
861 &OldViewDistance,
862 ),
863 Or<(
864 Changed<VisibleChunkLayer>,
865 Changed<VisibleEntityLayers>,
866 Changed<Position>,
867 Changed<ViewDistance>,
868 )>,
869 >,
870 chunk_layers: Query<&ChunkLayer>,
871 entity_layers: Query<&EntityLayer>,
872 entity_ids: Query<&EntityId>,
873 entity_init: Query<(EntityInitQuery, &Position)>,
874) {
875 clients.par_iter_mut().for_each_mut(
876 |(
877 self_entity,
878 mut client,
879 mut remove_buf,
880 chunk_layer,
881 mut old_chunk_layer,
882 visible_entity_layers,
883 mut old_visible_entity_layers,
884 pos,
885 old_pos,
886 view_dist,
887 old_view_dist,
888 )| {
889 let view = ChunkView::new(ChunkPos::from_pos(pos.0), view_dist.0);
890 let old_view = ChunkView::new(ChunkPos::from_pos(old_pos.get()), old_view_dist.0);
891
892 if old_view.pos != view.pos {
895 client.write_packet(&ChunkRenderDistanceCenterS2c {
896 chunk_x: VarInt(view.pos.x),
897 chunk_z: VarInt(view.pos.z),
898 });
899 }
900
901 if old_chunk_layer.0 != chunk_layer.0 {
903 if let Ok(layer) = chunk_layers.get(old_chunk_layer.0) {
906 for pos in old_view.iter() {
907 if let Some(chunk) = layer.chunk(pos) {
908 client.write_packet(&UnloadChunkS2c { pos });
909 chunk.dec_viewer_count();
910 }
911 }
912 }
913
914 if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
916 for pos in view.iter() {
917 if let Some(chunk) = layer.chunk(pos) {
918 chunk.write_init_packets(&mut *client, pos, layer.info());
919 chunk.inc_viewer_count();
920 }
921 }
922 }
923
924 for &layer in &old_visible_entity_layers.0 {
927 if let Ok(layer) = entity_layers.get(layer) {
928 for pos in old_view.iter() {
929 for entity in layer.entities_at(pos) {
930 if self_entity != entity {
931 if let Ok(id) = entity_ids.get(entity) {
932 remove_buf.push(id.get());
933 }
934 }
935 }
936 }
937 }
938 }
939
940 remove_buf.send_and_clear(&mut *client);
941
942 for &layer in &visible_entity_layers.0 {
944 if let Ok(layer) = entity_layers.get(layer) {
945 for pos in view.iter() {
946 for entity in layer.entities_at(pos) {
947 if self_entity != entity {
948 if let Ok((init, pos)) = entity_init.get(entity) {
949 init.write_init_packets(pos.get(), &mut *client);
950 }
951 }
952 }
953 }
954 }
955 }
956 } else {
957 if visible_entity_layers.is_changed() {
959 for &layer in old_visible_entity_layers
961 .0
962 .difference(&visible_entity_layers.0)
963 {
964 if let Ok(layer) = entity_layers.get(layer) {
965 for pos in old_view.iter() {
966 for entity in layer.entities_at(pos) {
967 if self_entity != entity {
968 if let Ok(id) = entity_ids.get(entity) {
969 remove_buf.push(id.get());
970 }
971 }
972 }
973 }
974 }
975 }
976
977 remove_buf.send_and_clear(&mut *client);
978
979 for &layer in visible_entity_layers
981 .0
982 .difference(&old_visible_entity_layers.0)
983 {
984 if let Ok(layer) = entity_layers.get(layer) {
985 for pos in old_view.iter() {
986 for entity in layer.entities_at(pos) {
987 if self_entity != entity {
988 if let Ok((init, pos)) = entity_init.get(entity) {
989 init.write_init_packets(pos.get(), &mut *client);
990 }
991 }
992 }
993 }
994 }
995 }
996 }
997
998 if old_view != view {
1000 if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
1006 for pos in old_view.diff(view) {
1007 if let Some(chunk) = layer.chunk(pos) {
1008 client.write_packet(&UnloadChunkS2c { pos });
1009 chunk.dec_viewer_count();
1010 }
1011 }
1012 }
1013
1014 if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
1016 for pos in view.diff(old_view) {
1017 if let Some(chunk) = layer.chunk(pos) {
1018 chunk.write_init_packets(&mut *client, pos, layer.info());
1019 chunk.inc_viewer_count();
1020 }
1021 }
1022 }
1023
1024 for &layer in &visible_entity_layers.0 {
1026 if let Ok(layer) = entity_layers.get(layer) {
1027 for pos in old_view.diff(view) {
1028 for entity in layer.entities_at(pos) {
1029 if self_entity != entity {
1030 if let Ok(id) = entity_ids.get(entity) {
1031 remove_buf.push(id.get());
1032 }
1033 }
1034 }
1035 }
1036 }
1037 }
1038
1039 for &layer in &visible_entity_layers.0 {
1041 if let Ok(layer) = entity_layers.get(layer) {
1042 for pos in view.diff(old_view) {
1043 for entity in layer.entities_at(pos) {
1044 if self_entity != entity {
1045 if let Ok((init, pos)) = entity_init.get(entity) {
1046 init.write_init_packets(pos.get(), &mut *client);
1047 }
1048 }
1049 }
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 old_chunk_layer.0 = chunk_layer.0;
1059
1060 if visible_entity_layers.is_changed() {
1061 old_visible_entity_layers
1062 .0
1063 .clone_from(&visible_entity_layers.0);
1064 }
1065 },
1066 );
1067}
1068
1069pub(crate) fn update_game_mode(mut clients: Query<(&mut Client, &GameMode), Changed<GameMode>>) {
1070 for (mut client, game_mode) in &mut clients {
1071 if client.is_added() {
1072 continue;
1074 }
1075
1076 client.write_packet(&GameStateChangeS2c {
1077 kind: GameEventKind::ChangeGameMode,
1078 value: *game_mode as i32 as f32,
1079 })
1080 }
1081}
1082
1083fn update_old_view_dist(
1084 mut clients: Query<(&mut OldViewDistance, &ViewDistance), Changed<ViewDistance>>,
1085) {
1086 for (mut old_dist, dist) in &mut clients {
1087 old_dist.0 = dist.0;
1088 }
1089}
1090
1091fn flush_packets(
1092 mut clients: Query<(Entity, &mut Client), Changed<Client>>,
1093 mut commands: Commands,
1094) {
1095 for (entity, mut client) in &mut clients {
1096 if let Err(e) = client.flush_packets() {
1097 warn!("Failed to flush packet queue for client {entity:?}: {e:#}.");
1098 commands.entity(entity).remove::<Client>();
1099 }
1100 }
1101}
1102
1103fn init_tracked_data(mut clients: Query<(&mut Client, &TrackedData), Added<TrackedData>>) {
1104 for (mut client, tracked_data) in &mut clients {
1105 if let Some(init_data) = tracked_data.init_data() {
1106 client.write_packet(&EntityTrackerUpdateS2c {
1107 entity_id: VarInt(0),
1108 metadata: init_data.into(),
1109 });
1110 }
1111 }
1112}
1113
1114fn update_tracked_data(mut clients: Query<(&mut Client, &TrackedData)>) {
1115 for (mut client, tracked_data) in &mut clients {
1116 if let Some(update_data) = tracked_data.update_data() {
1117 client.write_packet(&EntityTrackerUpdateS2c {
1118 entity_id: VarInt(0),
1119 metadata: update_data.into(),
1120 });
1121 }
1122 }
1123}
1124
1125fn cleanup_chunks_after_client_despawn(
1127 mut clients: Query<(View, &VisibleChunkLayer), (With<ClientMarker>, With<Despawned>)>,
1128 chunk_layers: Query<&ChunkLayer>,
1129) {
1130 for (view, layer) in &mut clients {
1131 if let Ok(layer) = chunk_layers.get(layer.0) {
1132 for pos in view.get().iter() {
1133 if let Some(chunk) = layer.chunk(pos) {
1134 chunk.dec_viewer_count();
1135 }
1136 }
1137 }
1138 }
1139}