veloren_common/terrain/sprite/
magic.rs

1#[macro_export]
2macro_rules! sprites {
3    (
4        $($category_name:ident = $category_disc:literal $(has $($attr:ident),* $(,)?)? {
5            $($sprite_name:ident = $sprite_id:literal),* $(,)?
6        }),* $(,)?
7    ) => {
8        make_case_elim!(
9            category,
10            #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, EnumIter, FromPrimitive)]
11            #[repr(u32)]
12            pub enum Category {
13                $($category_name = $category_disc,)*
14            }
15        );
16
17        impl Category {
18            #[inline] pub const fn all() -> &'static [Self] {
19                &[$(Self::$category_name,)*]
20            }
21
22            #[cfg(test)]
23            #[inline] const fn all_sprites(&self) -> &'static [SpriteKind] {
24                match self {
25                    $(Self::$category_name => &[$(SpriteKind::$sprite_name,)*],)*
26                }
27            }
28
29            // Size, in bits, of the sprite ID
30            #[inline] pub const fn sprite_id_mask(&self) -> u32 {
31                match self {
32                    $(Self::$category_name => ((0u32 $(| $sprite_id)*) + 1).next_power_of_two() - 1,)*
33                }
34            }
35
36            // Size, in bits, of the sprite ID
37            #[inline] pub const fn sprite_id_size(&self) -> u32 { self.sprite_id_mask().count_ones() }
38
39            // The mask that, when applied to the block data, yields the sprite kind
40            #[inline(always)] pub const fn sprite_kind_mask(&self) -> u32 { 0x00FF0000 | self.sprite_id_mask() }
41
42            /// Note that this function assumes that the `BlockKind` of `block` permits sprite inhabitants
43            /// (i.e: is unfilled).
44            #[expect(non_upper_case_globals)]
45            #[inline] pub(super) const fn from_block(block: Block) -> Option<Self> {
46                $(const $category_name: u8 = Category::$category_name as u8;)*
47                match block.sprite_category_byte() {
48                    $($category_name => Some(Self::$category_name),)*
49                    _ => None,
50                }
51            }
52
53            // TODO: It would be nice to use `NonZeroU8` here for the space saving, but `0` is a valid
54            // offset for categories with only one SpriteKind (i.e: the sprite ID is zero-length and so
55            // attributes can go right up to the end of the block data). However, we could decide that an
56            // offset of, say, 0xFF (which would obviously be far out of bounds anyway) represents 'this
57            // attribute has no presence in this category'.
58            #[inline] pub const fn attr_offsets(&self) -> &[Option<u8>; Attributes::all().len()] {
59                match self {
60                    $(Self::$category_name => {
61                        #[allow(unused_mut, unused_variables, unused_assignments)]
62                        const fn gen_attr_offsets() -> [Option<u8>; Attributes::all().len()] {
63                            let mut lut = [None; Attributes::all().len()];
64                            // Don't take up space used by the sprite ID
65                            let mut offset = Category::$category_name.sprite_id_size();
66                            $($({
67                                // Perform basic checks
68                                if offset + $attr::BITS as u32 > 16 {
69                                    panic!("Sprite category has an attribute set that will not fit in the block data");
70                                } else if lut[$attr::INDEX].is_some() {
71                                    panic!("Sprite category cannot have more than one instance of an attribute");
72                                } else if offset > (!0u8) as u32 {
73                                    panic!("Uhhh");
74                                }
75                                lut[$attr::INDEX] = Some(offset as u8);
76                                offset += $attr::BITS as u32;
77                            })*)*
78                            lut
79                        }
80                        const ATTR_OFFSETS: [Option<u8>; Attributes::all().len()] = gen_attr_offsets();
81                        &ATTR_OFFSETS
82                    },)*
83                }
84            }
85
86            /// Returns `true` if this category of sprite has the given attribute.
87            #[inline] pub fn has_attr<A: Attribute>(&self) -> bool {
88                self.attr_offsets()[A::INDEX].is_some()
89            }
90
91            /// Read an attribute from the given block.
92            ///
93            /// Note that this function assumes that the category of `self` matches that of the block, but does
94            /// not validate this.
95            #[inline] pub(super) fn read_attr<A: Attribute>(&self, block: Block) -> Result<A, AttributeError<A::Error>> {
96                let offset = match self.attr_offsets()[A::INDEX] {
97                    Some(offset) => offset,
98                    None => return Err(AttributeError::NotPresent),
99                };
100                let bits = (block.to_be_u32() >> offset as u32) & ((1 << A::BITS as u32) - 1);
101                A::from_bits(bits as u16).map_err(AttributeError::Attribute)
102            }
103
104            /// Write an attribute to the given block.
105            ///
106            /// Note that this function assumes that the category of `self` matches that of the block, but does
107            /// not validate this.
108            #[inline] pub(super) fn write_attr<A: Attribute>(&self, block: &mut Block, attr: A) -> Result<(), AttributeError<core::convert::Infallible>> {
109                let offset = match self.attr_offsets()[A::INDEX] {
110                    Some(offset) => offset,
111                    None => return Err(AttributeError::NotPresent),
112                };
113                let bits = attr.into_bits() as u32;
114                #[cfg(debug_assertions)]
115                assert!(bits < (1 << A::BITS as u32), "The bit representation of the attribute {} must fit within {} bits, but the representation was {:0b}", core::any::type_name::<A>(), A::BITS, bits);
116                let data = ((block.to_be_u32() & (!(((1 << A::BITS as u32) - 1) << offset as u32))) | (bits << offset as u32)).to_be_bytes();
117                *block = block.with_data([data[1], data[2], data[3]]);
118                Ok(())
119            }
120        }
121
122        #[inline] const fn gen_discriminant(category: Category, id: u16) -> u32 {
123            (category as u32) << 16 | id as u32
124        }
125
126        make_case_elim!(
127            sprite_kind,
128            #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, EnumIter, FromPrimitive)]
129            #[repr(u32)]
130            pub enum SpriteKind {
131                $($($sprite_name = $crate::terrain::sprite::gen_discriminant($crate::terrain::sprite::Category::$category_name, $sprite_id),)*)*
132            }
133        );
134
135        #[doc(hidden)]
136        mod categories {
137            use super::*;
138            $(
139                /// Struct used to deserialize attributes for a specific category of sprites.
140                #[doc(hidden)]
141                #[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
142                pub struct $category_name $(($($attr),*))?;
143
144                const _: () = {
145
146                    #[doc(hidden)]
147                    pub struct Visitor<'a, 'de, O, F> {
148                        f: F,
149                        expecting: &'a str,
150                        _marker: std::marker::PhantomData<O>,
151                        _lifetime: std::marker::PhantomData<&'de ()>,
152                    }
153
154                    #[automatically_derived]
155                    impl<'a, 'de, O, F: FnOnce($category_name) -> O> serde::de::Visitor<'de> for Visitor<'a, 'de, O, F> {
156                        type Value = O;
157
158                        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
159                            formatter.write_str(&format!("Variant SpriteKind::{}", self.expecting))
160                        }
161
162                        #[inline]
163                        fn visit_seq<A>(self, mut _seq: A) -> Result<Self::Value, <A as serde::de::SeqAccess<'de>>::Error>
164                        where
165                            A: serde::de::SeqAccess<'de>,
166                        {
167                            let res = $category_name $(($(
168                                serde::de::SeqAccess::next_element::<$attr>(&mut _seq)?.unwrap_or_default()
169                            ),*))?;
170
171                            Ok((self.f)(res))
172                        }
173                    }
174
175                    impl $category_name {
176                        pub const LEN: usize = ${count($attr)};
177
178                        pub fn apply_to_block(self, _block: &mut Block) -> Result<(), AttributeError<core::convert::Infallible>> {
179                            $($(
180                                _block.set_attr(self.${index()} ${ignore($attr)})?;
181                            )*)?
182
183                            Ok(())
184                        }
185
186                        pub fn visitor<'de, O, F: FnOnce($category_name) -> O>(f: F, expecting: &str) -> Visitor<'_, 'de, O, F> {
187                            Visitor {
188                                f,
189                                expecting,
190                                _marker: std::marker::PhantomData,
191                                _lifetime: std::marker::PhantomData,
192                            }
193                        }
194                    }
195                };
196
197            )*
198        }
199        #[doc(hidden)]
200        #[derive(Copy, Clone, Debug, Eq, PartialEq)]
201        #[repr(u32)]
202        enum StructureSpriteKind {
203            $($($sprite_name(categories::$category_name) = SpriteKind::$sprite_name as u32,)*)*
204        }
205
206        impl StructureSpriteKind {
207            /// Assigns this structure sprite to a block.
208            ///
209            /// For this to work `with_sprite` has to return a block that has the passed `SpriteKind`. If it
210            /// returns a block that doesn't have a sprite, that block will be returned. If it returns a block
211            /// with another sprite than what was passed it might apply sprite attributes to that block.
212            fn get_block(self, with_sprite: impl FnOnce(SpriteKind) -> Block) -> Block {
213                match self {
214                    $($(Self::$sprite_name(c) => {
215                        let mut block = with_sprite(SpriteKind::$sprite_name);
216                        // NOTE: We ignore this error because:
217                        // * If we returned the error it would be inconsistent behaviour between sprites that
218                        //   have attributes and ones that don't.
219                        // * We don't want to panic, because some uses of some usages of this function passes
220                        //   `Block::with_sprite` as `with_sprite`, which also doesn't do anything if the block
221                        //   can't have a sprite.
222                        _ = c.apply_to_block(&mut block);
223                        block
224                    },)*)*
225                }
226            }
227        }
228
229        const _: () = {
230            mod __priv {
231                use super::{SpriteKind, StructureSpriteKind, categories};
232                use std::{
233                    fmt::{self, Formatter},
234                    marker::PhantomData,
235                };
236                use serde::{de, Deserialize, Deserializer};
237
238                impl<'de> Deserialize<'de> for StructureSpriteKind {
239                    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
240                    where
241                        D: Deserializer<'de>
242                    {
243                        #[doc(hidden)]
244                        struct Visitor<'de> {
245                            lifetime: PhantomData<&'de ()>,
246                        }
247
248                        impl<'de> de::Visitor<'de> for Visitor<'de> {
249                            type Value = StructureSpriteKind;
250
251                            fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
252                                formatter.write_str("enum SpriteKind")
253                            }
254
255                            fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
256                            where
257                                A: de::EnumAccess<'de>
258                            {
259                                match de::EnumAccess::variant(data)? {
260                                    $($(
261                                        (SpriteKind::$sprite_name, variant) => {
262                                            let visitor = categories::$category_name::visitor(StructureSpriteKind::$sprite_name, stringify!($sprite_name));
263                                            de::VariantAccess::tuple_variant(
264                                                variant,
265                                                categories::$category_name::LEN,
266                                                visitor,
267                                            )
268                                        },
269                                    )*)*
270                                }
271                            }
272                        }
273
274                        #[doc(hidden)]
275                        const VARIANTS: &[&str] = &[
276                            $($(stringify!($sprite_name),)*)*
277                        ];
278
279                        Deserializer::deserialize_enum(
280                            deserializer,
281                            "SpriteKind",
282                            VARIANTS,
283                            Visitor {
284                                lifetime: PhantomData,
285                            },
286                        )
287                    }
288                }
289            }
290        };
291
292        impl SpriteKind {
293            #[inline] pub const fn all() -> &'static [Self] {
294                &[$($(Self::$sprite_name,)*)*]
295            }
296
297            #[inline] pub const fn category(&self) -> Category {
298                match self {
299                    $($(Self::$sprite_name => Category::$category_name,)*)*
300                }
301            }
302
303            /// Note that this function assumes that the category of `self` matches that of the block data, but does
304            /// not validate this.
305            #[expect(non_upper_case_globals)]
306            #[inline] pub(super) const fn from_block(block: Block) -> Option<Self> {
307                match block.sprite_category() {
308                    None => None,
309                    $(Some(category @ Category::$category_name) => {
310                        $(const $sprite_name: u32 = SpriteKind::$sprite_name as u32;)*
311                        match block.to_be_u32() & category.sprite_kind_mask() {
312                            $($sprite_name => Some(Self::$sprite_name),)*
313                            _ => None,
314                        }
315                    },)*
316                }
317            }
318
319            #[inline] pub(super) fn to_initial_bytes(self) -> [u8; 3] {
320                let sprite_bytes = (self as u32).to_be_bytes();
321                let block = Block::from_raw(super::BlockKind::Air, [sprite_bytes[1], sprite_bytes[2], sprite_bytes[3]]);
322                match self.category() {
323                    $(Category::$category_name => block$($(.with_attr($attr::default()).unwrap())*)?,)*
324                }
325                    .data()
326            }
327        }
328    };
329}
330
331#[derive(Debug)]
332pub enum AttributeError<E> {
333    /// The attribute was not present for the given block data's category.
334    NotPresent,
335    /// An attribute-specific error occurred when performing extraction.
336    Attribute(E),
337}
338
339pub trait Attribute: Default + Sized {
340    /// The unique index assigned to this attribute, used to index offset
341    /// arrays.
342    const INDEX: usize;
343    /// The number of bits required to represent this attribute.
344    const BITS: u8;
345    /// The error that might occur when decoding the attribute from bits.
346    type Error: core::fmt::Debug;
347    fn from_bits(bits: u16) -> Result<Self, Self::Error>;
348    fn into_bits(self) -> u16;
349}
350
351#[macro_export]
352macro_rules! attributes {
353    ($(
354        $name:ident { bits: $bits:literal, err: $err:path, from: $from_bits:expr, into: $into_bits:expr $(,)? }
355    ),* $(,)?) => {
356        #[derive(Copy, Clone, Debug)]
357        #[repr(u16)]
358        pub enum Attributes {
359            $($name,)*
360        }
361
362        impl Attributes {
363            #[inline] pub const fn all() -> &'static [Self] {
364                &[$(Self::$name,)*]
365            }
366        }
367
368        $(
369            impl Attribute for $name {
370                const INDEX: usize = Attributes::$name as usize;
371                const BITS: u8 = $bits;
372                type Error = $err;
373                #[inline(always)] fn from_bits(bits: u16) -> Result<Self, Self::Error> { $from_bits(bits) }
374                #[inline(always)] fn into_bits(self) -> u16 { $into_bits(self) }
375            }
376        )*
377    };
378}