maplibre/tcs/
tiles.rs

1use std::{
2    any,
3    any::TypeId,
4    cell::UnsafeCell,
5    collections::{btree_map, BTreeMap, HashSet},
6};
7
8use downcast_rs::{impl_downcast, Downcast};
9
10use crate::{
11    coords::{Quadkey, WorldTileCoords},
12    io::geometry_index::GeometryIndex,
13};
14
15#[derive(Copy, Clone, Debug)]
16pub struct Tile {
17    pub coords: WorldTileCoords,
18}
19
20/// A component is data associated with an [`Entity`](crate::tcs::entity::Entity). Each entity can have
21/// multiple different types of components, but only one of them per type.
22pub trait TileComponent: Downcast + 'static {}
23impl_downcast!(TileComponent);
24
25#[derive(Default)]
26pub struct Tiles {
27    pub tiles: BTreeMap<Quadkey, Tile>,
28    pub components: BTreeMap<Quadkey, Vec<UnsafeCell<Box<dyn TileComponent>>>>,
29    pub geometry_index: GeometryIndex,
30}
31
32impl Tiles {
33    pub fn query<Q: ComponentQuery>(&self, coords: WorldTileCoords) -> Option<Q::Item<'_>> {
34        let mut global_state = GlobalQueryState::default();
35        let state = <Q::State<'_> as QueryState>::create(&mut global_state);
36        Q::query(self, Tile { coords }, state)
37    }
38
39    pub fn query_mut<Q: ComponentQueryMut>(
40        &mut self,
41        coords: WorldTileCoords,
42    ) -> Option<Q::MutItem<'_>> {
43        let mut global_state = GlobalQueryState::default();
44        let state = <Q::State<'_> as QueryState>::create(&mut global_state);
45        Q::query_mut(self, Tile { coords }, state)
46    }
47
48    pub fn exists(&self, coords: WorldTileCoords) -> bool {
49        if let Some(key) = coords.build_quad_key() {
50            self.tiles.get(&key).is_some()
51        } else {
52            false
53        }
54    }
55
56    pub fn spawn_mut(&mut self, coords: WorldTileCoords) -> Option<TileSpawnResult> {
57        if let Some(key) = coords.build_quad_key() {
58            if let Some(tile) = self.tiles.get(&key) {
59                let tile = *tile;
60                Some(TileSpawnResult { tiles: self, tile })
61            } else {
62                let tile = Tile { coords };
63                self.tiles.insert(key, tile);
64                self.components.insert(key, Vec::new());
65                Some(TileSpawnResult { tiles: self, tile })
66            }
67        } else {
68            None
69        }
70    }
71
72    pub fn clear(&mut self) {
73        self.tiles.clear();
74        self.components.clear();
75    }
76}
77
78pub struct TileSpawnResult<'t> {
79    tiles: &'t mut Tiles,
80    tile: Tile,
81}
82
83impl<'w> TileSpawnResult<'w> {
84    pub fn insert<T: TileComponent>(&mut self, component: T) -> &mut Self {
85        let components = &mut self.tiles.components;
86        let coords = self.tile.coords;
87
88        if let Some(entry) = coords.build_quad_key().map(|key| components.entry(key)) {
89            match entry {
90                btree_map::Entry::Vacant(_entry) => {
91                    panic!("Can not add a component at {coords}. Entity does not exist.",)
92                }
93                btree_map::Entry::Occupied(mut entry) => {
94                    entry.get_mut().push(UnsafeCell::new(Box::new(component)));
95                }
96            }
97        }
98        self
99    }
100}
101
102#[derive(Default)]
103pub struct GlobalQueryState {
104    mutably_borrowed: HashSet<TypeId>,
105}
106
107pub trait QueryState<'s> {
108    fn create(state: &'s mut GlobalQueryState) -> Self;
109    fn clone_to<'a, S: QueryState<'a>>(&'a mut self) -> S;
110}
111
112pub struct EphemeralQueryState<'s> {
113    state: &'s mut GlobalQueryState,
114}
115
116impl<'s> QueryState<'s> for EphemeralQueryState<'s> {
117    fn create(state: &'s mut GlobalQueryState) -> Self {
118        Self { state }
119    }
120
121    fn clone_to<'a, S: QueryState<'a>>(&'a mut self) -> S {
122        S::create(self.state)
123    }
124}
125
126// ComponentQuery
127
128pub trait ComponentQuery {
129    type Item<'t>;
130
131    type State<'s>: QueryState<'s>;
132
133    fn query<'t, 's>(
134        tiles: &'t Tiles,
135        tile: Tile,
136        state: Self::State<'s>,
137    ) -> Option<Self::Item<'t>>;
138}
139
140impl<'a, T: TileComponent> ComponentQuery for &'a T {
141    type Item<'t> = &'t T;
142    type State<'s> = EphemeralQueryState<'s>;
143
144    fn query<'t, 's>(
145        tiles: &'t Tiles,
146        tile: Tile,
147        _state: Self::State<'s>,
148    ) -> Option<Self::Item<'t>> {
149        let components = tiles.components.get(&tile.coords.build_quad_key()?)?;
150
151        components
152            .iter()
153            // FIXME tcs: Is this safe? We cast directly to & instead of &mut
154            .find(|component| unsafe {
155                component.get().as_ref().unwrap().as_ref().type_id() == TypeId::of::<T>()
156            })
157            .map(|component| unsafe {
158                component
159                    .get()
160                    .as_ref()
161                    .unwrap()
162                    .as_ref()
163                    .downcast_ref()
164                    .expect("inserted component has wrong TypeId")
165            })
166    }
167}
168
169// ComponentQueryMut
170
171pub trait ComponentQueryMut {
172    type MutItem<'t>;
173
174    type State<'s>: QueryState<'s>;
175
176    fn query_mut<'t, 's>(
177        tiles: &'t mut Tiles,
178        tile: Tile,
179        state: Self::State<'s>,
180    ) -> Option<Self::MutItem<'t>>;
181}
182
183impl<'a, T: TileComponent> ComponentQueryMut for &'a T {
184    type MutItem<'t> = &'t T;
185    type State<'s> = EphemeralQueryState<'s>;
186
187    fn query_mut<'t, 's>(
188        tiles: &'t mut Tiles,
189        tile: Tile,
190        state: Self::State<'s>,
191    ) -> Option<Self::MutItem<'t>> {
192        <&T as ComponentQuery>::query(tiles, tile, state)
193    }
194}
195
196impl<'a, T: TileComponent> ComponentQueryMut for &'a mut T {
197    type MutItem<'t> = &'t mut T;
198    type State<'s> = EphemeralQueryState<'s>;
199
200    fn query_mut<'t, 's>(
201        tiles: &'t mut Tiles,
202        tile: Tile,
203        _state: Self::State<'s>,
204    ) -> Option<Self::MutItem<'t>> {
205        let components = tiles.components.get_mut(&tile.coords.build_quad_key()?)?;
206
207        components
208            .iter_mut()
209            .find(|component| unsafe {
210                component.get().as_ref().unwrap().as_ref().type_id() == TypeId::of::<T>()
211            })
212            .map(|component| {
213                component
214                    .get_mut()
215                    .as_mut()
216                    .downcast_mut()
217                    .expect("inserted component has wrong TypeId")
218            })
219    }
220}
221
222// ComponentQueryUnsafe
223
224pub trait ComponentQueryUnsafe: ComponentQueryMut {
225    unsafe fn query_unsafe<'t, 's>(
226        tiles: &'t Tiles,
227        tile: Tile,
228        state: Self::State<'s>,
229    ) -> Option<Self::MutItem<'t>>;
230}
231
232impl<'a, T: TileComponent> ComponentQueryUnsafe for &'a T {
233    unsafe fn query_unsafe<'t, 's>(
234        tiles: &'t Tiles,
235        tile: Tile,
236        state: Self::State<'s>,
237    ) -> Option<Self::MutItem<'t>> {
238        <&T as ComponentQuery>::query(tiles, tile, state)
239    }
240}
241
242impl<'a, T: TileComponent> ComponentQueryUnsafe for &'a mut T {
243    /// SAFETY: Safe if tiles is borrowed mutably.
244    // FIXME tcs: check if really safe
245    unsafe fn query_unsafe<'t, 's>(
246        tiles: &'t Tiles,
247        tile: Tile,
248        state: Self::State<'s>,
249    ) -> Option<Self::MutItem<'t>> {
250        let id = TypeId::of::<T>();
251        let borrowed = &mut state.state.mutably_borrowed;
252
253        if borrowed.contains(&id) {
254            panic!(
255                "tried to borrow an {} more than once mutably",
256                any::type_name::<T>()
257            )
258        }
259
260        borrowed.insert(id);
261
262        let components = tiles.components.get(&tile.coords.build_quad_key()?)?;
263
264        components
265            .iter()
266            .find(|component| {
267                component.get().as_ref().unwrap().as_ref().type_id() == TypeId::of::<T>()
268            })
269            .map(|component| {
270                component
271                    .get()
272                    .as_mut()
273                    .unwrap()
274                    .downcast_mut()
275                    .expect("inserted component has wrong TypeId")
276            })
277    }
278}
279
280// Lift to tuples
281
282impl<CQ1: ComponentQuery, CQ2: ComponentQuery> ComponentQuery for (CQ1, CQ2) {
283    type Item<'t> = (CQ1::Item<'t>, CQ2::Item<'t>);
284    type State<'s> = EphemeralQueryState<'s>;
285
286    fn query<'t, 's>(
287        tiles: &'t Tiles,
288        tile: Tile,
289        mut state: Self::State<'s>,
290    ) -> Option<Self::Item<'t>> {
291        Some((
292            CQ1::query(tiles, tile, state.clone_to::<CQ1::State<'_>>())?,
293            CQ2::query(tiles, tile, state.clone_to::<CQ2::State<'_>>())?,
294        ))
295    }
296}
297
298impl<
299        CQ1: ComponentQueryMut + ComponentQueryUnsafe + 'static,
300        CQ2: ComponentQueryMut + ComponentQueryUnsafe + 'static,
301    > ComponentQueryMut for (CQ1, CQ2)
302{
303    type MutItem<'t> = (CQ1::MutItem<'t>, CQ2::MutItem<'t>);
304    type State<'s> = EphemeralQueryState<'s>;
305
306    fn query_mut<'t, 's>(
307        tiles: &'t mut Tiles,
308        tile: Tile,
309        mut state: Self::State<'s>,
310    ) -> Option<Self::MutItem<'t>> {
311        unsafe {
312            Some((
313                <CQ1 as ComponentQueryUnsafe>::query_unsafe(
314                    tiles,
315                    tile,
316                    state.clone_to::<CQ1::State<'_>>(),
317                )?,
318                <CQ2 as ComponentQueryUnsafe>::query_unsafe(
319                    tiles,
320                    tile,
321                    state.clone_to::<CQ2::State<'_>>(),
322                )?,
323            ))
324        }
325    }
326}