Skip to main content

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