diff --git a/crates/valence_boss_bar/src/components.rs b/crates/valence_boss_bar/src/components.rs index e69de29bb..428bacdc2 100644 --- a/crates/valence_boss_bar/src/components.rs +++ b/crates/valence_boss_bar/src/components.rs @@ -0,0 +1,53 @@ +use std::borrow::Cow; + +use bevy_ecs::prelude::{Bundle, Component}; +use valence_entity::EntityLayerId; +use valence_server::protocol::packets::play::boss_bar_s2c::{ + BossBarAction, BossBarColor, BossBarDivision, BossBarFlags, ToPacketAction, +}; +use valence_server::{Text, UniqueId}; + +/// The bundle of components that make up a boss bar. +#[derive(Bundle, Default)] +pub struct BossBarBundle { + pub id: UniqueId, + pub title: BossBarTitle, + pub health: BossBarHealth, + pub style: BossBarStyle, + pub flags: BossBarFlags, + pub layer: EntityLayerId, +} + +/// The title of a boss bar. +#[derive(Component, Clone, Default)] +pub struct BossBarTitle(pub Text); + +impl ToPacketAction for BossBarTitle { + fn to_packet_action(&self) -> BossBarAction { + BossBarAction::UpdateTitle(Cow::Borrowed(&self.0)) + } +} + +/// The health of a boss bar. +#[derive(Component, Default)] +pub struct BossBarHealth(pub f32); + +impl ToPacketAction for BossBarHealth { + fn to_packet_action(&self) -> BossBarAction { + BossBarAction::UpdateHealth(self.0) + } +} + +/// The style of a boss bar. This includes the color and division of the boss +/// bar. +#[derive(Component, Default)] +pub struct BossBarStyle { + pub color: BossBarColor, + pub division: BossBarDivision, +} + +impl ToPacketAction for BossBarStyle { + fn to_packet_action(&self) -> BossBarAction { + BossBarAction::UpdateStyle(self.color, self.division) + } +} diff --git a/crates/valence_boss_bar/src/lib.rs b/crates/valence_boss_bar/src/lib.rs index 73b8b70d9..c84bd52b3 100644 --- a/crates/valence_boss_bar/src/lib.rs +++ b/crates/valence_boss_bar/src/lib.rs @@ -19,73 +19,24 @@ )] use std::borrow::Cow; -use std::collections::BTreeSet; use bevy_app::prelude::*; use bevy_ecs::prelude::*; -use valence_server::client::{Client, FlushPacketsSet}; +use valence_server::client::{ + Client, OldViewDistance, OldVisibleEntityLayers, ViewDistance, VisibleEntityLayers, +}; +use valence_server::layer::UpdateLayersPreClientSet; +use valence_server::protocol::packets::play::boss_bar_s2c::ToPacketAction; pub use valence_server::protocol::packets::play::boss_bar_s2c::{ BossBarAction, BossBarColor, BossBarDivision, BossBarFlags, }; use valence_server::protocol::packets::play::BossBarS2c; use valence_server::protocol::WritePacket; -use valence_server::{Despawned, Text, UniqueId}; +use valence_server::{ChunkPos, ChunkView, Despawned, EntityLayer, Layer, UniqueId}; -/// The bundle of components that make up a boss bar. -#[derive(Bundle, Debug, Default)] -pub struct BossBarBundle { - pub id: UniqueId, - pub title: BossBarTitle, - pub health: BossBarHealth, - pub style: BossBarStyle, - pub flags: BossBarFlags, - pub viewers: BossBarViewers, -} - -impl BossBarBundle { - pub fn new( - title: Text, - color: BossBarColor, - division: BossBarDivision, - flags: BossBarFlags, - ) -> BossBarBundle { - BossBarBundle { - id: UniqueId::default(), - title: BossBarTitle(title), - health: BossBarHealth(1.0), - style: BossBarStyle { color, division }, - flags, - viewers: BossBarViewers::default(), - } - } -} - -/// The title of a boss bar. -#[derive(Component, Clone, Debug, Default)] -pub struct BossBarTitle(pub Text); - -/// The health of a boss bar. -#[derive(Component, Debug, Default)] -pub struct BossBarHealth(pub f32); - -/// The style of a boss bar. This includes the color and division of the boss -/// bar. -#[derive(Component, Debug, Default)] -pub struct BossBarStyle { - pub color: BossBarColor, - pub division: BossBarDivision, -} - -/// The viewers of a boss bar. -#[derive(Component, Default, Debug)] -pub struct BossBarViewers { - /// The current viewers of the boss bar. It is the list that should be - /// updated. - pub viewers: BTreeSet, - /// The viewers of the last tick in order to determine which viewers have - /// been added and removed. - pub(crate) old_viewers: BTreeSet, -} +mod components; +pub use components::*; +use valence_entity::{EntityLayerId, OldPosition, Position}; pub struct BossBarPlugin; @@ -94,166 +45,221 @@ impl Plugin for BossBarPlugin { app.add_systems( PostUpdate, ( - boss_bar_title_update, - boss_bar_health_update, - boss_bar_style_update, - boss_bar_flags_update, - boss_bar_viewers_update, + update_boss_bar::, + update_boss_bar::, + update_boss_bar::, + update_boss_bar::, + update_boss_bar_layer_view, + update_boss_bar_chunk_view, boss_bar_despawn, - client_disconnection.before(boss_bar_viewers_update), ) - .before(FlushPacketsSet), + .before(UpdateLayersPreClientSet), ); } } -/// System that sends a bossbar update title packet to all viewers of a boss bar -/// that has had its title updated. -fn boss_bar_title_update( - boss_bars: Query<(&UniqueId, &BossBarTitle, &BossBarViewers), Changed>, - mut clients: Query<&mut Client>, +fn update_boss_bar( + boss_bars_query: Query<(&UniqueId, &T, &EntityLayerId, Option<&Position>), Changed>, + mut entity_layers_query: Query<&mut EntityLayer>, ) { - for (id, title, boss_bar_viewers) in boss_bars.iter() { - for viewer in boss_bar_viewers.viewers.iter() { - if let Ok(mut client) = clients.get_mut(*viewer) { - client.write_packet(&BossBarS2c { - id: id.0, - action: BossBarAction::UpdateTitle(Cow::Borrowed(&title.0)), - }); + for (id, part, entity_layer_id, pos) in boss_bars_query.iter() { + if let Ok(mut entity_layer) = entity_layers_query.get_mut(entity_layer_id.0) { + let packet = BossBarS2c { + id: id.0, + action: part.to_packet_action(), + }; + if let Some(pos) = pos { + entity_layer + .view_writer(pos.to_chunk_pos()) + .write_packet(&packet); + } else { + entity_layer.write_packet(&packet); } } } } -/// System that sends a bossbar update health packet to all viewers of a boss -/// bar that has had its health updated. -fn boss_bar_health_update( - boss_bars: Query<(&UniqueId, &BossBarHealth, &BossBarViewers), Changed>, - mut clients: Query<&mut Client>, +fn update_boss_bar_layer_view( + mut clients_query: Query< + ( + &mut Client, + &VisibleEntityLayers, + &OldVisibleEntityLayers, + &Position, + &OldPosition, + &ViewDistance, + &OldViewDistance, + ), + Changed, + >, + boss_bars_query: Query<( + &UniqueId, + &BossBarTitle, + &BossBarHealth, + &BossBarStyle, + &BossBarFlags, + &EntityLayerId, + Option<&Position>, + )>, ) { - for (id, health, boss_bar_viewers) in boss_bars.iter() { - for viewer in boss_bar_viewers.viewers.iter() { - if let Ok(mut client) = clients.get_mut(*viewer) { - client.write_packet(&BossBarS2c { - id: id.0, - action: BossBarAction::UpdateHealth(health.0), - }); - } - } - } -} + for ( + mut client, + visible_entity_layers, + old_visible_entity_layers, + position, + _old_position, + view_distance, + _old_view_distance, + ) in clients_query.iter_mut() + { + let view = ChunkView::new(ChunkPos::from_pos(position.0), view_distance.get()); -/// System that sends a bossbar update style packet to all viewers of a boss bar -/// that has had its style updated. -fn boss_bar_style_update( - boss_bars: Query<(&UniqueId, &BossBarStyle, &BossBarViewers), Changed>, - mut clients: Query<&mut Client>, -) { - for (id, style, boss_bar_viewers) in boss_bars.iter() { - for viewer in boss_bar_viewers.viewers.iter() { - if let Ok(mut client) = clients.get_mut(*viewer) { - client.write_packet(&BossBarS2c { - id: id.0, - action: BossBarAction::UpdateStyle(style.color, style.division), - }); + let old_layers = old_visible_entity_layers.get(); + let current_layers = &visible_entity_layers.0; + + for &added_layer in current_layers.difference(old_layers) { + for (id, title, health, style, flags, _, boss_bar_position) in boss_bars_query + .iter() + .filter(|(_, _, _, _, _, layer_id, _)| layer_id.0 == added_layer) + { + if let Some(position) = boss_bar_position { + if view.contains(position.to_chunk_pos()) { + client.write_packet(&BossBarS2c { + id: id.0, + action: BossBarAction::Add { + title: Cow::Borrowed(&title.0), + health: health.0, + color: style.color, + division: style.division, + flags: *flags, + }, + }); + } + } else { + client.write_packet(&BossBarS2c { + id: id.0, + action: BossBarAction::Add { + title: Cow::Borrowed(&title.0), + health: health.0, + color: style.color, + division: style.division, + flags: *flags, + }, + }); + } } } - } -} -/// System that sends a bossbar update flags packet to all viewers of a boss bar -/// that has had its flags updated. -fn boss_bar_flags_update( - boss_bars: Query<(&UniqueId, &BossBarFlags, &BossBarViewers), Changed>, - mut clients: Query<&mut Client>, -) { - for (id, flags, boss_bar_viewers) in boss_bars.iter() { - for viewer in boss_bar_viewers.viewers.iter() { - if let Ok(mut client) = clients.get_mut(*viewer) { - client.write_packet(&BossBarS2c { - id: id.0, - action: BossBarAction::UpdateFlags(*flags), - }); + for &removed_layer in old_layers.difference(current_layers) { + for (id, _, _, _, _, _, boss_bar_position) in boss_bars_query + .iter() + .filter(|(_, _, _, _, _, layer_id, _)| layer_id.0 == removed_layer) + { + if let Some(position) = boss_bar_position { + if view.contains(position.to_chunk_pos()) { + client.write_packet(&BossBarS2c { + id: id.0, + action: BossBarAction::Remove, + }); + } + } else { + client.write_packet(&BossBarS2c { + id: id.0, + action: BossBarAction::Remove, + }); + } } } } } -/// System that sends a bossbar add/remove packet to all viewers of a boss bar -/// that just have been added/removed. -fn boss_bar_viewers_update( - mut boss_bars: Query< +fn update_boss_bar_chunk_view( + mut clients_query: Query< ( - &UniqueId, - &BossBarTitle, - &BossBarHealth, - &BossBarStyle, - &BossBarFlags, - &mut BossBarViewers, + &mut Client, + &VisibleEntityLayers, + &OldVisibleEntityLayers, + &Position, + &OldPosition, + &ViewDistance, + &OldViewDistance, ), - Changed, + Changed, >, - mut clients: Query<&mut Client>, + boss_bars_query: Query<( + &UniqueId, + &BossBarTitle, + &BossBarHealth, + &BossBarStyle, + &BossBarFlags, + &EntityLayerId, + &Position, + )>, ) { - for (id, title, health, style, flags, mut boss_bar_viewers) in boss_bars.iter_mut() { - let old_viewers = &boss_bar_viewers.old_viewers; - let current_viewers = &boss_bar_viewers.viewers; - - for &added_viewer in current_viewers.difference(old_viewers) { - if let Ok(mut client) = clients.get_mut(added_viewer) { - client.write_packet(&BossBarS2c { - id: id.0, - action: BossBarAction::Add { - title: Cow::Borrowed(&title.0), - health: health.0, - color: style.color, - division: style.division, - flags: *flags, - }, - }); - } - } + for ( + mut client, + visible_entity_layers, + _old_visible_entity_layers, + position, + old_position, + view_distance, + old_view_distance, + ) in clients_query.iter_mut() + { + let view = ChunkView::new(ChunkPos::from_pos(position.0), view_distance.get()); + let old_view = ChunkView::new( + ChunkPos::from_pos(old_position.get()), + old_view_distance.get(), + ); - for &removed_viewer in old_viewers.difference(current_viewers) { - if let Ok(mut client) = clients.get_mut(removed_viewer) { - client.write_packet(&BossBarS2c { - id: id.0, - action: BossBarAction::Remove, - }); + for layer in visible_entity_layers.0.iter() { + for (id, title, health, style, flags, _, boss_bar_position) in boss_bars_query + .iter() + .filter(|(_, _, _, _, _, layer_id, _)| layer_id.0 == *layer) + { + if view.contains(boss_bar_position.to_chunk_pos()) + && !old_view.contains(boss_bar_position.to_chunk_pos()) + { + client.write_packet(&BossBarS2c { + id: id.0, + action: BossBarAction::Add { + title: Cow::Borrowed(&title.0), + health: health.0, + color: style.color, + division: style.division, + flags: *flags, + }, + }); + } else if !view.contains(boss_bar_position.to_chunk_pos()) + && old_view.contains(boss_bar_position.to_chunk_pos()) + { + client.write_packet(&BossBarS2c { + id: id.0, + action: BossBarAction::Remove, + }); + } } } - - boss_bar_viewers.old_viewers = boss_bar_viewers.viewers.clone(); } } -/// System that sends a bossbar remove packet to all viewers of a boss bar that -/// has been despawned. fn boss_bar_despawn( - mut boss_bars: Query<(&UniqueId, &BossBarViewers), Added>, - mut clients: Query<&mut Client>, + boss_bars_query: Query<(&UniqueId, &EntityLayerId, Option<&Position>), With>, + mut entity_layer_query: Query<&mut EntityLayer>, ) { - for (id, viewers) in boss_bars.iter_mut() { - for viewer in viewers.viewers.iter() { - if let Ok(mut client) = clients.get_mut(*viewer) { - client.write_packet(&BossBarS2c { - id: id.0, - action: BossBarAction::Remove, - }); + for (id, entity_layer_id, position) in boss_bars_query.iter() { + if let Ok(mut entity_layer) = entity_layer_query.get_mut(entity_layer_id.0) { + let packet = BossBarS2c { + id: id.0, + action: BossBarAction::Remove, + }; + if let Some(pos) = position { + entity_layer + .view_writer(pos.to_chunk_pos()) + .write_packet(&packet); + } else { + entity_layer.write_packet(&packet); } } } } - -/// System that removes a client from the viewers of its boss bars when it -/// disconnects. -fn client_disconnection( - disconnected_clients: Query, Added)>, - mut boss_bars_viewers: Query<&mut BossBarViewers>, -) { - for entity in disconnected_clients.iter() { - for mut boss_bar_viewers in boss_bars_viewers.iter_mut() { - boss_bar_viewers.viewers.remove(&entity); - } - } -} diff --git a/crates/valence_protocol/src/packets/play/boss_bar_s2c.rs b/crates/valence_protocol/src/packets/play/boss_bar_s2c.rs index 50012e1fe..efc55a502 100644 --- a/crates/valence_protocol/src/packets/play/boss_bar_s2c.rs +++ b/crates/valence_protocol/src/packets/play/boss_bar_s2c.rs @@ -26,7 +26,7 @@ pub enum BossBarAction<'a> { } /// The color of a boss bar. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Component, Default)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Default)] pub enum BossBarColor { #[default] Pink, @@ -39,7 +39,7 @@ pub enum BossBarColor { } /// The division of a boss bar. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Component, Default)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, Default)] pub enum BossBarDivision { #[default] NoDivision, @@ -59,3 +59,14 @@ pub struct BossBarFlags { #[bits(5)] _pad: u8, } + +impl ToPacketAction for BossBarFlags { + fn to_packet_action(&self) -> BossBarAction { + BossBarAction::UpdateFlags(*self) + } +} + +/// Trait for converting a component to a boss bar action. +pub trait ToPacketAction { + fn to_packet_action(&self) -> BossBarAction; +} diff --git a/examples/boss_bar.rs b/examples/boss_bar.rs index 47055ed22..a8aba6968 100644 --- a/examples/boss_bar.rs +++ b/examples/boss_bar.rs @@ -1,15 +1,20 @@ #![allow(clippy::type_complexity)] use rand::seq::SliceRandom; -use valence::boss_bar::{ +use valence::prelude::*; +use valence_boss_bar::{ BossBarBundle, BossBarColor, BossBarDivision, BossBarFlags, BossBarHealth, BossBarStyle, - BossBarTitle, BossBarViewers, + BossBarTitle, }; -use valence::message::{ChatMessageEvent, SendMessage}; -use valence::prelude::*; +use valence_server::entity::cow::CowEntityBundle; +use valence_server::message::ChatMessageEvent; +use valence_text::color::NamedColor; const SPAWN_Y: i32 = 64; +#[derive(Component)] +struct CustomBossBar; + pub fn main() { App::new() .add_plugins(DefaultPlugins) @@ -43,23 +48,37 @@ fn setup( } } - commands.spawn(layer); + let layer_id = commands.spawn(layer).id(); - commands.spawn(BossBarBundle { - title: BossBarTitle("Boss Bar".into_text()), - health: BossBarHealth(1.0), - style: BossBarStyle { - color: BossBarColor::Blue, - division: BossBarDivision::TenNotches, + commands.spawn(( + BossBarBundle { + title: BossBarTitle("Boss Bar".into_text()), + health: BossBarHealth(0.5), + layer: EntityLayerId(layer_id), + ..Default::default() }, - ..Default::default() - }); + CustomBossBar, + )); + + commands.spawn(( + CowEntityBundle { + position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 0.0]), + layer: EntityLayerId(layer_id), + ..Default::default() + }, + BossBarTitle("Louis XVI".color(NamedColor::Red)), + BossBarHealth(0.5), + BossBarStyle { + color: BossBarColor::Red, + division: BossBarDivision::default(), + }, + BossBarFlags::default(), + )); } fn init_clients( - mut clients: Query< + mut clients_query: Query< ( - Entity, &mut Client, &mut EntityLayerId, &mut VisibleChunkLayer, @@ -69,23 +88,19 @@ fn init_clients( ), Added, >, - mut boss_bar_viewers: Query<&mut BossBarViewers>, - layers: Query>, + layers_query: Query, With)>, ) { - let mut boss_bar_viewers = boss_bar_viewers.single_mut(); + let layer = layers_query.single(); for ( - entity, mut client, mut layer_id, mut visible_chunk_layer, mut visible_entity_layers, mut pos, mut game_mode, - ) in &mut clients + ) in &mut clients_query { - let layer = layers.single(); - layer_id.0 = layer; visible_chunk_layer.0 = layer; visible_entity_layers.0.insert(layer); @@ -118,40 +133,43 @@ fn init_clients( client.send_chat_message( "Type any number between 0 and 1 to set the health".on_click_suggest_command("health"), ); - - boss_bar_viewers.viewers.insert(entity); } } fn listen_messages( mut message_events: EventReader, - mut boss_bar: Query<( - &mut BossBarViewers, - &mut BossBarStyle, - &mut BossBarFlags, - &mut BossBarHealth, - &mut BossBarTitle, - )>, + mut boss_bars_query: Query< + ( + &mut BossBarStyle, + &mut BossBarFlags, + &mut BossBarHealth, + &mut BossBarTitle, + &EntityLayerId, + ), + With, + >, + mut clients_query: Query<&mut VisibleEntityLayers, With>, ) { let ( - mut boss_bar_viewers, mut boss_bar_style, mut boss_bar_flags, mut boss_bar_health, mut boss_bar_title, - ) = boss_bar.single_mut(); + entity_layer_id, + ) = boss_bars_query.single_mut(); - let events: Vec = message_events.iter().cloned().collect(); for ChatMessageEvent { client, message, .. - } in events.iter() + } in message_events.iter() { match message.as_ref() { "view" => { - if boss_bar_viewers.viewers.contains(client) { - boss_bar_viewers.viewers.remove(client); - } else { - boss_bar_viewers.viewers.insert(*client); + if let Ok(mut visible_entity_layers) = clients_query.get_mut(*client) { + if visible_entity_layers.0.contains(&entity_layer_id.0) { + visible_entity_layers.0.remove(&entity_layer_id.0); + } else { + visible_entity_layers.0.insert(entity_layer_id.0); + } } } "color" => { @@ -197,7 +215,7 @@ fn listen_messages( boss_bar_health.0 = health; } } else { - boss_bar_title.0 = message.to_string().into(); + boss_bar_title.0 = message.to_string().into_text(); } } }; diff --git a/src/tests/boss_bar.rs b/src/tests/boss_bar.rs index caeaa0bfe..5e5c4097a 100644 --- a/src/tests/boss_bar.rs +++ b/src/tests/boss_bar.rs @@ -1,8 +1,11 @@ use valence_boss_bar::{ BossBarBundle, BossBarColor, BossBarDivision, BossBarFlags, BossBarHealth, BossBarStyle, - BossBarTitle, BossBarViewers, + BossBarTitle, }; +use valence_server::client::VisibleEntityLayers; +use valence_server::entity::EntityLayerId; use valence_server::protocol::packets::play::BossBarS2c; +use valence_server::text::IntoText; use valence_server::Despawned; use crate::testing::ScenarioSingleClient; @@ -10,22 +13,24 @@ use crate::Text; #[test] fn test_intialize_on_join() { - let ScenarioSingleClient { - mut app, - client, - mut helper, - layer, - } = prepare(); + let mut scenario = ScenarioSingleClient::new(); - // Fetch the boss bar component - let mut boss_bar = app.world.get_mut::(layer).unwrap(); - // Add our mock client to the viewers list - assert!(boss_bar.viewers.insert(client)); - - app.update(); - - // Check if a boss bar packet was sent - let frames = helper.collect_received(); + // Insert a boss bar into the world + scenario + .app + .world + .entity_mut(scenario.layer) + .insert(BossBarBundle { + title: BossBarTitle("Boss Bar".into_text()), + health: BossBarHealth(0.5), + layer: EntityLayerId(scenario.layer), + ..Default::default() + }); + + scenario.app.update(); + + // We should receive a boss bar packet with the ADD action + let frames = scenario.helper.collect_received(); frames.assert_count::(1); } @@ -33,45 +38,30 @@ fn test_intialize_on_join() { fn test_despawn() { let ScenarioSingleClient { mut app, - client, mut helper, layer, + .. } = prepare(); - // Fetch the boss bar component - let mut boss_bar = app.world.get_mut::(layer).unwrap(); - // Add our mock client to the viewers list - assert!(boss_bar.viewers.insert(client)); - - app.update(); - // Despawn the boss bar app.world.entity_mut(layer).insert(Despawned); app.update(); - // Check if a boss bar packet was sent in addition to the ADD packet, which - // should be a Remove packet + // We should receive a boss bar packet with the REMOVE action let frames = helper.collect_received(); - frames.assert_count::(2); + frames.assert_count::(1); } #[test] fn test_title_update() { let ScenarioSingleClient { mut app, - client, mut helper, layer, + .. } = prepare(); - // Fetch the boss bar component - let mut boss_bar = app.world.get_mut::(layer).unwrap(); - // Add our mock client to the viewers list - assert!(boss_bar.viewers.insert(client)); - - app.update(); - // Update the title app.world .entity_mut(layer) @@ -79,55 +69,39 @@ fn test_title_update() { app.update(); - // Check if a boss bar packet was sent in addition to the ADD packet, which - // should be an UpdateTitle packet + // We should receive a boss bar packet with the UPDATE_TITLE action let frames = helper.collect_received(); - frames.assert_count::(2); + frames.assert_count::(1); } #[test] fn test_health_update() { let ScenarioSingleClient { mut app, - client, mut helper, layer, + .. } = prepare(); - // Fetch the boss bar component - let mut boss_bar = app.world.get_mut::(layer).unwrap(); - // Add our mock client to the viewers list - assert!(boss_bar.viewers.insert(client)); - - app.update(); - // Update the health app.world.entity_mut(layer).insert(BossBarHealth(0.5)); app.update(); - // Check if a boss bar packet was sent in addition to the ADD packet, which - // should be an UpdateHealth packet + // We should receive a boss bar packet with the UPDATE_HEALTH action let frames = helper.collect_received(); - frames.assert_count::(2); + frames.assert_count::(1); } #[test] fn test_style_update() { let ScenarioSingleClient { mut app, - client, mut helper, layer, + .. } = prepare(); - // Fetch the boss bar component - let mut boss_bar = app.world.get_mut::(layer).unwrap(); - // Add our mock client to the viewers list - assert!(boss_bar.viewers.insert(client)); - - app.update(); - // Update the style app.world.entity_mut(layer).insert(BossBarStyle { color: BossBarColor::Red, @@ -136,28 +110,20 @@ fn test_style_update() { app.update(); - // Check if a boss bar packet was sent in addition to the ADD packet, which - // should be an UpdateStyle packet + // We should receive a boss bar packet with the UPDATE_STYLE action let frames = helper.collect_received(); - frames.assert_count::(2); + frames.assert_count::(1); } #[test] fn test_flags_update() { let ScenarioSingleClient { mut app, - client, mut helper, layer, + .. } = prepare(); - // Fetch the boss bar component - let mut boss_bar = app.world.get_mut::(layer).unwrap(); - // Add our mock client to the viewers list - assert!(boss_bar.viewers.insert(client)); - - app.update(); - // Update the flags let mut new_flags = BossBarFlags::new(); new_flags.set_create_fog(true); @@ -165,44 +131,43 @@ fn test_flags_update() { app.update(); - // Check if a boss bar packet was sent in addition to the ADD packet, which - // should be an UpdateFlags packet + // We should receive a boss bar packet with the UPDATE_FLAGS action let frames = helper.collect_received(); - frames.assert_count::(2); + frames.assert_count::(1); } #[test] -fn test_client_disconnection() { +fn test_client_layer_change() { let ScenarioSingleClient { mut app, - client, mut helper, layer, + client, } = prepare(); - // Fetch the boss bar component - let mut boss_bar = app.world.get_mut::(layer).unwrap(); - // Add our mock client to the viewers list - assert!(boss_bar.viewers.insert(client)); + // Remove the layer from the client + { + let mut visible_entity_layers = app.world.get_mut::(client).unwrap(); + visible_entity_layers.0.clear(); + } app.update(); - // Remove the client from the world - app.world.entity_mut(client).insert(Despawned); + // We should receive a boss bar packet with the REMOVE action + let frames = helper.collect_received(); + frames.assert_count::(1); - app.update(); + // Add the layer back to the client + { + let mut visible_entity_layers = app.world.get_mut::(client).unwrap(); + visible_entity_layers.0.insert(layer); + } - assert!(app - .world - .get_mut::(layer) - .unwrap() - .viewers - .is_empty()); + app.update(); - // Check if a boss bar packet was sent in addition to the ADD packet, which - // should be a Remove packet + // We should receive a boss bar packet with the ADD action let frames = helper.collect_received(); - frames.assert_count::(2); + frames.assert_count::(1); } fn prepare() -> ScenarioSingleClient { @@ -216,12 +181,9 @@ fn prepare() -> ScenarioSingleClient { // Attach the new boss bar to the layer for convenience. s.app.world.entity_mut(s.layer).insert(BossBarBundle { - title: BossBarTitle(Text::text("Test")), - style: BossBarStyle { - color: BossBarColor::Blue, - division: BossBarDivision::SixNotches, - }, - flags: BossBarFlags::new(), + title: BossBarTitle("Boss Bar".into_text()), + health: BossBarHealth(0.5), + layer: EntityLayerId(s.layer), ..Default::default() });