chunkedge_boss_bar/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use bevy_app::prelude::*;
4use bevy_ecs::prelude::*;
5use chunkedge_server::client::{
6    Client, OldViewDistance, OldVisibleEntityLayers, ViewDistance, VisibleEntityLayers,
7};
8use chunkedge_server::layer::UpdateLayersPreClientSet;
9pub use chunkedge_server::protocol::packets::play::boss_event_s2c::{
10    BossBarAction, BossBarColor, BossBarDivision, BossBarFlags,
11};
12use chunkedge_server::protocol::packets::play::BossEventS2c;
13use chunkedge_server::protocol::{IntoTextComponent, WritePacket};
14use chunkedge_server::{ChunkView, Despawned, EntityLayer, Layer, UniqueId};
15
16mod components;
17use chunkedge_entity::{EntityLayerId, OldPosition, Position};
18pub use components::*;
19
20pub struct BossBarPlugin;
21
22impl Plugin for BossBarPlugin {
23    fn build(&self, app: &mut bevy_app::App) {
24        app.add_systems(
25            PostUpdate,
26            (
27                update_boss_bar::<BossBarTitle>,
28                update_boss_bar::<BossBarHealth>,
29                update_boss_bar::<BossBarStyle>,
30                update_boss_bar::<BossBarFlags>,
31                update_boss_bar_layer_view,
32                update_boss_bar_chunk_view,
33                boss_bar_despawn,
34            )
35                .before(UpdateLayersPreClientSet),
36        );
37    }
38}
39
40fn update_boss_bar<T: Component + ToPacketAction>(
41    boss_bars_query: Query<(&UniqueId, &T, &EntityLayerId, Option<&Position>), Changed<T>>,
42    mut entity_layers_query: Query<&mut EntityLayer>,
43) {
44    for (id, part, entity_layer_id, pos) in boss_bars_query.iter() {
45        if let Ok(mut entity_layer) = entity_layers_query.get_mut(entity_layer_id.0) {
46            let packet = BossEventS2c {
47                id: id.0,
48                action: part.to_packet_action(),
49            };
50            if let Some(pos) = pos {
51                entity_layer.view_writer(pos.0).write_packet(&packet);
52            } else {
53                entity_layer.write_packet(&packet);
54            }
55        }
56    }
57}
58
59fn update_boss_bar_layer_view(
60    mut clients_query: Query<
61        (
62            &mut Client,
63            &VisibleEntityLayers,
64            &OldVisibleEntityLayers,
65            &Position,
66            &OldPosition,
67            &ViewDistance,
68            &OldViewDistance,
69        ),
70        Changed<VisibleEntityLayers>,
71    >,
72    boss_bars_query: Query<(
73        &UniqueId,
74        &BossBarTitle,
75        &BossBarHealth,
76        &BossBarStyle,
77        &BossBarFlags,
78        &EntityLayerId,
79        Option<&Position>,
80    )>,
81) {
82    for (
83        mut client,
84        visible_entity_layers,
85        old_visible_entity_layers,
86        position,
87        _old_position,
88        view_distance,
89        _old_view_distance,
90    ) in &mut clients_query
91    {
92        let view = ChunkView::new(position.0.into(), view_distance.get());
93
94        let old_layers = old_visible_entity_layers.get();
95        let current_layers = &visible_entity_layers.0;
96
97        for &added_layer in current_layers.difference(old_layers) {
98            for (id, title, health, style, flags, _, boss_bar_position) in boss_bars_query
99                .iter()
100                .filter(|(_, _, _, _, _, layer_id, _)| layer_id.0 == added_layer)
101            {
102                if let Some(position) = boss_bar_position {
103                    if view.contains(position.0.into()) {
104                        client.write_packet(&BossEventS2c {
105                            id: id.0,
106                            action: BossBarAction::Add {
107                                title: (&title.0).into_cow_text_component(),
108                                health: health.0,
109                                color: style.color,
110                                division: style.division,
111                                flags: *flags,
112                            },
113                        });
114                    }
115                } else {
116                    client.write_packet(&BossEventS2c {
117                        id: id.0,
118                        action: BossBarAction::Add {
119                            title: (&title.0).into_cow_text_component(),
120                            health: health.0,
121                            color: style.color,
122                            division: style.division,
123                            flags: *flags,
124                        },
125                    });
126                }
127            }
128        }
129
130        for &removed_layer in old_layers.difference(current_layers) {
131            for (id, _, _, _, _, _, boss_bar_position) in boss_bars_query
132                .iter()
133                .filter(|(_, _, _, _, _, layer_id, _)| layer_id.0 == removed_layer)
134            {
135                if let Some(position) = boss_bar_position {
136                    if view.contains(position.0.into()) {
137                        client.write_packet(&BossEventS2c {
138                            id: id.0,
139                            action: BossBarAction::Remove,
140                        });
141                    }
142                } else {
143                    client.write_packet(&BossEventS2c {
144                        id: id.0,
145                        action: BossBarAction::Remove,
146                    });
147                }
148            }
149        }
150    }
151}
152
153fn update_boss_bar_chunk_view(
154    mut clients_query: Query<
155        (
156            &mut Client,
157            &VisibleEntityLayers,
158            &OldVisibleEntityLayers,
159            &Position,
160            &OldPosition,
161            &ViewDistance,
162            &OldViewDistance,
163        ),
164        Changed<Position>,
165    >,
166    boss_bars_query: Query<(
167        &UniqueId,
168        &BossBarTitle,
169        &BossBarHealth,
170        &BossBarStyle,
171        &BossBarFlags,
172        &EntityLayerId,
173        &Position,
174    )>,
175) {
176    for (
177        mut client,
178        visible_entity_layers,
179        _old_visible_entity_layers,
180        position,
181        old_position,
182        view_distance,
183        old_view_distance,
184    ) in &mut clients_query
185    {
186        let view = ChunkView::new(position.0.into(), view_distance.get());
187        let old_view = ChunkView::new(old_position.get().into(), old_view_distance.get());
188
189        for layer in &visible_entity_layers.0 {
190            for (id, title, health, style, flags, _, boss_bar_position) in boss_bars_query
191                .iter()
192                .filter(|(_, _, _, _, _, layer_id, _)| layer_id.0 == *layer)
193            {
194                if view.contains(boss_bar_position.0.into())
195                    && !old_view.contains(boss_bar_position.0.into())
196                {
197                    client.write_packet(&BossEventS2c {
198                        id: id.0,
199                        action: BossBarAction::Add {
200                            title: (&title.0).into_cow_text_component(),
201                            health: health.0,
202                            color: style.color,
203                            division: style.division,
204                            flags: *flags,
205                        },
206                    });
207                } else if !view.contains(boss_bar_position.0.into())
208                    && old_view.contains(boss_bar_position.0.into())
209                {
210                    client.write_packet(&BossEventS2c {
211                        id: id.0,
212                        action: BossBarAction::Remove,
213                    });
214                }
215            }
216        }
217    }
218}
219
220fn boss_bar_despawn(
221    boss_bars_query: Query<(&UniqueId, &EntityLayerId, Option<&Position>), With<Despawned>>,
222    mut entity_layer_query: Query<&mut EntityLayer>,
223) {
224    for (id, entity_layer_id, position) in boss_bars_query.iter() {
225        if let Ok(mut entity_layer) = entity_layer_query.get_mut(entity_layer_id.0) {
226            let packet = BossEventS2c {
227                id: id.0,
228                action: BossBarAction::Remove,
229            };
230            if let Some(pos) = position {
231                entity_layer.view_writer(pos.0).write_packet(&packet);
232            } else {
233                entity_layer.write_packet(&packet);
234            }
235        }
236    }
237}