valence_server/
client.rs

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 derive_more::{Deref, DerefMut, From, Into};
14use tracing::warn;
15use uuid::Uuid;
16use valence_entity::attributes::{EntityAttributes, TrackedEntityAttributes};
17use valence_entity::living::Health;
18use valence_entity::player::{Food, PlayerEntityBundle, Saturation};
19use valence_entity::query::EntityInitQuery;
20use valence_entity::tracked_data::TrackedData;
21use valence_entity::{
22    ClearEntityChangesSet, EntityId, EntityStatus, OldPosition, Position, Velocity,
23};
24use valence_math::{DVec3, Vec3};
25use valence_protocol::encode::{PacketEncoder, WritePacket};
26use valence_protocol::packets::play::chunk_biome_data_s2c::ChunkBiome;
27use valence_protocol::packets::play::game_state_change_s2c::GameEventKind;
28use valence_protocol::packets::play::particle_s2c::Particle;
29use valence_protocol::packets::play::{
30    ChunkBiomeDataS2c, ChunkLoadDistanceS2c, ChunkRenderDistanceCenterS2c, DeathMessageS2c,
31    DisconnectS2c, EntitiesDestroyS2c, EntityAttributesS2c, EntityStatusS2c,
32    EntityTrackerUpdateS2c, EntityVelocityUpdateS2c, GameStateChangeS2c, HealthUpdateS2c,
33    ParticleS2c, PlaySoundS2c, UnloadChunkS2c,
34};
35use valence_protocol::profile::Property;
36use valence_protocol::sound::{Sound, SoundCategory, SoundId};
37use valence_protocol::text::{IntoText, Text};
38use valence_protocol::var_int::VarInt;
39use valence_protocol::{BlockPos, ChunkPos, Encode, GameMode, Packet};
40use valence_registry::RegistrySet;
41use valence_server_common::{Despawned, UniqueId};
42
43use crate::layer::{ChunkLayer, EntityLayer, UpdateLayersPostClientSet, UpdateLayersPreClientSet};
44use crate::ChunkView;
45
46pub struct ClientPlugin;
47
48/// The [`SystemSet`] in [`PostUpdate`] where clients have their packet buffer
49/// flushed. Any system that writes packets to clients should happen _before_
50/// this. Otherwise, the data will arrive one tick late.
51#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
52pub struct FlushPacketsSet;
53
54/// The [`SystemSet`] in [`PreUpdate`] where new clients should be
55/// spawned. Systems that need to perform initialization work on clients before
56/// users get access to it should run _after_ this set.
57#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
58pub struct SpawnClientsSet;
59
60/// The system set where various facets of the client are updated. Systems that
61/// modify layers should run _before_ this.
62#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
63pub struct UpdateClientsSet;
64
65impl Plugin for ClientPlugin {
66    fn build(&self, app: &mut App) {
67        app.add_systems(
68            PostUpdate,
69            (
70                (
71                    crate::spawn::initial_join.after(RegistrySet),
72                    update_chunk_load_dist,
73                    handle_layer_messages.after(update_chunk_load_dist),
74                    update_view_and_layers
75                        .after(crate::spawn::initial_join)
76                        .after(handle_layer_messages),
77                    cleanup_chunks_after_client_despawn.after(update_view_and_layers),
78                    crate::spawn::update_respawn_position.after(update_view_and_layers),
79                    crate::spawn::respawn.after(crate::spawn::update_respawn_position),
80                    update_old_view_dist.after(update_view_and_layers),
81                    update_game_mode,
82                    update_food_saturation_health,
83                    update_tracked_data,
84                    init_tracked_data,
85                    update_tracked_attributes,
86                    init_tracked_attributes,
87                )
88                    .in_set(UpdateClientsSet),
89                flush_packets.in_set(FlushPacketsSet),
90            ),
91        )
92        .configure_sets(PreUpdate, SpawnClientsSet)
93        .configure_sets(
94            PostUpdate,
95            (
96                UpdateClientsSet
97                    .after(UpdateLayersPreClientSet)
98                    .before(UpdateLayersPostClientSet)
99                    .before(FlushPacketsSet),
100                ClearEntityChangesSet.after(UpdateClientsSet),
101                FlushPacketsSet,
102            ),
103        )
104        .add_event::<LoadEntityForClientEvent>()
105        .add_event::<UnloadEntityForClientEvent>();
106    }
107}
108
109/// The bundle of components needed for clients to function. All components are
110/// required unless otherwise stated.
111#[derive(Bundle)]
112pub struct ClientBundle {
113    pub marker: ClientMarker,
114    pub client: Client,
115    pub settings: crate::client_settings::ClientSettings,
116    pub entity_remove_buf: EntityRemoveBuf,
117    pub username: Username,
118    pub ip: Ip,
119    pub properties: Properties,
120    pub respawn_pos: crate::spawn::RespawnPosition,
121    pub op_level: crate::op_level::OpLevel,
122    pub action_sequence: crate::action::ActionSequence,
123    pub view_distance: ViewDistance,
124    pub old_view_distance: OldViewDistance,
125    pub visible_chunk_layer: VisibleChunkLayer,
126    pub old_visible_chunk_layer: OldVisibleChunkLayer,
127    pub visible_entity_layers: VisibleEntityLayers,
128    pub old_visible_entity_layers: OldVisibleEntityLayers,
129    pub keepalive_state: crate::keepalive::KeepaliveState,
130    pub ping: crate::keepalive::Ping,
131    pub teleport_state: crate::teleport::TeleportState,
132    pub game_mode: GameMode,
133    pub prev_game_mode: crate::spawn::PrevGameMode,
134    pub death_location: crate::spawn::DeathLocation,
135    pub is_hardcore: crate::spawn::IsHardcore,
136    pub hashed_seed: crate::spawn::HashedSeed,
137    pub reduced_debug_info: crate::spawn::ReducedDebugInfo,
138    pub has_respawn_screen: crate::spawn::HasRespawnScreen,
139    pub is_debug: crate::spawn::IsDebug,
140    pub is_flat: crate::spawn::IsFlat,
141    pub portal_cooldown: crate::spawn::PortalCooldown,
142    pub flying_speed: crate::abilities::FlyingSpeed,
143    pub fov_modifier: crate::abilities::FovModifier,
144    pub player_abilities_flags: crate::abilities::PlayerAbilitiesFlags,
145    pub player: PlayerEntityBundle,
146}
147
148impl ClientBundle {
149    pub fn new(args: ClientBundleArgs) -> Self {
150        Self {
151            marker: ClientMarker,
152            client: Client {
153                conn: args.conn,
154                enc: args.enc,
155            },
156            settings: Default::default(),
157            entity_remove_buf: Default::default(),
158            username: Username(args.username),
159            ip: Ip(args.ip),
160            properties: Properties(args.properties),
161            respawn_pos: Default::default(),
162            op_level: Default::default(),
163            action_sequence: Default::default(),
164            view_distance: Default::default(),
165            old_view_distance: OldViewDistance(2),
166            visible_chunk_layer: Default::default(),
167            old_visible_chunk_layer: OldVisibleChunkLayer(Entity::PLACEHOLDER),
168            visible_entity_layers: Default::default(),
169            old_visible_entity_layers: OldVisibleEntityLayers(BTreeSet::new()),
170            keepalive_state: crate::keepalive::KeepaliveState::new(),
171            ping: Default::default(),
172            teleport_state: crate::teleport::TeleportState::new(),
173            game_mode: GameMode::default(),
174            prev_game_mode: Default::default(),
175            death_location: Default::default(),
176            is_hardcore: Default::default(),
177            is_flat: Default::default(),
178            has_respawn_screen: Default::default(),
179            hashed_seed: Default::default(),
180            reduced_debug_info: Default::default(),
181            is_debug: Default::default(),
182            portal_cooldown: Default::default(),
183            flying_speed: Default::default(),
184            fov_modifier: Default::default(),
185            player_abilities_flags: Default::default(),
186            player: PlayerEntityBundle {
187                uuid: UniqueId(args.uuid),
188                ..Default::default()
189            },
190        }
191    }
192}
193
194/// Arguments for [`ClientBundle::new`].
195pub struct ClientBundleArgs {
196    /// The username for the client.
197    pub username: String,
198    /// UUID of the client.
199    pub uuid: Uuid,
200    /// IP address of the client.
201    pub ip: IpAddr,
202    /// Properties of this client from the game profile.
203    pub properties: Vec<Property>,
204    /// The abstract socket connection.
205    pub conn: Box<dyn ClientConnection>,
206    /// The packet encoder to use. This should be in sync with [`Self::conn`].
207    pub enc: PacketEncoder,
208}
209
210/// Marker [`Component`] for client entities. This component should exist even
211/// if the client is disconnected.
212#[derive(Component, Copy, Clone)]
213pub struct ClientMarker;
214
215/// The main client component. Contains the underlying network connection and
216/// packet buffer.
217///
218/// The component is removed when the client is disconnected. You are allowed to
219/// remove the component yourself.
220#[derive(Component)]
221pub struct Client {
222    conn: Box<dyn ClientConnection>,
223    pub(crate) enc: PacketEncoder,
224}
225
226/// Represents the bidirectional packet channel between the server and a client
227/// in the "play" state.
228pub trait ClientConnection: Send + Sync + 'static {
229    /// Sends encoded clientbound packet data. This function must not block and
230    /// the data should be sent as soon as possible.
231    fn try_send(&mut self, bytes: BytesMut) -> anyhow::Result<()>;
232    /// Receives the next pending serverbound packet. This must return
233    /// immediately without blocking.
234    fn try_recv(&mut self) -> anyhow::Result<Option<ReceivedPacket>>;
235    /// The number of pending packets waiting to be received via
236    /// [`Self::try_recv`].
237    fn len(&self) -> usize;
238
239    fn is_empty(&self) -> bool {
240        self.len() == 0
241    }
242}
243
244#[derive(Clone, Debug)]
245pub struct ReceivedPacket {
246    /// The moment in time this packet arrived. This is _not_ the instant this
247    /// packet was returned from [`ClientConnection::try_recv`].
248    pub timestamp: Instant,
249    /// This packet's ID.
250    pub id: i32,
251    /// The content of the packet, excluding the leading varint packet ID.
252    pub body: Bytes,
253}
254
255impl Drop for Client {
256    fn drop(&mut self) {
257        _ = self.flush_packets();
258    }
259}
260
261/// Writes packets into this client's packet buffer. The buffer is flushed at
262/// the end of the tick.
263impl WritePacket for Client {
264    fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
265    where
266        P: Packet + Encode,
267    {
268        self.enc.write_packet_fallible(packet)
269    }
270
271    fn write_packet_bytes(&mut self, bytes: &[u8]) {
272        self.enc.write_packet_bytes(bytes)
273    }
274}
275
276impl Client {
277    pub fn connection(&self) -> &dyn ClientConnection {
278        self.conn.as_ref()
279    }
280
281    pub fn connection_mut(&mut self) -> &mut dyn ClientConnection {
282        self.conn.as_mut()
283    }
284
285    /// Flushes the packet queue to the underlying connection.
286    ///
287    /// This is called automatically at the end of the tick and when the client
288    /// is dropped. Unless you're in a hurry, there's usually no reason to
289    /// call this method yourself.
290    ///
291    /// Returns an error if flushing was unsuccessful.
292    pub fn flush_packets(&mut self) -> anyhow::Result<()> {
293        let bytes = self.enc.take();
294        if !bytes.is_empty() {
295            self.conn.try_send(bytes)
296        } else {
297            Ok(())
298        }
299    }
300
301    /// Kills the client and shows `message` on the death screen. If an entity
302    /// killed the player, you should supply it as `killer`.
303    pub fn kill<'a, M: IntoText<'a>>(&mut self, message: M) {
304        self.write_packet(&DeathMessageS2c {
305            player_id: VarInt(0),
306            message: message.into_cow_text(),
307        });
308    }
309
310    /// Respawns client. Optionally can roll the credits before respawning.
311    pub fn win_game(&mut self, show_credits: bool) {
312        self.write_packet(&GameStateChangeS2c {
313            kind: GameEventKind::WinGame,
314            value: if show_credits { 1.0 } else { 0.0 },
315        });
316    }
317
318    /// Puts a particle effect at the given position, only for this client.
319    pub fn play_particle<P, O>(
320        &mut self,
321        particle: &Particle,
322        long_distance: bool,
323        position: P,
324        offset: O,
325        max_speed: f32,
326        count: i32,
327    ) where
328        P: Into<DVec3>,
329        O: Into<Vec3>,
330    {
331        self.write_packet(&ParticleS2c {
332            particle: Cow::Borrowed(particle),
333            long_distance,
334            position: position.into(),
335            offset: offset.into(),
336            max_speed,
337            count,
338        })
339    }
340
341    /// Plays a sound effect at the given position, only for this client.
342    pub fn play_sound<P: Into<DVec3>>(
343        &mut self,
344        sound: Sound,
345        category: SoundCategory,
346        position: P,
347        volume: f32,
348        pitch: f32,
349    ) {
350        let position = position.into();
351
352        self.write_packet(&PlaySoundS2c {
353            id: SoundId::Direct {
354                id: sound.to_ident().into(),
355                range: None,
356            },
357            category,
358            position: (position * 8.0).as_ivec3(),
359            volume,
360            pitch,
361            seed: rand::random(),
362        });
363    }
364
365    /// `velocity` is in m/s.
366    pub fn set_velocity<V: Into<Vec3>>(&mut self, velocity: V) {
367        self.write_packet(&EntityVelocityUpdateS2c {
368            entity_id: VarInt(0),
369            velocity: Velocity(velocity.into()).to_packet_units(),
370        });
371    }
372
373    /// Triggers an [`EntityStatus`].
374    ///
375    /// The status is only visible to this client.
376    pub fn trigger_status(&mut self, status: EntityStatus) {
377        self.write_packet(&EntityStatusS2c {
378            entity_id: 0,
379            entity_status: status as u8,
380        });
381    }
382}
383
384/// A [`Command`] to disconnect a [`Client`] with a displayed reason.
385#[derive(Clone, PartialEq, Debug)]
386pub struct DisconnectClient {
387    pub client: Entity,
388    pub reason: Text,
389}
390
391impl Command for DisconnectClient {
392    fn apply(self, world: &mut World) {
393        if let Some(mut entity) = world.get_entity_mut(self.client) {
394            if let Some(mut client) = entity.get_mut::<Client>() {
395                client.write_packet(&DisconnectS2c {
396                    reason: self.reason.into(),
397                });
398
399                // Despawned will be removed at the end of the tick, this way, the packets have
400                // time to be sent.
401                entity.insert(Despawned);
402            }
403        }
404    }
405}
406
407/// Contains a list of Minecraft entities that need to be despawned. Entity IDs
408/// in this list will be despawned all at once at the end of the tick.
409///
410/// You should not need to use this directly under normal circumstances.
411#[derive(Component, Default, Debug)]
412pub struct EntityRemoveBuf(Vec<VarInt>);
413
414impl EntityRemoveBuf {
415    pub fn push(&mut self, entity_id: i32) {
416        debug_assert!(
417            entity_id != 0,
418            "removing entity with protocol ID 0 (which should be reserved for clients)"
419        );
420
421        self.0.push(VarInt(entity_id));
422    }
423
424    /// Sends the entity remove packet and clears the buffer. Does nothing if
425    /// the buffer is empty.
426    pub fn send_and_clear<W: WritePacket>(&mut self, mut w: W) {
427        if !self.0.is_empty() {
428            w.write_packet(&EntitiesDestroyS2c {
429                entity_ids: Cow::Borrowed(&self.0),
430            });
431
432            self.0.clear();
433        }
434    }
435}
436
437#[derive(Component, Clone, PartialEq, Eq, Default, Debug, Deref)]
438pub struct Username(pub String);
439
440impl fmt::Display for Username {
441    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
442        self.0.fmt(f)
443    }
444}
445
446/// Player properties from the game profile.
447#[derive(Component, Clone, PartialEq, Eq, Default, Debug, Deref, DerefMut, From, Into)]
448pub struct Properties(pub Vec<Property>);
449
450impl Properties {
451    /// Finds the property with the name "textures".
452    pub fn textures(&self) -> Option<&Property> {
453        self.0.iter().find(|p| p.name == "textures")
454    }
455
456    /// Finds the property with the name "textures" mutably.
457    pub fn textures_mut(&mut self) -> Option<&mut Property> {
458        self.0.iter_mut().find(|p| p.name == "textures")
459    }
460
461    /// Returns the value of the "textures" property. It's a base64-encoded
462    /// JSON string that contains the skin and cape URLs.
463    pub fn skin(&self) -> Option<&str> {
464        self.textures().map(|p| p.value.as_str())
465    }
466
467    /// Sets the value of the "textures" property, or adds it if it does not
468    /// exist. Can be used for custom skins on player entities.
469    ///
470    /// `signature` is the Yggdrasil signature for the texture data. It is
471    /// required if you want the skin to show up on vanilla Notchian
472    /// clients. You can't sign skins yourself, so you'll have to get it from
473    /// Mojang.
474    pub fn set_skin<Sk: Into<String>, Si: Into<String>>(&mut self, skin: Sk, signature: Si) {
475        if let Some(prop) = self.textures_mut() {
476            prop.value = skin.into();
477            prop.signature = Some(signature.into());
478        } else {
479            self.0.push(Property {
480                name: "textures".to_owned(),
481                value: skin.into(),
482                signature: Some(signature.into()),
483            });
484        }
485    }
486}
487
488#[derive(Clone, PartialEq, Eq, Debug)]
489pub struct PropertyValue {
490    pub value: String,
491    pub signature: Option<String>,
492}
493
494#[derive(Component, Clone, PartialEq, Eq, Debug, Deref)]
495pub struct Ip(pub IpAddr);
496
497#[derive(Component, Clone, PartialEq, Eq, Debug, Deref)]
498pub struct ViewDistance(u8);
499
500impl ViewDistance {
501    pub fn new(dist: u8) -> Self {
502        let mut new = Self(0);
503        new.set(dist);
504        new
505    }
506
507    pub fn get(&self) -> u8 {
508        self.0
509    }
510
511    /// `dist` is clamped to `2..=32`.
512    pub fn set(&mut self, dist: u8) {
513        self.0 = dist.clamp(2, 32);
514    }
515}
516
517impl Default for ViewDistance {
518    fn default() -> Self {
519        Self(2)
520    }
521}
522
523/// The [`ViewDistance`] at the end of the previous tick. Automatically updated
524/// as [`ViewDistance`] is changed.
525#[derive(Component, Clone, PartialEq, Eq, Default, Debug, Deref)]
526pub struct OldViewDistance(u8);
527
528impl OldViewDistance {
529    pub fn get(&self) -> u8 {
530        self.0
531    }
532}
533
534#[derive(QueryData, Copy, Clone, Debug)]
535pub struct View {
536    pub pos: &'static Position,
537    pub view_dist: &'static ViewDistance,
538}
539
540impl ViewItem<'_> {
541    pub fn get(&self) -> ChunkView {
542        ChunkView::new(self.pos.0.into(), self.view_dist.0)
543    }
544}
545
546#[derive(QueryData, Copy, Clone, Debug)]
547pub struct OldView {
548    pub old_pos: &'static OldPosition,
549    pub old_view_dist: &'static OldViewDistance,
550}
551
552impl OldViewItem<'_> {
553    pub fn get(&self) -> ChunkView {
554        ChunkView::new(self.old_pos.get().into(), self.old_view_dist.0)
555    }
556}
557
558/// A [`Component`] containing a handle to the [`ChunkLayer`] a client can
559/// see.
560///
561/// A client can only see one chunk layer at a time. Mutating this component
562/// will cause the client to respawn in the new chunk layer.
563#[derive(Component, Copy, Clone, PartialEq, Eq, Debug, Deref, DerefMut)]
564pub struct VisibleChunkLayer(pub Entity);
565
566impl Default for VisibleChunkLayer {
567    fn default() -> Self {
568        Self(Entity::PLACEHOLDER)
569    }
570}
571
572/// The value of [`VisibleChunkLayer`] from the end of the previous tick.
573#[derive(Component, PartialEq, Eq, Debug, Deref)]
574pub struct OldVisibleChunkLayer(Entity);
575
576impl OldVisibleChunkLayer {
577    pub fn get(&self) -> Entity {
578        self.0
579    }
580}
581
582/// A [`Component`] containing the set of [`EntityLayer`]s a client can see.
583/// All Minecraft entities from all layers in this set are potentially visible
584/// to the client.
585///
586/// This set can be mutated at any time to change which entity layers are
587/// visible to the client. [`Despawned`] entity layers are automatically
588/// removed.
589#[derive(Component, Default, Debug)]
590pub struct VisibleEntityLayers(pub BTreeSet<Entity>);
591
592/// The value of [`VisibleEntityLayers`] from the end of the previous tick.
593#[derive(Component, Default, Debug, Deref)]
594pub struct OldVisibleEntityLayers(BTreeSet<Entity>);
595
596impl OldVisibleEntityLayers {
597    pub fn get(&self) -> &BTreeSet<Entity> {
598        &self.0
599    }
600}
601
602/// A system for adding [`Despawned`] components to disconnected clients. This
603/// works by listening for removed [`Client`] components.
604pub fn despawn_disconnected_clients(
605    mut commands: Commands,
606    mut disconnected_clients: RemovedComponents<Client>,
607) {
608    for entity in disconnected_clients.read() {
609        if let Some(mut entity) = commands.get_entity(entity) {
610            entity.insert(Despawned);
611        }
612    }
613}
614
615fn update_chunk_load_dist(
616    mut clients: Query<(&mut Client, &ViewDistance, &OldViewDistance), Changed<ViewDistance>>,
617) {
618    for (mut client, dist, old_dist) in &mut clients {
619        if client.is_added() {
620            // Join game packet includes the view distance.
621            continue;
622        }
623
624        if dist.0 != old_dist.0 {
625            // Note: This packet is just aesthetic.
626            client.write_packet(&ChunkLoadDistanceS2c {
627                view_distance: VarInt(dist.0.into()),
628            });
629        }
630    }
631}
632
633fn handle_layer_messages(
634    mut clients: Query<(
635        Entity,
636        &EntityId,
637        &mut Client,
638        &mut EntityRemoveBuf,
639        OldView,
640        &OldVisibleChunkLayer,
641        &mut VisibleEntityLayers,
642        &OldVisibleEntityLayers,
643    )>,
644    chunk_layers: Query<&ChunkLayer>,
645    entity_layers: Query<&EntityLayer>,
646    entities: Query<(EntityInitQuery, &OldPosition)>,
647) {
648    clients.par_iter_mut().for_each(
649        |(
650            self_entity,
651            self_entity_id,
652            mut client,
653            mut remove_buf,
654            old_view,
655            old_visible_chunk_layer,
656            mut visible_entity_layers,
657            old_visible_entity_layers,
658        )| {
659            let block_pos = BlockPos::from(old_view.old_pos.get());
660            let old_view = old_view.get();
661
662            fn in_radius(p0: BlockPos, p1: BlockPos, radius_squared: u32) -> bool {
663                let dist_squared =
664                    (p1.x - p0.x).pow(2) + (p1.y - p0.y).pow(2) + (p1.z - p0.z).pow(2);
665
666                dist_squared as u32 <= radius_squared
667            }
668
669            // Chunk layer messages
670            if let Ok(chunk_layer) = chunk_layers.get(old_visible_chunk_layer.get()) {
671                let messages = chunk_layer.messages();
672                let bytes = messages.bytes();
673
674                // Global messages
675                for (msg, range) in messages.iter_global() {
676                    match msg {
677                        crate::layer::chunk::GlobalMsg::Packet => {
678                            client.write_packet_bytes(&bytes[range]);
679                        }
680                        crate::layer::chunk::GlobalMsg::PacketExcept { except } => {
681                            if self_entity != except {
682                                client.write_packet_bytes(&bytes[range]);
683                            }
684                        }
685                    }
686                }
687
688                let mut chunk_biome_buf = vec![];
689
690                // Local messages
691                messages.query_local(old_view, |msg, range| match msg {
692                    crate::layer::chunk::LocalMsg::PacketAt { .. } => {
693                        client.write_packet_bytes(&bytes[range]);
694                    }
695                    crate::layer::chunk::LocalMsg::PacketAtExcept { except, .. } => {
696                        if self_entity != except {
697                            client.write_packet_bytes(&bytes[range]);
698                        }
699                    }
700                    crate::layer::chunk::LocalMsg::RadiusAt {
701                        center,
702                        radius_squared,
703                    } => {
704                        if in_radius(block_pos, center, radius_squared) {
705                            client.write_packet_bytes(&bytes[range]);
706                        }
707                    }
708                    crate::layer::chunk::LocalMsg::RadiusAtExcept {
709                        center,
710                        radius_squared,
711                        except,
712                    } => {
713                        if self_entity != except && in_radius(block_pos, center, radius_squared) {
714                            client.write_packet_bytes(&bytes[range]);
715                        }
716                    }
717                    crate::layer::chunk::LocalMsg::ChangeBiome { pos } => {
718                        chunk_biome_buf.push(ChunkBiome {
719                            pos,
720                            data: &bytes[range],
721                        });
722                    }
723                    crate::layer::chunk::LocalMsg::ChangeChunkState { pos } => {
724                        match &bytes[range] {
725                            [ChunkLayer::LOAD, .., ChunkLayer::UNLOAD] => {
726                                // Chunk is being loaded and unloaded on the
727                                // same tick, so there's no need to do anything.
728                                debug_assert!(chunk_layer.chunk(pos).is_none());
729                            }
730                            [.., ChunkLayer::LOAD | ChunkLayer::OVERWRITE] => {
731                                // Load chunk.
732                                let chunk = chunk_layer.chunk(pos).expect("chunk must exist");
733                                chunk.write_init_packets(&mut *client, pos, chunk_layer.info());
734                                chunk.inc_viewer_count();
735                            }
736                            [.., ChunkLayer::UNLOAD] => {
737                                // Unload chunk.
738                                client.write_packet(&UnloadChunkS2c { pos });
739                                debug_assert!(chunk_layer.chunk(pos).is_none());
740                            }
741                            _ => unreachable!("invalid message data while changing chunk state"),
742                        }
743                    }
744                });
745
746                if !chunk_biome_buf.is_empty() {
747                    client.write_packet(&ChunkBiomeDataS2c {
748                        chunks: chunk_biome_buf.into(),
749                    });
750                }
751            }
752
753            // Entity layer messages
754            for &layer_id in &old_visible_entity_layers.0 {
755                if let Ok(layer) = entity_layers.get(layer_id) {
756                    let messages = layer.messages();
757                    let bytes = messages.bytes();
758
759                    // Global messages
760                    for (msg, range) in messages.iter_global() {
761                        match msg {
762                            crate::layer::entity::GlobalMsg::Packet => {
763                                client.write_packet_bytes(&bytes[range]);
764                            }
765                            crate::layer::entity::GlobalMsg::PacketExcept { except } => {
766                                if self_entity != except {
767                                    client.write_packet_bytes(&bytes[range]);
768                                }
769                            }
770                            crate::layer::entity::GlobalMsg::DespawnLayer => {
771                                // Remove this entity layer. The changes to the visible entity layer
772                                // set will be detected by the `update_view_and_layers` system and
773                                // despawning of entities will happen there.
774                                visible_entity_layers.0.remove(&layer_id);
775                            }
776                        }
777                    }
778
779                    // Local messages
780                    messages.query_local(old_view, |msg, range| match msg {
781                        crate::layer::entity::LocalMsg::DespawnEntity { dest_layer, .. } => {
782                            if !old_visible_entity_layers.0.contains(&dest_layer) {
783                                let mut bytes = &bytes[range];
784
785                                while let Ok(id) = bytes.read_i32::<NativeEndian>() {
786                                    if self_entity_id.get() != id {
787                                        remove_buf.push(id);
788                                    }
789                                }
790                            }
791                        }
792                        crate::layer::entity::LocalMsg::DespawnEntityTransition {
793                            dest_pos,
794                            ..
795                        } => {
796                            if !old_view.contains(dest_pos) {
797                                let mut bytes = &bytes[range];
798
799                                while let Ok(id) = bytes.read_i32::<NativeEndian>() {
800                                    if self_entity_id.get() != id {
801                                        remove_buf.push(id);
802                                    }
803                                }
804                            }
805                        }
806                        crate::layer::entity::LocalMsg::SpawnEntity { src_layer, .. } => {
807                            if !old_visible_entity_layers.0.contains(&src_layer) {
808                                let mut bytes = &bytes[range];
809
810                                while let Ok(u64) = bytes.read_u64::<NativeEndian>() {
811                                    let entity = Entity::from_bits(u64);
812
813                                    if self_entity != entity {
814                                        if let Ok((init, old_pos)) = entities.get(entity) {
815                                            remove_buf.send_and_clear(&mut *client);
816
817                                            // Spawn at the entity's old position since we may get a
818                                            // relative movement packet for this entity in a later
819                                            // iteration of the loop.
820                                            init.write_init_packets(old_pos.get(), &mut *client);
821                                        }
822                                    }
823                                }
824                            }
825                        }
826                        crate::layer::entity::LocalMsg::SpawnEntityTransition {
827                            src_pos, ..
828                        } => {
829                            if !old_view.contains(src_pos) {
830                                let mut bytes = &bytes[range];
831
832                                while let Ok(u64) = bytes.read_u64::<NativeEndian>() {
833                                    let entity = Entity::from_bits(u64);
834
835                                    if self_entity != entity {
836                                        if let Ok((init, old_pos)) = entities.get(entity) {
837                                            remove_buf.send_and_clear(&mut *client);
838
839                                            // Spawn at the entity's old position since we may get a
840                                            // relative movement packet for this entity in a later
841                                            // iteration of the loop.
842                                            init.write_init_packets(old_pos.get(), &mut *client);
843                                        }
844                                    }
845                                }
846                            }
847                        }
848                        crate::layer::entity::LocalMsg::PacketAt { .. } => {
849                            client.write_packet_bytes(&bytes[range]);
850                        }
851                        crate::layer::entity::LocalMsg::PacketAtExcept { except, .. } => {
852                            if self_entity != except {
853                                client.write_packet_bytes(&bytes[range]);
854                            }
855                        }
856                        crate::layer::entity::LocalMsg::RadiusAt {
857                            center,
858                            radius_squared,
859                        } => {
860                            if in_radius(block_pos, center, radius_squared) {
861                                client.write_packet_bytes(&bytes[range]);
862                            }
863                        }
864                        crate::layer::entity::LocalMsg::RadiusAtExcept {
865                            center,
866                            radius_squared,
867                            except,
868                        } => {
869                            if self_entity != except && in_radius(block_pos, center, radius_squared)
870                            {
871                                client.write_packet_bytes(&bytes[range]);
872                            }
873                        }
874                    });
875
876                    remove_buf.send_and_clear(&mut *client);
877                }
878            }
879        },
880    );
881}
882
883/// This event will be emitted when a entity is unloaded for a client (e.g when
884/// moving out of range of the entity).
885#[derive(Debug, Clone, PartialEq, Event)]
886pub struct UnloadEntityForClientEvent {
887    /// The client to unload the entity for.
888    pub client: Entity,
889    /// The entity ID of the entity that will be unloaded.
890    pub entity_unloaded: Entity,
891}
892
893/// This event will be emitted when a entity is loaded for a client (e.g when
894/// moving into range of the entity).
895#[derive(Debug, Clone, PartialEq, Event)]
896pub struct LoadEntityForClientEvent {
897    /// The client to load the entity for.
898    pub client: Entity,
899    /// The entity that will be loaded.
900    pub entity_loaded: Entity,
901}
902
903pub(crate) fn update_view_and_layers(
904    mut clients: Query<
905        (
906            Entity,
907            &mut Client,
908            &mut EntityRemoveBuf,
909            &VisibleChunkLayer,
910            &mut OldVisibleChunkLayer,
911            Ref<VisibleEntityLayers>,
912            &mut OldVisibleEntityLayers,
913            &Position,
914            &OldPosition,
915            &ViewDistance,
916            &OldViewDistance,
917        ),
918        Or<(
919            Changed<VisibleChunkLayer>,
920            Changed<VisibleEntityLayers>,
921            Changed<Position>,
922            Changed<ViewDistance>,
923        )>,
924    >,
925    chunk_layers: Query<&ChunkLayer>,
926    entity_layers: Query<&EntityLayer>,
927    entity_ids: Query<&EntityId>,
928    entity_init: Query<(EntityInitQuery, &Position)>,
929
930    mut unload_entity_writer: EventWriter<UnloadEntityForClientEvent>,
931    mut load_entity_writer: EventWriter<LoadEntityForClientEvent>,
932) {
933    // Wrap the events in this, so we only need one channel.
934    enum ChannelEvent {
935        UnloadEntity(UnloadEntityForClientEvent),
936        LoadEntity(LoadEntityForClientEvent),
937    }
938
939    let (tx, rx) = std::sync::mpsc::channel();
940
941    (clients).par_iter_mut().for_each(
942        |(
943            self_entity,
944            mut client,
945            mut remove_buf,
946            chunk_layer,
947            mut old_chunk_layer,
948            visible_entity_layers,
949            mut old_visible_entity_layers,
950            pos,
951            old_pos,
952            view_dist,
953            old_view_dist,
954        )| {
955            let view = ChunkView::new(ChunkPos::from(pos.0), view_dist.0);
956            let old_view = ChunkView::new(ChunkPos::from(old_pos.get()), old_view_dist.0);
957
958            // Make sure the center chunk is set before loading chunks! Otherwise the client
959            // may ignore the chunk.
960            if old_view.pos != view.pos {
961                client.write_packet(&ChunkRenderDistanceCenterS2c {
962                    chunk_x: VarInt(view.pos.x),
963                    chunk_z: VarInt(view.pos.z),
964                });
965            }
966
967            // Was the client's chunk layer changed?
968            if old_chunk_layer.0 != chunk_layer.0 {
969                // Unload all chunks in the old view.
970                // TODO: can we skip this step if old dimension != new dimension?
971                if let Ok(layer) = chunk_layers.get(old_chunk_layer.0) {
972                    for pos in old_view.iter() {
973                        if let Some(chunk) = layer.chunk(pos) {
974                            client.write_packet(&UnloadChunkS2c { pos });
975                            chunk.dec_viewer_count();
976                        }
977                    }
978                }
979
980                // Load all chunks in the new view.
981                if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
982                    for pos in view.iter() {
983                        if let Some(chunk) = layer.chunk(pos) {
984                            chunk.write_init_packets(&mut *client, pos, layer.info());
985                            chunk.inc_viewer_count();
986                        }
987                    }
988                }
989
990                // Unload all entities from the old view in all old visible entity layers.
991                // TODO: can we skip this step if old dimension != new dimension?
992                for &layer in &old_visible_entity_layers.0 {
993                    if let Ok(layer) = entity_layers.get(layer) {
994                        for pos in old_view.iter() {
995                            for entity in layer.entities_at(pos) {
996                                if self_entity != entity {
997                                    if let Ok(id) = entity_ids.get(entity) {
998                                        tx.send(ChannelEvent::UnloadEntity(
999                                            UnloadEntityForClientEvent {
1000                                                client: self_entity,
1001                                                entity_unloaded: entity,
1002                                            },
1003                                        ))
1004                                        .unwrap();
1005
1006                                        remove_buf.push(id.get());
1007                                    }
1008                                }
1009                            }
1010                        }
1011                    }
1012                }
1013
1014                remove_buf.send_and_clear(&mut *client);
1015
1016                // Load all entities in the new view from all new visible entity layers.
1017                for &layer in &visible_entity_layers.0 {
1018                    if let Ok(layer) = entity_layers.get(layer) {
1019                        for pos in view.iter() {
1020                            for entity in layer.entities_at(pos) {
1021                                if self_entity != entity {
1022                                    if let Ok((init, pos)) = entity_init.get(entity) {
1023                                        tx.send(ChannelEvent::LoadEntity(
1024                                            LoadEntityForClientEvent {
1025                                                client: self_entity,
1026                                                entity_loaded: entity,
1027                                            },
1028                                        ))
1029                                        .unwrap();
1030
1031                                        init.write_init_packets(pos.get(), &mut *client);
1032                                    }
1033                                }
1034                            }
1035                        }
1036                    }
1037                }
1038            } else {
1039                // Update the client's visible entity layers.
1040                if visible_entity_layers.is_changed() {
1041                    // Unload all entity layers that are no longer visible in the old view.
1042                    for &layer in old_visible_entity_layers
1043                        .0
1044                        .difference(&visible_entity_layers.0)
1045                    {
1046                        if let Ok(layer) = entity_layers.get(layer) {
1047                            for pos in old_view.iter() {
1048                                for entity in layer.entities_at(pos) {
1049                                    if self_entity != entity {
1050                                        if let Ok(id) = entity_ids.get(entity) {
1051                                            tx.send(ChannelEvent::UnloadEntity(
1052                                                UnloadEntityForClientEvent {
1053                                                    client: self_entity,
1054                                                    entity_unloaded: entity,
1055                                                },
1056                                            ))
1057                                            .unwrap();
1058
1059                                            remove_buf.push(id.get());
1060                                        }
1061                                    }
1062                                }
1063                            }
1064                        }
1065                    }
1066
1067                    remove_buf.send_and_clear(&mut *client);
1068
1069                    // Load all entity layers that are newly visible in the old view.
1070                    for &layer in visible_entity_layers
1071                        .0
1072                        .difference(&old_visible_entity_layers.0)
1073                    {
1074                        if let Ok(layer) = entity_layers.get(layer) {
1075                            for pos in old_view.iter() {
1076                                for entity in layer.entities_at(pos) {
1077                                    if self_entity != entity {
1078                                        if let Ok((init, pos)) = entity_init.get(entity) {
1079                                            tx.send(ChannelEvent::LoadEntity(
1080                                                LoadEntityForClientEvent {
1081                                                    client: self_entity,
1082                                                    entity_loaded: entity,
1083                                                },
1084                                            ))
1085                                            .unwrap();
1086
1087                                            init.write_init_packets(pos.get(), &mut *client);
1088                                        }
1089                                    }
1090                                }
1091                            }
1092                        }
1093                    }
1094                }
1095
1096                // Update the client's view (chunk position and view distance)
1097                if old_view != view {
1098                    // Unload chunks and entities in the old view and load chunks and entities in
1099                    // the new view. We don't need to do any work where the old and new view
1100                    // overlap.
1101
1102                    // Unload chunks in the old view.
1103                    if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
1104                        for pos in old_view.diff(view) {
1105                            if let Some(chunk) = layer.chunk(pos) {
1106                                client.write_packet(&UnloadChunkS2c { pos });
1107                                chunk.dec_viewer_count();
1108                            }
1109                        }
1110                    }
1111
1112                    // Load chunks in the new view.
1113                    if let Ok(layer) = chunk_layers.get(chunk_layer.0) {
1114                        for pos in view.diff(old_view) {
1115                            if let Some(chunk) = layer.chunk(pos) {
1116                                chunk.write_init_packets(&mut *client, pos, layer.info());
1117                                chunk.inc_viewer_count();
1118                            }
1119                        }
1120                    }
1121
1122                    // Unload entities from the new visible layers (since we updated it above).
1123                    for &layer in &visible_entity_layers.0 {
1124                        if let Ok(layer) = entity_layers.get(layer) {
1125                            for pos in old_view.diff(view) {
1126                                for entity in layer.entities_at(pos) {
1127                                    if self_entity != entity {
1128                                        if let Ok(id) = entity_ids.get(entity) {
1129                                            tx.send(ChannelEvent::UnloadEntity(
1130                                                UnloadEntityForClientEvent {
1131                                                    client: self_entity,
1132                                                    entity_unloaded: entity,
1133                                                },
1134                                            ))
1135                                            .unwrap();
1136
1137                                            remove_buf.push(id.get());
1138                                        }
1139                                    }
1140                                }
1141                            }
1142                        }
1143                    }
1144
1145                    // Load entities from the new visible layers.
1146                    for &layer in &visible_entity_layers.0 {
1147                        if let Ok(layer) = entity_layers.get(layer) {
1148                            for pos in view.diff(old_view) {
1149                                for entity in layer.entities_at(pos) {
1150                                    if self_entity != entity {
1151                                        if let Ok((init, pos)) = entity_init.get(entity) {
1152                                            tx.send(ChannelEvent::LoadEntity(
1153                                                LoadEntityForClientEvent {
1154                                                    client: self_entity,
1155                                                    entity_loaded: entity,
1156                                                },
1157                                            ))
1158                                            .unwrap();
1159
1160                                            init.write_init_packets(pos.get(), &mut *client);
1161                                        }
1162                                    }
1163                                }
1164                            }
1165                        }
1166                    }
1167                }
1168            }
1169
1170            // Update the old layers.
1171
1172            old_chunk_layer.0 = chunk_layer.0;
1173
1174            if visible_entity_layers.is_changed() {
1175                old_visible_entity_layers
1176                    .0
1177                    .clone_from(&visible_entity_layers.0);
1178            }
1179        },
1180    );
1181
1182    // Send the events.
1183    for event in rx.try_iter() {
1184        match event {
1185            ChannelEvent::UnloadEntity(event) => {
1186                unload_entity_writer.send(event);
1187            }
1188            ChannelEvent::LoadEntity(event) => {
1189                load_entity_writer.send(event);
1190            }
1191        };
1192    }
1193}
1194
1195pub(crate) fn update_game_mode(mut clients: Query<(&mut Client, &GameMode), Changed<GameMode>>) {
1196    for (mut client, game_mode) in &mut clients {
1197        if client.is_added() {
1198            // Game join packet includes the initial game mode.
1199            continue;
1200        }
1201
1202        client.write_packet(&GameStateChangeS2c {
1203            kind: GameEventKind::ChangeGameMode,
1204            value: *game_mode as i32 as f32,
1205        })
1206    }
1207}
1208
1209fn update_food_saturation_health(
1210    mut clients: Query<
1211        (&mut Client, &Food, &Saturation, &Health),
1212        Or<(Changed<Food>, Changed<Saturation>, Changed<Health>)>,
1213    >,
1214) {
1215    for (mut client, food, saturation, health) in &mut clients {
1216        client.write_packet(&HealthUpdateS2c {
1217            health: health.0,
1218            food: VarInt(food.0),
1219            food_saturation: saturation.0,
1220        });
1221    }
1222}
1223
1224fn update_old_view_dist(
1225    mut clients: Query<(&mut OldViewDistance, &ViewDistance), Changed<ViewDistance>>,
1226) {
1227    for (mut old_dist, dist) in &mut clients {
1228        old_dist.0 = dist.0;
1229    }
1230}
1231
1232fn flush_packets(
1233    mut clients: Query<(Entity, &mut Client), Changed<Client>>,
1234    mut commands: Commands,
1235) {
1236    for (entity, mut client) in &mut clients {
1237        if let Err(e) = client.flush_packets() {
1238            warn!("Failed to flush packet queue for client {entity:?}: {e:#}.");
1239            commands.entity(entity).remove::<Client>();
1240        }
1241    }
1242}
1243
1244fn init_tracked_data(mut clients: Query<(&mut Client, &TrackedData), Added<TrackedData>>) {
1245    for (mut client, tracked_data) in &mut clients {
1246        if let Some(init_data) = tracked_data.init_data() {
1247            client.write_packet(&EntityTrackerUpdateS2c {
1248                entity_id: VarInt(0),
1249                tracked_values: init_data.into(),
1250            });
1251        }
1252    }
1253}
1254
1255fn update_tracked_data(mut clients: Query<(&mut Client, &TrackedData)>) {
1256    for (mut client, tracked_data) in &mut clients {
1257        if let Some(update_data) = tracked_data.update_data() {
1258            client.write_packet(&EntityTrackerUpdateS2c {
1259                entity_id: VarInt(0),
1260                tracked_values: update_data.into(),
1261            });
1262        }
1263    }
1264}
1265
1266fn init_tracked_attributes(
1267    mut clients: Query<(&mut Client, &EntityAttributes), Added<EntityAttributes>>,
1268) {
1269    for (mut client, attributes) in &mut clients {
1270        client.write_packet(&EntityAttributesS2c {
1271            entity_id: VarInt(0),
1272            properties: attributes.to_properties(),
1273        });
1274    }
1275}
1276
1277fn update_tracked_attributes(mut clients: Query<(&mut Client, &TrackedEntityAttributes)>) {
1278    for (mut client, attributes) in &mut clients {
1279        let properties = attributes.get_properties();
1280        if !properties.is_empty() {
1281            client.write_packet(&EntityAttributesS2c {
1282                entity_id: VarInt(0),
1283                properties,
1284            });
1285        }
1286    }
1287}
1288
1289/// Decrement viewer count of chunks when the client is despawned.
1290fn cleanup_chunks_after_client_despawn(
1291    mut clients: Query<(View, &VisibleChunkLayer), (With<ClientMarker>, With<Despawned>)>,
1292    chunk_layers: Query<&ChunkLayer>,
1293) {
1294    for (view, layer) in &mut clients {
1295        if let Ok(layer) = chunk_layers.get(layer.0) {
1296            for pos in view.get().iter() {
1297                if let Some(chunk) = layer.chunk(pos) {
1298                    chunk.dec_viewer_count();
1299                }
1300            }
1301        }
1302    }
1303}