Skip to main content

steel_core/entity/entities/objects/technical/
marker.rs

1use crate::entity::damage::DamageSource;
2use crate::entity::{Entity, EntityBase, EntityBaseLoad};
3use crate::world::World;
4use glam::DVec3;
5use std::sync::Weak;
6use steel_macros::entity_behavior;
7use steel_registry::blocks::behavior::PushReaction;
8use steel_registry::entity_type::EntityTypeRef;
9use steel_utils::{DowncastType, DowncastTypeKey};
10use uuid::Uuid;
11
12/// An entity that is minimal. It cannot move, take damage, make sounds, and it doesn't
13/// have any gameplay functions. Markers are not sent to the client, and are primarily intended for use
14/// in map-making and in data packs. This entity is purely server-side.
15#[entity_behavior(class = "Marker")]
16pub struct MarkerEntity {
17    base: EntityBase,
18    entity_type: EntityTypeRef,
19}
20
21// SAFETY: This key is owned by Steel and uniquely identifies `MarkerEntity`.
22unsafe impl DowncastType for MarkerEntity {
23    const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:entity/marker");
24}
25
26impl MarkerEntity {
27    /// Creates a new marker entity.
28    #[must_use]
29    pub fn new(entity_type: EntityTypeRef, id: i32, position: DVec3, world: Weak<World>) -> Self {
30        Self {
31            base: EntityBase::new(id, position, entity_type.dimensions, world),
32            entity_type,
33        }
34    }
35
36    /// Creates a new marker entity with a specific UUID.
37    #[must_use]
38    pub fn with_uuid(
39        entity_type: EntityTypeRef,
40        id: i32,
41        position: DVec3,
42        uuid: Uuid,
43        world: Weak<World>,
44    ) -> Self {
45        Self {
46            base: EntityBase::with_uuid(id, uuid, position, entity_type.dimensions, world),
47            entity_type,
48        }
49    }
50
51    /// Creates a marker entity from saved data.
52    #[must_use]
53    pub fn from_saved(entity_type: EntityTypeRef, load: EntityBaseLoad) -> Self {
54        Self {
55            base: EntityBase::from_load(load, entity_type.dimensions),
56            entity_type,
57        }
58    }
59}
60
61impl Entity for MarkerEntity {
62    fn base(&self) -> &EntityBase {
63        &self.base
64    }
65
66    fn entity_type(&self) -> EntityTypeRef {
67        self.entity_type
68    }
69
70    fn is_ignoring_block_triggers(&self) -> bool {
71        true
72    }
73
74    fn piston_push_reaction(&self) -> PushReaction {
75        PushReaction::Ignore
76    }
77
78    fn can_add_passenger(&self, _passenger: &dyn Entity) -> bool {
79        false
80    }
81
82    fn could_accept_passenger(&self) -> bool {
83        false
84    }
85
86    fn tick(&self) {}
87
88    fn no_physics(&self) -> bool {
89        true
90    }
91
92    fn hurt(&self, _world: &World, _source: &DamageSource, _amount: f32) -> bool {
93        false
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use crate::entity::damage::DamageSource;
100    use crate::entity::entities::{MarkerEntity, PigEntity};
101    use crate::entity::{Entity, SharedEntity, start_riding_entities};
102    use crate::test_support::test_world;
103    use glam::DVec3;
104    use std::sync::Arc;
105    use steel_registry::{vanilla_damage_types, vanilla_entities};
106
107    const TEST_POSITION: DVec3 = DVec3::new(-8.0, 128.0, 4.0);
108
109    #[test]
110    fn markers_cannot_get_hurt() {
111        let world = test_world();
112        let marker = MarkerEntity::new(
113            &vanilla_entities::MARKER,
114            0,
115            TEST_POSITION,
116            Arc::downgrade(world),
117        );
118        assert!(!marker.hurt(
119            world,
120            &DamageSource::environment(&vanilla_damage_types::GENERIC),
121            5.0
122        ));
123    }
124
125    #[test]
126    fn markers_cannot_fall() {
127        let world = test_world();
128        let marker = MarkerEntity::new(
129            &vanilla_entities::MARKER,
130            0,
131            TEST_POSITION,
132            Arc::downgrade(world),
133        );
134        for _ in 0..100 {
135            marker.tick();
136        }
137        assert_eq!(marker.position(), TEST_POSITION);
138    }
139
140    #[test]
141    fn markers_cannot_have_passenger() {
142        let world = test_world();
143        let marker: SharedEntity = Arc::new(MarkerEntity::new(
144            &vanilla_entities::MARKER,
145            0,
146            TEST_POSITION,
147            Arc::downgrade(world),
148        ));
149        let passenger: SharedEntity = Arc::new(PigEntity::new(
150            &vanilla_entities::PIG,
151            1,
152            TEST_POSITION,
153            Arc::downgrade(world),
154        ));
155        assert!(!start_riding_entities(&passenger, &marker));
156    }
157}