valence_server/
client.rs

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/// The [`SystemSet`] in [`PostUpdate`] where clients have their packet buffer
45/// flushed. Any system that writes packets to clients should happen _before_
46/// this. Otherwise, the data will arrive one tick late.
47#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
48pub struct FlushPacketsSet;
49
50/// The [`SystemSet`] in [`PreUpdate`] where new clients should be
51/// spawned. Systems that need to perform initialization work on clients before
52/// users get access to it should run _after_ this set.
53#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
54pub struct SpawnClientsSet;
55
56/// The system set where various facets of the client are updated. Systems that
57/// modify layers should run _before_ this.
58#[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/// The bundle of components needed for clients to function. All components are
101/// required unless otherwise stated.
102#[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
185/// Arguments for [`ClientBundle::new`].
186pub struct ClientBundleArgs {
187    /// The username for the client.
188    pub username: String,
189    pub uuid: Uuid,
190    pub ip: IpAddr,
191    pub properties: Vec<Property>,
192    pub conn: Box<dyn ClientConnection>,
193    /// The packet encoder to use. This should be in sync with [`Self::conn`].
194    pub enc: PacketEncoder,
195}
196
197/// Marker [`Component`] for client entities. This component should exist even
198/// if the client is disconnected.
199#[derive(Component, Copy, Clone)]
200pub struct ClientMarker;
201
202/// The main client component. Contains the underlying network connection and
203/// packet buffer.
204///
205/// The component is removed when the client is disconnected. You are allowed to
206/// remove the component yourself.
207#[derive(Component)]
208pub struct Client {
209    conn: Box<dyn ClientConnection>,
210    pub(crate) enc: PacketEncoder,
211}
212
213/// Represents the bidirectional packet channel between the server and a client
214/// in the "play" state.
215pub trait ClientConnection: Send + Sync + 'static {
216    /// Sends encoded clientbound packet data. This function must not block and
217    /// the data should be sent as soon as possible.
218    fn try_send(&mut self, bytes: BytesMut) -> anyhow::Result<()>;
219    /// Receives the next pending serverbound packet. This must return
220    /// immediately without blocking.
221    fn try_recv(&mut self) -> anyhow::Result<Option<ReceivedPacket>>;
222    /// The number of pending packets waiting to be received via
223    /// [`Self::try_recv`].
224    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    /// The moment in time this packet arrived. This is _not_ the instant this
234    /// packet was returned from [`ClientConnection::try_recv`].
235    pub timestamp: Instant,
236    /// This packet's ID.
237    pub id: i32,
238    /// The content of the packet, excluding the leading varint packet ID.
239    pub body: Bytes,
240}
241
242impl Drop for Client {
243    fn drop(&mut self) {
244        _ = self.flush_packets();
245    }
246}
247
248/// Writes packets into this client's packet buffer. The buffer is flushed at
249/// the end of the tick.
250impl 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    /// Flushes the packet queue to the underlying connection.
273    ///
274    /// This is called automatically at the end of the tick and when the client
275    /// is dropped. Unless you're in a hurry, there's usually no reason to
276    /// call this method yourself.
277    ///
278    /// Returns an error if flushing was unsuccessful.
279    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    /// Kills the client and shows `message` on the death screen. If an entity
289    /// killed the player, you should supply it as `killer`.
290    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    /// Respawns client. Optionally can roll the credits before respawning.
298    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    /// Puts a particle effect at the given position, only for this client.
306    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    /// Plays a sound effect at the given position, only for this client.
326    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    /// `velocity` is in m/s.
350    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    /// Triggers an [`EntityStatus`].
358    ///
359    /// The status is only visible to this client.
360    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/// A [`Command`] to disconnect a [`Client`] with a displayed reason.
369#[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/// Contains a list of Minecraft entities that need to be despawned. Entity IDs
390/// in this list will be despawned all at once at the end of the tick.
391///
392/// You should not need to use this directly under normal circumstances.
393#[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    /// Sends the entity remove packet and clears the buffer. Does nothing if
407    /// the buffer is empty.
408    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    /// Finds the property with the name "textures".
433    pub fn textures(&self) -> Option<&Property> {
434        self.0.iter().find(|prop| prop.name == "textures")
435    }
436
437    /// Finds the property with the name "textures".
438    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    /// `dist` is clamped to `2..=32`.
475    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/// The [`ViewDistance`] at the end of the previous tick. Automatically updated
487/// as [`ViewDistance`] is changed.
488#[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/// A [`Component`] containing a handle to the [`ChunkLayer`] a client can
523/// see.
524///
525/// A client can only see one chunk layer at a time. Mutating this component
526/// will cause the client to respawn in the new chunk layer.
527#[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/// The value of [`VisibleChunkLayer`] from the end of the previous tick.
537#[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/// A [`Component`] containing the set of [`EntityLayer`]s a client can see.
547/// All Minecraft entities from all layers in this set are potentially visible
548/// to the client.
549///
550/// This set can be mutated at any time to change which entity layers are
551/// visible to the client. [`Despawned`] entity layers are automatically
552/// removed.
553#[derive(Component, Default, Debug)]
554pub struct VisibleEntityLayers(pub BTreeSet<Entity>);
555
556/// The value of [`VisibleEntityLayers`] from the end of the previous tick.
557#[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
566/// A system for adding [`Despawned`] components to disconnected clients. This
567/// works by listening for removed [`Client`] components.
568pub 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            // Join game packet includes the view distance.
585            continue;
586        }
587
588        if dist.0 != old_dist.0 {
589            // Note: This packet is just aesthetic.
590            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            // Chunk layer messages
634            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                // Global messages
639                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                // Local messages
655                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                                // Chunk is being loaded and unloaded on the
691                                // same tick, so there's no need to do anything.
692                                debug_assert!(chunk_layer.chunk(pos).is_none());
693                            }
694                            [.., ChunkLayer::LOAD | ChunkLayer::OVERWRITE] => {
695                                // Load chunk.
696                                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                                // Unload chunk.
702                                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            // Entity layer messages
718            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                    // Global messages
724                    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                                // Remove this entity layer. The changes to the visible entity layer
736                                // set will be detected by the `update_view_and_layers` system and
737                                // despawning of entities will happen there.
738                                visible_entity_layers.0.remove(&layer_id);
739                            }
740                        }
741                    }
742
743                    // Local messages
744                    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                                            // Spawn at the entity's old position since we may get a
782                                            // relative movement packet for this entity in a later
783                                            // iteration of the loop.
784                                            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                                            // Spawn at the entity's old position since we may get a
805                                            // relative movement packet for this entity in a later
806                                            // iteration of the loop.
807                                            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            // Make sure the center chunk is set before loading chunks! Otherwise the client
893            // may ignore the chunk.
894            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            // Was the client's chunk layer changed?
902            if old_chunk_layer.0 != chunk_layer.0 {
903                // Unload all chunks in the old view.
904                // TODO: can we skip this step if old dimension != new dimension?
905                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                // Load all chunks in the new view.
915                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                // Unload all entities from the old view in all old visible entity layers.
925                // TODO: can we skip this step if old dimension != new dimension?
926                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                // Load all entities in the new view from all new visible entity layers.
943                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                // Update the client's visible entity layers.
958                if visible_entity_layers.is_changed() {
959                    // Unload all entity layers that are no longer visible in the old view.
960                    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                    // Load all entity layers that are newly visible in the old view.
980                    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                // Update the client's view (chunk position and view distance)
999                if old_view != view {
1000                    // Unload chunks and entities in the old view and load chunks and entities in
1001                    // the new view. We don't need to do any work where the old and new view
1002                    // overlap.
1003
1004                    // Unload chunks in the old view.
1005                    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                    // Load chunks in the new view.
1015                    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                    // Unload entities from the new visible layers (since we updated it above).
1025                    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                    // Load entities from the new visible layers.
1040                    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            // Update the old layers.
1057
1058            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            // Game join packet includes the initial game mode.
1073            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
1125/// Decrement viewer count of chunks when the client is despawned.
1126fn 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}