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#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
58pub struct FlushPacketsSet;
59
60#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
64pub struct SpawnClientsSet;
65
66#[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#[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
212pub struct ClientBundleArgs {
214 pub username: String,
216 pub uuid: Uuid,
218 pub ip: IpAddr,
220 pub properties: Vec<Property>,
222 pub conn: Box<dyn ClientConnection>,
224 pub view_distance: u8,
226 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 pub enc: PacketEncoder,
237}
238
239#[derive(Component, Copy, Clone)]
242pub struct ClientMarker;
243
244#[derive(Component)]
250pub struct Client {
251 conn: Box<dyn ClientConnection>,
252 pub(crate) enc: PacketEncoder,
253}
254
255pub trait ClientConnection: Send + Sync + 'static {
258 fn try_send(&mut self, bytes: BytesMut) -> anyhow::Result<()>;
261 fn try_recv(&mut self) -> anyhow::Result<Option<ReceivedPacket>>;
264 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 pub timestamp: Instant,
278 pub id: i32,
280 pub body: Bytes,
282}
283
284impl Drop for Client {
285 fn drop(&mut self) {
286 _ = self.flush_packets();
287 }
288}
289
290impl 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 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 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 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 #[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 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 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 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#[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 entity.insert(Despawned);
437 }
438 }
439}
440
441#[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 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#[derive(Component, Clone, PartialEq, Eq, Default, Debug, Deref, DerefMut, From, Into)]
482pub struct Properties(pub Vec<Property>);
483
484impl Properties {
485 pub fn textures(&self) -> Option<&Property> {
487 self.0.iter().find(|p| p.name == "textures")
488 }
489
490 pub fn textures_mut(&mut self) -> Option<&mut Property> {
492 self.0.iter_mut().find(|p| p.name == "textures")
493 }
494
495 pub fn skin(&self) -> Option<&str> {
498 self.textures().map(|p| p.value.as_str())
499 }
500
501 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 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#[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#[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#[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#[derive(Component, Default, Debug)]
624pub struct VisibleEntityLayers(pub BTreeSet<Entity>);
625
626#[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
636pub 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 continue;
656 }
657
658 if dist.0 != old_dist.0 {
659 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 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 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 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 debug_assert!(chunk_layer.chunk(pos).is_none());
763 }
764 [.., ChunkLayer::LOAD | ChunkLayer::OVERWRITE] => {
765 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 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 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 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 visible_entity_layers.0.remove(&layer_id);
809 }
810 }
811 }
812
813 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 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 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#[derive(Debug, Clone, PartialEq, Message)]
920pub struct UnloadEntityForClientMessage {
921 pub client: Entity,
923 pub entity_unloaded: Entity,
925}
926
927#[derive(Debug, Clone, PartialEq, Message)]
930pub struct LoadEntityForClientMessage {
931 pub client: Entity,
933 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 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 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 if old_chunk_layer.0 != chunk_layer.0 {
1003 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 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 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 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 if visible_entity_layers.is_changed() {
1075 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 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 if old_view != view {
1132 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 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 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 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 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 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 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
1323fn 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}