Skip to main content

chunkedge_scoreboard/
components.rs

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