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}