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