steel_registry/
attribute.rs1use rustc_hash::FxHashMap;
2use steel_utils::{
3 Identifier,
4 codec::VarInt,
5 serial::{ReadFrom, WriteTo},
6};
7
8use crate::RegistryTags;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17#[repr(u8)]
18#[expect(
19 clippy::enum_variant_names,
20 reason = "matches vanilla `AttributeModifier.Operation` names"
21)]
22pub enum AttributeModifierOperation {
23 AddValue = 0,
24 AddMultipliedBase = 1,
25 AddMultipliedTotal = 2,
26}
27
28impl AttributeModifierOperation {
29 #[must_use]
30 pub const fn from_id(id: i32) -> Option<Self> {
31 match id {
32 0 => Some(Self::AddValue),
33 1 => Some(Self::AddMultipliedBase),
34 2 => Some(Self::AddMultipliedTotal),
35 _ => None,
36 }
37 }
38
39 #[must_use]
40 pub const fn by_name(name: &str) -> Option<Self> {
41 match name {
42 "add_value" => Some(Self::AddValue),
43 "add_multiplied_base" => Some(Self::AddMultipliedBase),
44 "add_multiplied_total" => Some(Self::AddMultipliedTotal),
45 _ => None,
46 }
47 }
48
49 #[must_use]
50 pub const fn name(self) -> &'static str {
51 match self {
52 Self::AddValue => "add_value",
53 Self::AddMultipliedBase => "add_multiplied_base",
54 Self::AddMultipliedTotal => "add_multiplied_total",
55 }
56 }
57}
58
59impl WriteTo for AttributeModifierOperation {
60 fn write(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
61 VarInt(*self as i32).write(writer)
62 }
63}
64
65impl ReadFrom for AttributeModifierOperation {
66 fn read(data: &mut std::io::Cursor<&[u8]>) -> std::io::Result<Self> {
67 let id = VarInt::read(data)?.0;
68 Ok(Self::from_id(id).unwrap_or(Self::AddValue))
69 }
70}
71
72#[derive(Debug)]
77pub struct Attribute {
78 pub key: Identifier,
79 pub translation_key: &'static str,
80 pub default_value: f64,
81 pub min_value: f64,
82 pub max_value: f64,
83 pub syncable: bool,
84}
85
86impl Attribute {
87 #[must_use]
89 pub const fn sanitize_value(&self, value: f64) -> f64 {
90 value.clamp(self.min_value, self.max_value)
91 }
92}
93
94pub type AttributeRef = &'static Attribute;
95
96pub struct AttributeRegistry {
97 attributes_by_id: Vec<AttributeRef>,
98 attributes_by_key: FxHashMap<Identifier, usize>,
99 tags: RegistryTags,
100 allows_registering: bool,
101}
102
103impl Default for AttributeRegistry {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109impl AttributeRegistry {
110 #[must_use]
111 pub fn new() -> Self {
112 Self {
113 attributes_by_id: Vec::new(),
114 attributes_by_key: FxHashMap::default(),
115 tags: RegistryTags::default(),
116 allows_registering: true,
117 }
118 }
119
120 pub fn register(&mut self, attribute: AttributeRef) {
122 assert!(
123 self.allows_registering,
124 "Cannot register attributes after the registry has been frozen"
125 );
126 let idx = self.attributes_by_id.len();
127 self.attributes_by_key.insert(attribute.key.clone(), idx);
128 self.attributes_by_id.push(attribute);
129 }
130
131 #[must_use]
133 pub fn replace(&mut self, attribute: AttributeRef, id: usize) -> bool {
134 if id >= self.attributes_by_id.len() {
135 return false;
136 }
137 self.attributes_by_id[id] = attribute;
138 true
139 }
140
141 pub fn iter(&self) -> impl Iterator<Item = (usize, AttributeRef)> + '_ {
142 self.attributes_by_id
143 .iter()
144 .enumerate()
145 .map(|(id, &attr)| (id, attr))
146 }
147}
148
149crate::impl_registry!(
150 AttributeRegistry,
151 Attribute,
152 attributes_by_id,
153 attributes_by_key,
154 attributes
155);
156crate::impl_tagged_registry!(AttributeRegistry, attributes_by_key, "attribute");