valence_scoreboard/
components.rs

1use std::collections::HashMap;
2
3use bevy_ecs::prelude::*;
4use derive_more::{Deref, DerefMut};
5use valence_server::entity::EntityLayerId;
6use valence_server::protocol::packets::play::scoreboard_display_s2c::ScoreboardPosition;
7use valence_server::protocol::packets::play::scoreboard_objective_update_s2c::ObjectiveRenderType;
8use valence_server::text::IntoText;
9use valence_server::Text;
10
11/// A string that identifies an objective. There is one scoreboard per
12/// objective.It's generally not safe to modify this after it's been created.
13/// Limited to 16 characters.
14///
15/// Directly analogous to an Objective's Name.
16#[derive(Debug, Clone, PartialEq, Eq, Hash, Component, Deref)]
17pub struct Objective(pub(crate) String);
18
19impl Objective {
20    pub fn new<N: Into<String>>(name: N) -> Self {
21        let name = name.into();
22        debug_assert!(
23            name.len() <= 16,
24            "Objective name {} is too long ({} > 16)",
25            name,
26            name.len()
27        );
28        Self(name)
29    }
30
31    pub fn name(&self) -> &str {
32        &self.0
33    }
34}
35
36/// Optional display name for an objective. If not present, the objective's name
37/// is used.
38#[derive(Debug, Clone, PartialEq, Component, Deref, DerefMut)]
39pub struct ObjectiveDisplay(pub Text);
40
41/// A mapping of keys to their scores.
42#[derive(Debug, Clone, Component, Default)]
43pub struct ObjectiveScores(pub(crate) HashMap<String, i32>);
44
45impl ObjectiveScores {
46    pub fn new() -> Self {
47        Default::default()
48    }
49
50    pub fn with_map<M: Into<HashMap<String, i32>>>(map: M) -> Self {
51        Self(map.into())
52    }
53
54    pub fn get(&self, key: &str) -> Option<&i32> {
55        self.0.get(key)
56    }
57
58    pub fn get_mut(&mut self, key: &str) -> Option<&mut i32> {
59        self.0.get_mut(key)
60    }
61
62    pub fn insert<K: Into<String>>(&mut self, key: K, value: i32) -> Option<i32> {
63        self.0.insert(key.into(), value)
64    }
65}
66
67#[derive(Debug, Clone, Default, PartialEq, Component)]
68pub struct OldObjectiveScores(pub(crate) HashMap<String, i32>);
69
70impl OldObjectiveScores {
71    pub fn diff<'a>(&'a self, scores: &'a ObjectiveScores) -> Vec<&'a str> {
72        let mut diff = Vec::new();
73
74        for (key, value) in &self.0 {
75            if scores.0.get(key) != Some(value) {
76                diff.push(key.as_str());
77            }
78        }
79
80        let new_keys = scores
81            .0
82            .keys()
83            .filter(|key| !self.0.contains_key(key.as_str()))
84            .map(|key| key.as_str());
85
86        let removed_keys = self
87            .0
88            .keys()
89            .filter(|key| !scores.0.contains_key(key.as_str()))
90            .map(|key| key.as_str());
91
92        diff.extend(new_keys);
93        diff.extend(removed_keys);
94        diff
95    }
96}
97
98#[derive(Bundle)]
99pub struct ObjectiveBundle {
100    pub name: Objective,
101    pub display: ObjectiveDisplay,
102    pub render_type: ObjectiveRenderType,
103    pub scores: ObjectiveScores,
104    pub old_scores: OldObjectiveScores,
105    pub position: ScoreboardPosition,
106    pub layer: EntityLayerId,
107}
108
109impl Default for ObjectiveBundle {
110    fn default() -> Self {
111        Self {
112            name: Objective::new(""),
113            display: ObjectiveDisplay("".into_text()),
114            render_type: Default::default(),
115            scores: Default::default(),
116            old_scores: Default::default(),
117            position: Default::default(),
118            layer: Default::default(),
119        }
120    }
121}