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