valence_registry/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub mod biome;
4pub mod codec;
5pub mod dimension_type;
6pub mod tags;
7
8use std::fmt::Debug;
9use std::hash::Hash;
10use std::marker::PhantomData;
11use std::ops::{Index, IndexMut};
12
13use bevy_app::prelude::*;
14use bevy_ecs::prelude::*;
15pub use biome::BiomeRegistry;
16pub use codec::RegistryCodec;
17pub use dimension_type::DimensionTypeRegistry;
18use indexmap::map::Entry;
19use indexmap::IndexMap;
20pub use tags::TagsRegistry;
21use valence_ident::Ident;
22
23pub struct RegistryPlugin;
24
25/// The [`SystemSet`] where the [`RegistryCodec`] and [`TagsRegistry`] caches
26/// are rebuilt. Systems that modify the registry codec or tags registry should
27/// run _before_ this.
28///
29/// This set lives in [`PostUpdate`].
30#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
31pub struct RegistrySet;
32
33impl Plugin for RegistryPlugin {
34    fn build(&self, app: &mut App) {
35        app.configure_sets(PostUpdate, RegistrySet);
36
37        codec::build(app);
38        tags::build(app);
39    }
40}
41
42#[derive(Clone, Debug)]
43pub struct Registry<I, V> {
44    items: IndexMap<Ident<String>, V>,
45    _marker: PhantomData<I>,
46}
47
48impl<I: RegistryIdx, V> Registry<I, V> {
49    pub fn new() -> Self {
50        Self {
51            items: IndexMap::new(),
52            _marker: PhantomData,
53        }
54    }
55
56    pub fn insert<N: Into<Ident<String>>>(&mut self, name: N, item: V) -> Option<I> {
57        if self.items.len() >= I::MAX {
58            // Too many items in the registry.
59            return None;
60        }
61
62        let len = self.items.len();
63
64        match self.items.entry(name.into()) {
65            Entry::Occupied(_) => None,
66            Entry::Vacant(ve) => {
67                ve.insert(item);
68                Some(I::from_index(len))
69            }
70        }
71    }
72
73    pub fn swap_to_front(&mut self, name: Ident<&str>) {
74        if let Some(idx) = self.items.get_index_of(name.as_str()) {
75            self.items.swap_indices(0, idx);
76        }
77    }
78
79    pub fn remove(&mut self, name: Ident<&str>) -> Option<V> {
80        self.items.shift_remove(name.as_str())
81    }
82
83    pub fn clear(&mut self) {
84        self.items.clear();
85    }
86
87    pub fn get(&self, name: Ident<&str>) -> Option<&V> {
88        self.items.get(name.as_str())
89    }
90
91    pub fn get_mut(&mut self, name: Ident<&str>) -> Option<&mut V> {
92        self.items.get_mut(name.as_str())
93    }
94
95    pub fn index_of(&self, name: Ident<&str>) -> Option<I> {
96        self.items.get_index_of(name.as_str()).map(I::from_index)
97    }
98
99    pub fn iter(
100        &self,
101    ) -> impl DoubleEndedIterator<Item = (I, Ident<&str>, &V)> + ExactSizeIterator + '_ {
102        self.items
103            .iter()
104            .enumerate()
105            .map(|(i, (k, v))| (I::from_index(i), k.as_str_ident(), v))
106    }
107
108    pub fn iter_mut(
109        &mut self,
110    ) -> impl DoubleEndedIterator<Item = (I, Ident<&str>, &mut V)> + ExactSizeIterator + '_ {
111        self.items
112            .iter_mut()
113            .enumerate()
114            .map(|(i, (k, v))| (I::from_index(i), k.as_str_ident(), v))
115    }
116}
117
118impl<I: RegistryIdx, V> Index<I> for Registry<I, V> {
119    type Output = V;
120
121    fn index(&self, index: I) -> &Self::Output {
122        self.items
123            .get_index(index.to_index())
124            .unwrap_or_else(|| panic!("out of bounds registry index of {}", index.to_index()))
125            .1
126    }
127}
128
129impl<I: RegistryIdx, V> IndexMut<I> for Registry<I, V> {
130    fn index_mut(&mut self, index: I) -> &mut Self::Output {
131        self.items
132            .get_index_mut(index.to_index())
133            .unwrap_or_else(|| panic!("out of bounds registry index of {}", index.to_index()))
134            .1
135    }
136}
137
138impl<'a, I: RegistryIdx, V> Index<Ident<&'a str>> for Registry<I, V> {
139    type Output = V;
140
141    fn index(&self, index: Ident<&'a str>) -> &Self::Output {
142        if let Some(item) = self.items.get(index.as_str()) {
143            item
144        } else {
145            panic!("missing registry item with name '{index}'")
146        }
147    }
148}
149
150impl<'a, I: RegistryIdx, V> IndexMut<Ident<&'a str>> for Registry<I, V> {
151    fn index_mut(&mut self, index: Ident<&'a str>) -> &mut Self::Output {
152        if let Some(item) = self.items.get_mut(index.as_str()) {
153            item
154        } else {
155            panic!("missing registry item with name '{index}'")
156        }
157    }
158}
159
160impl<I, V> Default for Registry<I, V> {
161    fn default() -> Self {
162        Self {
163            items: IndexMap::new(),
164            _marker: PhantomData,
165        }
166    }
167}
168
169pub trait RegistryIdx: Copy + Clone + PartialEq + Eq + PartialOrd + Ord + Hash + Debug {
170    const MAX: usize;
171
172    fn to_index(self) -> usize;
173    fn from_index(idx: usize) -> Self;
174}