1mod fire_freeze;
7mod movement;
8mod persistence;
9mod relationships;
10
11pub use fire_freeze::EntityFireFreezeState;
12pub use movement::{
13 EntityGroundContact, EntityMovement, EntityMovementEmission, EntityMovementFlags,
14 EntityMovementProgress, EntityVerticalMovementStateUpdate,
15};
16pub use persistence::{EntityBaseLoad, EntityBaseSaveData};
17pub use relationships::PendingWorldChangeToken;
18use relationships::{EntityLifecycleState, EntityRelationshipState};
19
20use std::{
21 collections::VecDeque,
22 mem,
23 sync::{Arc, Weak},
24};
25
26use glam::DVec3;
27use simdnbt::owned::NbtCompound;
28use steel_registry::entity_data::EntityPose;
29use steel_registry::entity_type::EntityDimensions;
30use steel_registry::vanilla_entities;
31use steel_utils::locks::SyncMutex;
32use steel_utils::{BlockPos, BlockStateId, WorldAabb};
33use text_components::TextComponent;
34use uuid::Uuid;
35
36use crate::entity::fluid_contact::EntityFluidContact;
37use crate::entity::{
38 EntityLevelCallback, EntityMoveError, InsideBlockEffectType, NullEntityCallback, RemovalReason,
39 SharedEntity,
40};
41use crate::physics::EntityPhysicsState;
42use crate::portal::{PortalKind, PortalProcessResult, PortalProcessor};
43use crate::world::World;
44
45const PISTON_MOVEMENT_LIMIT: f64 = 0.51;
46const PISTON_ZERO_MOVEMENT_EPSILON: f64 = 1.0e-7;
47const PISTON_APPLIED_MOVEMENT_EPSILON: f64 = 1.0e-5;
48const STUCK_SPEED_MULTIPLIER_EPSILON: f64 = 1.0e-7;
49const MOVEMENT_TRACE_LIMIT: usize = 100;
50const MOVEMENT_TRACE_POSITION_EPSILON_SQ: f64 = 9.999_999_4e-11;
51pub const DEFAULT_TICKS_REQUIRED_TO_FREEZE: i32 = 140;
53pub const DEFAULT_MAX_AIR_SUPPLY: i32 = 300;
55pub const MAX_ENTITY_TAGS: usize = 1024;
57const FIRE_IGNITE_TICKS: i32 = 8 * 20;
58const LAVA_IGNITE_TICKS: i32 = 15 * 20;
59
60fn require_finite_position(position: DVec3, field: &str) {
61 assert!(
62 position.is_finite(),
63 "entity {field} must be finite: {position:?}"
64 );
65}
66
67fn normalize_rotation(rotation: (f32, f32)) -> (f32, f32) {
68 assert!(
69 rotation.0.is_finite() && rotation.1.is_finite(),
70 "entity rotation must be finite: {rotation:?}"
71 );
72 (rotation.0 % 360.0, rotation.1.clamp(-90.0, 90.0) % 360.0)
73}
74
75#[derive(Debug, Clone, Copy)]
76pub(crate) struct EntityPhysicsStateInput {
77 pub(crate) max_up_step: f32,
78 pub(crate) backs_off_from_edge: bool,
79 pub(crate) descending: bool,
80 pub(crate) can_walk_on_powder_snow: bool,
81 pub(crate) is_falling_block: bool,
82}
83
84#[derive(Debug, Default)]
85struct EntityMovementTrace {
86 movement_this_tick: VecDeque<EntityMovement>,
87 final_movements_this_tick: Vec<EntityMovement>,
88}
89
90impl EntityMovementTrace {
91 fn record(&mut self, movement: EntityMovement) {
92 if self.movement_this_tick.len() >= MOVEMENT_TRACE_LIMIT {
93 let first = self.movement_this_tick.pop_front();
94 let second = self.movement_this_tick.pop_front();
95 match (first, second) {
96 (Some(first), Some(second)) => self
97 .movement_this_tick
98 .push_front(EntityMovement::new(first.from(), second.to())),
99 (Some(first), None) => self.movement_this_tick.push_front(first),
100 (None, _) => {}
101 }
102 }
103
104 self.movement_this_tick.push_back(movement);
105 }
106
107 fn remove_latest_recording(&mut self) {
108 self.movement_this_tick.pop_back();
109 }
110
111 fn reset(&mut self) {
112 self.movement_this_tick.clear();
113 self.final_movements_this_tick.clear();
114 }
115
116 fn take_for_block_effects(
117 &mut self,
118 old_position: DVec3,
119 position: DVec3,
120 ) -> Vec<EntityMovement> {
121 self.final_movements_this_tick.clear();
122 self.final_movements_this_tick
123 .extend(self.movement_this_tick.drain(..));
124
125 if let Some(last_movement) = self.final_movements_this_tick.last().copied() {
126 if (last_movement.to() - position).length_squared() > MOVEMENT_TRACE_POSITION_EPSILON_SQ
127 {
128 self.final_movements_this_tick
129 .push(EntityMovement::new(last_movement.to(), position));
130 }
131 } else {
132 self.final_movements_this_tick
133 .push(EntityMovement::new(old_position, position));
134 }
135
136 self.final_movements_this_tick.as_slice().to_vec()
137 }
138
139 fn last_for_block_effects(&self) -> Vec<EntityMovement> {
140 self.final_movements_this_tick.as_slice().to_vec()
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq)]
146struct EntityPistonMovement {
147 deltas: [f64; 3],
148 game_time: i64,
149}
150
151impl EntityPistonMovement {
152 const fn new() -> Self {
153 Self {
154 deltas: [0.0; 3],
155 game_time: 0,
156 }
157 }
158
159 fn limit_movement(&mut self, movement: DVec3, current_game_time: i64) -> DVec3 {
160 if movement.length_squared() <= PISTON_ZERO_MOVEMENT_EPSILON {
161 return movement;
162 }
163
164 if current_game_time != self.game_time {
165 self.deltas = [0.0; 3];
166 self.game_time = current_game_time;
167 }
168
169 if movement.x != 0.0 {
170 return self.apply_axis_restriction(0, movement.x, DVec3::X);
171 }
172 if movement.y != 0.0 {
173 return self.apply_axis_restriction(1, movement.y, DVec3::Y);
174 }
175 if movement.z != 0.0 {
176 return self.apply_axis_restriction(2, movement.z, DVec3::Z);
177 }
178
179 DVec3::ZERO
180 }
181
182 fn apply_axis_restriction(&mut self, axis: usize, amount: f64, unit: DVec3) -> DVec3 {
183 let limited =
184 (amount + self.deltas[axis]).clamp(-PISTON_MOVEMENT_LIMIT, PISTON_MOVEMENT_LIMIT);
185 let applied = limited - self.deltas[axis];
186 self.deltas[axis] = limited;
187
188 if applied.abs() <= PISTON_APPLIED_MOVEMENT_EPSILON {
189 DVec3::ZERO
190 } else {
191 unit * applied
192 }
193 }
194}
195
196#[derive(Debug, Clone, Copy, PartialEq)]
198pub struct EntityAmethystStepSound {
199 pub volume: f32,
201 pub pitch: f32,
203}
204
205#[derive(Debug, Clone, Copy, PartialEq)]
212pub struct EntityBaseState {
213 tick_count: i32,
214 position: DVec3,
215 old_position: DVec3,
216 last_known_position: Option<DVec3>,
217 last_known_speed: DVec3,
218 velocity: DVec3,
219 rotation: (f32, f32),
220 old_rotation: (f32, f32),
221 pose: EntityPose,
222 dimensions: EntityDimensions,
223 bounding_box: WorldAabb,
224 movement_flags: EntityMovementFlags,
225 ground_contact: EntityGroundContact,
226 movement_progress: EntityMovementProgress,
227 fire_freeze: EntityFireFreezeState,
228 in_block_state: Option<BlockStateId>,
229 fluid_contact: EntityFluidContact,
230 was_eye_in_water: bool,
231 piston_movement: EntityPistonMovement,
232 fall_distance: f64,
233 stuck_speed_multiplier: DVec3,
234 no_physics: bool,
235 needs_velocity_sync: bool,
236 hurt_marked: bool,
237}
238
239impl EntityBaseState {
240 #[must_use]
242 pub fn new(position: DVec3, dimensions: EntityDimensions) -> Self {
243 require_finite_position(position, "position");
244 Self {
245 tick_count: 0,
246 position,
247 old_position: position,
248 last_known_position: None,
249 last_known_speed: DVec3::ZERO,
250 velocity: DVec3::ZERO,
251 rotation: (0.0, 0.0),
252 old_rotation: (0.0, 0.0),
253 pose: EntityPose::Standing,
254 dimensions,
255 bounding_box: Self::make_bounding_box(position, dimensions),
256 movement_flags: EntityMovementFlags::new(),
257 ground_contact: EntityGroundContact::airborne(),
258 movement_progress: EntityMovementProgress::new(),
259 fire_freeze: EntityFireFreezeState::new(),
260 in_block_state: None,
261 fluid_contact: EntityFluidContact::default(),
262 was_eye_in_water: false,
263 piston_movement: EntityPistonMovement::new(),
264 fall_distance: 0.0,
265 stuck_speed_multiplier: DVec3::ZERO,
266 no_physics: false,
267 needs_velocity_sync: false,
268 hurt_marked: false,
269 }
270 }
271
272 #[must_use]
277 pub fn new_with_bounding_box(
278 position: DVec3,
279 dimensions: EntityDimensions,
280 bounding_box: WorldAabb,
281 ) -> Self {
282 Self {
283 bounding_box,
284 ..Self::new(position, dimensions)
285 }
286 }
287
288 #[must_use]
289 fn make_bounding_box(position: DVec3, dimensions: EntityDimensions) -> WorldAabb {
290 WorldAabb::entity_box(
291 position.x,
292 position.y,
293 position.z,
294 f64::from(dimensions.half_width()),
295 f64::from(dimensions.height),
296 )
297 }
298
299 #[must_use]
301 pub fn with_velocity(mut self, velocity: DVec3) -> Self {
302 if velocity.is_finite() {
303 self.velocity = velocity;
304 }
305 self
306 }
307
308 #[must_use]
310 pub fn with_old_position(mut self, old_position: DVec3) -> Self {
311 require_finite_position(old_position, "old position");
312 self.old_position = old_position;
313 self
314 }
315
316 #[must_use]
318 pub fn with_rotation(mut self, rotation: (f32, f32)) -> Self {
319 let rotation = normalize_rotation(rotation);
320 self.rotation = rotation;
321 self.old_rotation = rotation;
322 self
323 }
324
325 #[must_use]
327 pub const fn with_fall_distance(mut self, fall_distance: f64) -> Self {
328 self.fall_distance = fall_distance;
329 self
330 }
331
332 #[must_use]
334 pub const fn with_fire_freeze_state(mut self, fire_freeze: EntityFireFreezeState) -> Self {
335 self.fire_freeze = fire_freeze;
336 self
337 }
338
339 #[must_use]
341 pub const fn with_on_ground(mut self, on_ground: bool) -> Self {
342 self.movement_flags = self.movement_flags.with_on_ground(on_ground);
343 self.ground_contact = if on_ground {
344 EntityGroundContact::on_ground(None)
345 } else {
346 EntityGroundContact::airborne()
347 };
348 self
349 }
350}
351
352pub struct EntityBase {
378 id: i32,
380 uuid: Uuid,
382 world: SyncMutex<Weak<World>>,
384 state: SyncMutex<EntityBaseState>,
386 save_data: SyncMutex<EntityBaseSaveData>,
388 movement_trace: SyncMutex<EntityMovementTrace>,
390 lifecycle: SyncMutex<EntityLifecycleState>,
392 relationships: SyncMutex<EntityRelationshipState>,
394 portal_process: SyncMutex<Option<PortalProcessor>>,
396 level_callback: SyncMutex<Arc<dyn EntityLevelCallback>>,
398}
399
400impl EntityBase {
401 #[must_use]
403 pub fn new(id: i32, position: DVec3, dimensions: EntityDimensions, world: Weak<World>) -> Self {
404 Self::new_with_state(id, EntityBaseState::new(position, dimensions), world)
405 }
406
407 #[must_use]
409 #[expect(
410 clippy::large_types_passed_by_value,
411 reason = "EntityBaseState is an owned construction snapshot built with with_* helpers"
412 )]
413 pub fn new_with_state(id: i32, state: EntityBaseState, world: Weak<World>) -> Self {
414 Self::with_uuid_and_state(id, Uuid::new_v4(), state, world)
415 }
416
417 #[must_use]
421 pub fn with_uuid(
422 id: i32,
423 uuid: Uuid,
424 position: DVec3,
425 dimensions: EntityDimensions,
426 world: Weak<World>,
427 ) -> Self {
428 Self::with_uuid_and_state(id, uuid, EntityBaseState::new(position, dimensions), world)
429 }
430
431 #[must_use]
436 #[expect(
437 clippy::large_types_passed_by_value,
438 reason = "EntityBaseState is an owned construction snapshot built with with_* helpers"
439 )]
440 pub fn with_uuid_and_state(
441 id: i32,
442 uuid: Uuid,
443 state: EntityBaseState,
444 world: Weak<World>,
445 ) -> Self {
446 Self {
447 id,
448 uuid,
449 world: SyncMutex::new(world),
450 state: SyncMutex::new(state),
451 save_data: SyncMutex::new(EntityBaseSaveData::new()),
452 movement_trace: SyncMutex::new(EntityMovementTrace::default()),
453 lifecycle: SyncMutex::new(EntityLifecycleState::new()),
454 relationships: SyncMutex::new(EntityRelationshipState::default()),
455 portal_process: SyncMutex::new(None),
456 level_callback: SyncMutex::new(Arc::new(NullEntityCallback)),
457 }
458 }
459
460 #[must_use]
462 pub fn from_load(load: EntityBaseLoad, dimensions: EntityDimensions) -> Self {
463 let base = Self::with_uuid_and_state(
464 load.id,
465 load.uuid,
466 EntityBaseState::new(load.position, dimensions)
467 .with_velocity(load.velocity)
468 .with_rotation(load.rotation)
469 .with_fall_distance(load.fall_distance)
470 .with_fire_freeze_state(load.fire_freeze)
471 .with_on_ground(load.on_ground),
472 load.world,
473 );
474 base.replace_save_data(load.save_data);
475 base
476 }
477
478 #[inline]
480 pub const fn id(&self) -> i32 {
481 self.id
482 }
483
484 #[inline]
486 pub const fn uuid(&self) -> Uuid {
487 self.uuid
488 }
489
490 #[inline]
492 pub fn position(&self) -> DVec3 {
493 self.state.lock().position
494 }
495
496 #[inline]
498 pub fn old_position(&self) -> DVec3 {
499 self.state.lock().old_position
500 }
501
502 #[inline]
504 pub fn known_speed(&self) -> DVec3 {
505 self.state.lock().last_known_speed
506 }
507
508 #[inline]
510 pub fn tick_count(&self) -> i32 {
511 self.state.lock().tick_count
512 }
513
514 #[inline]
516 pub fn bounding_box(&self) -> WorldAabb {
517 self.state.lock().bounding_box
518 }
519
520 pub(crate) fn physics_state(&self, input: EntityPhysicsStateInput) -> EntityPhysicsState {
522 let state = self.state.lock();
523 EntityPhysicsState::new(state.position, state.bounding_box, input.max_up_step)
524 .with_on_ground(state.movement_flags.on_ground())
525 .with_backs_off_from_edge(input.backs_off_from_edge)
526 .with_fall_distance(state.fall_distance)
527 .with_descending(input.descending)
528 .with_can_walk_on_powder_snow(input.can_walk_on_powder_snow)
529 .with_falling_block(input.is_falling_block)
530 }
531
532 #[inline]
534 pub fn pose(&self) -> EntityPose {
535 self.state.lock().pose
536 }
537
538 #[inline]
540 pub fn dimensions(&self) -> EntityDimensions {
541 self.state.lock().dimensions
542 }
543
544 #[inline]
546 pub fn velocity(&self) -> DVec3 {
547 self.state.lock().velocity
548 }
549
550 #[inline]
552 pub fn rotation(&self) -> (f32, f32) {
553 self.state.lock().rotation
554 }
555
556 #[inline]
558 pub fn old_rotation(&self) -> (f32, f32) {
559 self.state.lock().old_rotation
560 }
561
562 #[inline]
564 pub fn on_ground(&self) -> bool {
565 self.state.lock().movement_flags.on_ground()
566 }
567
568 #[inline]
570 pub fn movement_flags(&self) -> EntityMovementFlags {
571 self.state.lock().movement_flags
572 }
573
574 #[inline]
576 pub fn ground_contact(&self) -> EntityGroundContact {
577 self.state.lock().ground_contact
578 }
579
580 #[inline]
582 pub fn movement_progress(&self) -> EntityMovementProgress {
583 self.state.lock().movement_progress
584 }
585
586 #[inline]
588 pub fn fire_freeze_state(&self) -> EntityFireFreezeState {
589 self.state.lock().fire_freeze
590 }
591
592 pub fn save_data(&self) -> EntityBaseSaveData {
594 self.save_data.lock().clone()
595 }
596
597 pub fn replace_save_data(&self, save_data: EntityBaseSaveData) {
599 *self.save_data.lock() = save_data;
600 }
601
602 pub fn in_block_state(&self, world: &World) -> BlockStateId {
604 let mut state = self.state.lock();
605 if let Some(in_block_state) = state.in_block_state {
606 return in_block_state;
607 }
608
609 let position = state.position;
610 let block_pos = BlockPos::containing(position.x, position.y, position.z);
611 let in_block_state = world.get_block_state(block_pos);
612 state.in_block_state = Some(in_block_state);
613 in_block_state
614 }
615
616 pub fn set_fire_freeze_state(&self, fire_freeze: EntityFireFreezeState) {
618 self.state.lock().fire_freeze = fire_freeze;
619 }
620
621 #[inline]
623 pub fn horizontal_collision(&self) -> bool {
624 self.state.lock().movement_flags.horizontal_collision()
625 }
626
627 #[inline]
629 pub fn vertical_collision(&self) -> bool {
630 self.state.lock().movement_flags.vertical_collision()
631 }
632
633 #[inline]
635 pub fn vertical_collision_below(&self) -> bool {
636 self.state.lock().movement_flags.vertical_collision_below()
637 }
638
639 pub fn supporting_block(&self) -> Option<BlockPos> {
641 self.state.lock().ground_contact.supporting_block()
642 }
643
644 pub fn on_ground_no_blocks(&self) -> bool {
646 self.state.lock().ground_contact.on_ground_no_blocks()
647 }
648
649 pub fn fluid_contact(&self) -> EntityFluidContact {
651 self.state.lock().fluid_contact
652 }
653
654 pub fn was_eye_in_water(&self) -> bool {
656 self.state.lock().was_eye_in_water
657 }
658
659 #[inline]
661 pub fn fall_distance(&self) -> f64 {
662 self.state.lock().fall_distance
663 }
664
665 #[inline]
667 pub fn no_physics(&self) -> bool {
668 self.state.lock().no_physics
669 }
670
671 #[inline]
673 pub fn air_supply(&self) -> i32 {
674 self.save_data.lock().air_supply
675 }
676
677 #[inline]
679 pub fn portal_cooldown(&self) -> i32 {
680 self.save_data.lock().portal_cooldown
681 }
682
683 #[inline]
685 pub fn is_on_portal_cooldown(&self) -> bool {
686 self.portal_cooldown() > 0
687 }
688
689 #[inline]
691 pub fn portal_process(&self) -> Option<PortalProcessor> {
692 *self.portal_process.lock()
693 }
694
695 #[inline]
697 pub fn no_gravity(&self) -> bool {
698 self.save_data.lock().no_gravity
699 }
700
701 #[inline]
703 pub fn invulnerable(&self) -> bool {
704 self.save_data.lock().invulnerable
705 }
706
707 #[inline]
709 pub fn custom_name(&self) -> Option<TextComponent> {
710 self.save_data.lock().custom_name.clone()
711 }
712
713 #[inline]
715 pub fn custom_name_visible(&self) -> bool {
716 self.save_data.lock().custom_name_visible
717 }
718
719 #[inline]
721 pub fn silent(&self) -> bool {
722 self.save_data.lock().silent
723 }
724
725 #[inline]
727 pub fn glowing(&self) -> bool {
728 self.save_data.lock().glowing
729 }
730
731 pub fn tags(&self) -> Vec<String> {
733 self.save_data.lock().tags.iter().cloned().collect()
734 }
735
736 pub fn custom_data(&self) -> NbtCompound {
738 self.save_data.lock().custom_data.clone()
739 }
740
741 #[inline]
743 pub fn needs_velocity_sync(&self) -> bool {
744 self.state.lock().needs_velocity_sync
745 }
746
747 #[inline]
749 pub fn hurt_marked(&self) -> bool {
750 self.state.lock().hurt_marked
751 }
752
753 #[inline]
757 pub fn level(&self) -> Option<Arc<World>> {
758 self.world.lock().upgrade()
759 }
760
761 pub fn vehicle(&self) -> Option<SharedEntity> {
763 self.relationships.lock().vehicle()
764 }
765
766 pub fn passengers(&self) -> Vec<SharedEntity> {
768 self.relationships.lock().passengers()
769 }
770
771 pub fn first_passenger(&self) -> Option<SharedEntity> {
773 self.relationships.lock().first_passenger()
774 }
775
776 pub fn is_vehicle(&self) -> bool {
778 self.first_passenger().is_some()
779 }
780
781 pub fn has_passenger_id(&self, passenger_id: i32) -> bool {
783 self.relationships.lock().has_passenger_id(passenger_id)
784 }
785
786 pub fn boarding_cooldown(&self) -> i32 {
788 self.relationships.lock().boarding_cooldown
789 }
790
791 pub(crate) fn remove_passenger_id(&self, passenger_id: i32) -> bool {
793 self.relationships.lock().remove_passenger_id(passenger_id)
794 }
795
796 pub fn stop_riding(&self) {
798 self.stop_riding_relationship();
799 }
800
801 pub(crate) fn restore_passenger_relationship(vehicle: &SharedEntity, passenger: &SharedEntity) {
803 passenger.base().stop_riding_relationship();
804 Self::add_passenger_relationship(vehicle, passenger);
805 }
806
807 pub(crate) fn start_riding_relationship(vehicle: &SharedEntity, passenger: &SharedEntity) {
809 passenger.base().stop_riding_relationship();
810 Self::add_passenger_relationship(vehicle, passenger);
811 }
812
813 fn add_passenger_relationship(vehicle: &SharedEntity, passenger: &SharedEntity) {
814 if vehicle.base().has_passenger_id(passenger.id()) {
815 return;
816 }
817
818 passenger.base().relationships.lock().vehicle = Some(Arc::downgrade(vehicle));
819 let passenger_ref = Arc::downgrade(passenger);
820 let mut vehicle_relationships = vehicle.base().relationships.lock();
821 let first_passenger_is_player = vehicle_relationships
822 .first_passenger()
823 .is_some_and(|first| first.entity_type() == &vanilla_entities::PLAYER);
824 if passenger.entity_type() == &vanilla_entities::PLAYER && !first_passenger_is_player {
825 vehicle_relationships.passengers.insert(0, passenger_ref);
826 } else {
827 vehicle_relationships.passengers.push(passenger_ref);
828 }
829 }
830
831 pub(crate) fn set_boarding_cooldown(&self, boarding_cooldown: i32) {
833 self.relationships.lock().boarding_cooldown = boarding_cooldown;
834 }
835
836 pub fn advance_base_tick_state(&self) {
838 self.clear_in_block_state_for_base_tick();
839 self.set_old_rotation_to_current();
840 self.compute_known_speed();
841 self.decrement_boarding_cooldown();
842 }
843
844 fn clear_in_block_state_for_base_tick(&self) {
846 self.state.lock().in_block_state = None;
847 }
848
849 pub fn compute_known_speed(&self) {
851 let mut state = self.state.lock();
852 let previous_position = match state.last_known_position {
853 Some(position) => position,
854 None => state.position,
855 };
856 state.last_known_speed = state.position - previous_position;
857 state.last_known_position = Some(state.position);
858 }
859
860 fn decrement_boarding_cooldown(&self) {
861 let mut relationships = self.relationships.lock();
862 if relationships.boarding_cooldown > 0 {
863 relationships.boarding_cooldown -= 1;
864 }
865 }
866
867 pub fn process_portal_cooldown(&self) {
869 let mut save_data = self.save_data.lock();
870 if save_data.portal_cooldown > 0 {
871 save_data.portal_cooldown -= 1;
872 }
873 }
874
875 pub fn reset_for_player_respawn(&self, dimensions: EntityDimensions) {
877 self.reset_for_player_respawn_inner(dimensions, None);
878 }
879
880 pub(crate) fn reset_for_player_respawn_during_world_change(
882 &self,
883 dimensions: EntityDimensions,
884 pending_token: PendingWorldChangeToken,
885 ) {
886 self.reset_for_player_respawn_inner(dimensions, Some(pending_token));
887 }
888
889 fn reset_for_player_respawn_inner(
890 &self,
891 dimensions: EntityDimensions,
892 pending_world_change: Option<PendingWorldChangeToken>,
893 ) {
894 let bounding_box = {
895 let mut state = self.state.lock();
896 let position = state.position;
897 state.old_position = position;
898 state.last_known_position = None;
899 state.last_known_speed = DVec3::ZERO;
900 state.velocity = DVec3::ZERO;
901 state.old_rotation = state.rotation;
902 state.pose = EntityPose::Standing;
903 state.dimensions = dimensions;
904 state.bounding_box = EntityBaseState::make_bounding_box(position, dimensions);
905 state.movement_flags = EntityMovementFlags::new();
906 state.ground_contact = EntityGroundContact::airborne();
907 state.movement_progress = EntityMovementProgress::new();
908 state.fire_freeze = EntityFireFreezeState::new();
909 state.in_block_state = None;
910 state.fluid_contact = EntityFluidContact::default();
911 state.was_eye_in_water = false;
912 state.piston_movement = EntityPistonMovement::new();
913 state.fall_distance = 0.0;
914 state.stuck_speed_multiplier = DVec3::ZERO;
915 state.no_physics = false;
916 state.needs_velocity_sync = false;
917 state.hurt_marked = false;
918 state.bounding_box
919 };
920 self.notify_bounding_box_changed(bounding_box);
921
922 self.movement_trace.lock().reset();
923 *self.portal_process.lock() = None;
924 self.lifecycle.lock().pending_world_change = pending_world_change;
925
926 let mut save_data = self.save_data.lock();
927 let tags = mem::take(&mut save_data.tags);
928 *save_data = EntityBaseSaveData::new();
929 save_data.tags = tags;
930 }
931
932 pub(crate) fn set_world(&self, world: Weak<World>) {
934 *self.world.lock() = world;
935 }
936
937 pub fn begin_pending_world_change(&self) -> Option<PendingWorldChangeToken> {
939 let mut lifecycle = self.lifecycle.lock();
940 if lifecycle.removal_reason.is_some() || lifecycle.pending_world_change.is_some() {
941 return None;
942 }
943 let token = lifecycle.next_world_change_token();
944 lifecycle.pending_world_change = Some(token);
945 Some(token)
946 }
947
948 pub(crate) fn begin_pending_player_respawn(&self) -> Option<PendingWorldChangeToken> {
953 let mut lifecycle = self.lifecycle.lock();
954 if !matches!(lifecycle.removal_reason, None | Some(RemovalReason::Killed))
955 || lifecycle.pending_world_change.is_some()
956 {
957 return None;
958 }
959 let token = lifecycle.next_world_change_token();
960 lifecycle.pending_world_change = Some(token);
961 Some(token)
962 }
963
964 pub fn finish_pending_world_change(&self, token: PendingWorldChangeToken) -> bool {
966 let mut lifecycle = self.lifecycle.lock();
967 if lifecycle.pending_world_change != Some(token) {
968 return false;
969 }
970 lifecycle.pending_world_change = None;
971 true
972 }
973
974 #[inline]
976 pub fn is_world_change_pending(&self) -> bool {
977 self.lifecycle.lock().pending_world_change.is_some()
978 }
979
980 #[inline]
982 pub fn is_world_change_token_pending(&self, token: PendingWorldChangeToken) -> bool {
983 self.lifecycle.lock().pending_world_change == Some(token)
984 }
985
986 #[inline]
988 pub fn is_removed(&self) -> bool {
989 self.lifecycle.lock().removal_reason.is_some()
990 }
991
992 #[inline]
994 pub fn removal_reason(&self) -> Option<RemovalReason> {
995 self.lifecycle.lock().removal_reason
996 }
997
998 pub fn set_removed(&self, reason: RemovalReason) {
1002 let callback = {
1003 let mut lifecycle = self.lifecycle.lock();
1004 if lifecycle.removal_reason.is_some() {
1005 None
1006 } else {
1007 lifecycle.removal_reason = Some(reason);
1008 lifecycle.pending_world_change = None;
1009 Some(self.level_callback.lock().clone())
1010 }
1011 };
1012
1013 if let Some(callback) = callback {
1014 self.detach_from_relationships(reason);
1015 callback.on_remove(reason);
1016 *self.level_callback.lock() = Arc::new(NullEntityCallback);
1017 }
1018 }
1019
1020 fn detach_from_relationships(&self, reason: RemovalReason) {
1021 if reason.should_destroy() {
1022 self.stop_riding_relationship();
1023 }
1024 self.eject_passenger_relationships();
1025 }
1026
1027 fn stop_riding_relationship(&self) {
1028 let vehicle = {
1029 let mut relationships = self.relationships.lock();
1030 let vehicle = relationships.vehicle();
1031 relationships.vehicle = None;
1032 vehicle
1033 };
1034
1035 if let Some(vehicle) = vehicle {
1036 vehicle.base().remove_passenger_id(self.id);
1037 self.set_boarding_cooldown(60);
1038 }
1039 }
1040
1041 fn eject_passenger_relationships(&self) {
1042 let passengers = {
1043 let mut relationships = self.relationships.lock();
1044 let passengers = relationships.passengers();
1045 relationships.passengers.clear();
1046 passengers
1047 };
1048
1049 for passenger in passengers {
1050 if passenger.base().clear_vehicle_if(self.id) {
1051 passenger.base().set_boarding_cooldown(60);
1052 }
1053 }
1054 }
1055
1056 fn clear_vehicle_if(&self, vehicle_id: i32) -> bool {
1057 {
1058 let mut relationships = self.relationships.lock();
1059 let Some(vehicle) = relationships.vehicle() else {
1060 return false;
1061 };
1062 if vehicle.id() != vehicle_id {
1063 return false;
1064 }
1065 }
1066
1067 if let Err(error) = self.try_set_position(self.position()) {
1068 log::warn!(
1069 "Failed to refresh passenger {} manager position before clearing vehicle {vehicle_id}: {error}",
1070 self.id
1071 );
1072 }
1073
1074 let mut relationships = self.relationships.lock();
1075 let Some(vehicle) = relationships.vehicle() else {
1076 return false;
1077 };
1078 if vehicle.id() != vehicle_id {
1079 return false;
1080 }
1081 relationships.vehicle = None;
1082 true
1083 }
1084
1085 pub fn clear_removed(&self) -> bool {
1091 let mut lifecycle = self.lifecycle.lock();
1092 let was_removed = lifecycle.removal_reason.is_some();
1093 lifecycle.removal_reason = None;
1094 lifecycle.pending_world_change = None;
1095 was_removed
1096 }
1097
1098 pub fn set_level_callback(&self, callback: Arc<dyn EntityLevelCallback>) {
1100 *self.level_callback.lock() = callback;
1101 }
1102
1103 #[must_use = "movement commits can fail when world entity state rejects the update"]
1105 pub fn try_set_position(&self, pos: DVec3) -> Result<(), EntityMoveError> {
1106 require_finite_position(pos, "position");
1107 let old_pos = self.state.lock().position;
1108 let callback = self.level_callback.lock().clone();
1109 callback.validate_move(old_pos, pos)?;
1110 self.set_position_local_unchecked(pos);
1111 if let Err(error) = callback.on_move_committed(old_pos, pos) {
1112 self.set_position_local_unchecked(old_pos);
1113 return Err(error);
1114 }
1115 Ok(())
1116 }
1117
1118 pub(crate) fn set_position_local(&self, pos: DVec3) {
1122 let callback = self.level_callback.lock().clone();
1123 assert!(
1124 callback.allows_local_position_update(),
1125 "entity {} local position update bypassed world entity manager",
1126 self.id
1127 );
1128 self.set_position_local_unchecked(pos);
1129 }
1130
1131 fn set_position_local_unchecked(&self, pos: DVec3) {
1132 require_finite_position(pos, "position");
1133 {
1134 let mut state = self.state.lock();
1135 let old = state.position;
1136 state.position = pos;
1137 state.bounding_box = EntityBaseState::make_bounding_box(pos, state.dimensions);
1138 if BlockPos::containing(old.x, old.y, old.z)
1139 != BlockPos::containing(pos.x, pos.y, pos.z)
1140 {
1141 state.in_block_state = None;
1142 }
1143 }
1144 }
1145
1146 pub fn set_old_position_to_current(&self) {
1148 let mut state = self.state.lock();
1149 state.old_position = state.position;
1150 }
1151
1152 pub fn set_old_position(&self, old_position: DVec3) {
1154 require_finite_position(old_position, "old position");
1155 self.state.lock().old_position = old_position;
1156 }
1157
1158 pub fn set_old_rotation_to_current(&self) {
1160 let mut state = self.state.lock();
1161 state.old_rotation = state.rotation;
1162 }
1163
1164 pub fn set_old_yaw_to_current(&self) {
1166 let mut state = self.state.lock();
1167 state.old_rotation.0 = state.rotation.0;
1168 }
1169
1170 pub fn set_old_rotation(&self, old_rotation: (f32, f32)) {
1172 self.state.lock().old_rotation = normalize_rotation(old_rotation);
1173 }
1174
1175 pub fn record_movement_this_tick(&self, movement: EntityMovement) {
1177 self.movement_trace.lock().record(movement);
1178 }
1179
1180 pub fn remove_latest_movement_recording(&self) {
1182 self.movement_trace.lock().remove_latest_recording();
1183 }
1184
1185 pub fn clear_movement_this_tick(&self) {
1187 self.movement_trace.lock().reset();
1188 }
1189
1190 pub fn take_movements_for_block_effects(&self) -> Vec<EntityMovement> {
1192 let (old_position, position) = {
1193 let state = self.state.lock();
1194 (state.old_position, state.position)
1195 };
1196
1197 self.movement_trace
1198 .lock()
1199 .take_for_block_effects(old_position, position)
1200 }
1201
1202 pub fn last_movements_for_block_effects(&self) -> Vec<EntityMovement> {
1204 self.movement_trace.lock().last_for_block_effects()
1205 }
1206
1207 pub fn set_bounding_box(&self, bounding_box: WorldAabb) {
1212 self.state.lock().bounding_box = bounding_box;
1213 self.notify_bounding_box_changed(bounding_box);
1214 }
1215
1216 pub fn set_pose_and_dimensions(&self, pose: EntityPose, dimensions: EntityDimensions) {
1218 let bounding_box = {
1219 let mut state = self.state.lock();
1220 state.pose = pose;
1221 state.dimensions = dimensions;
1222 state.bounding_box = EntityBaseState::make_bounding_box(state.position, dimensions);
1223 state.bounding_box
1224 };
1225 self.notify_bounding_box_changed(bounding_box);
1226 }
1227
1228 fn notify_bounding_box_changed(&self, bounding_box: WorldAabb) {
1229 let callback = Arc::clone(&self.level_callback.lock());
1230 callback.on_bounding_box_changed(bounding_box);
1231 }
1232
1233 pub fn set_velocity(&self, velocity: DVec3) {
1235 if velocity.is_finite() {
1236 self.state.lock().velocity = velocity;
1237 }
1238 }
1239
1240 #[inline]
1242 pub fn advance_tick_count(&self) {
1243 let mut state = self.state.lock();
1244 state.tick_count = state.tick_count.wrapping_add(1);
1245 }
1246
1247 pub fn record_movement_progress(
1249 &self,
1250 clipped_movement: DVec3,
1251 climbing: bool,
1252 ) -> EntityMovementProgress {
1253 let mut state = self.state.lock();
1254 state
1255 .movement_progress
1256 .add_movement(clipped_movement, climbing);
1257 state.movement_progress
1258 }
1259
1260 pub fn set_next_step(&self, next_step: f32) {
1262 self.state.lock().movement_progress.next_step = next_step;
1263 }
1264
1265 pub fn amethyst_step_sound(&self, tick_count: i32) -> Option<EntityAmethystStepSound> {
1267 let intensity = {
1268 let mut state = self.state.lock();
1269 let progress = &mut state.movement_progress;
1270 if tick_count < progress.last_crystal_sound_play_tick + 20 {
1271 return None;
1272 }
1273
1274 let tick_delta = tick_count - progress.last_crystal_sound_play_tick;
1275 progress.crystal_sound_intensity *= 0.997_f32.powi(tick_delta);
1276 progress.crystal_sound_intensity = (progress.crystal_sound_intensity + 0.07).min(1.0);
1277 progress.last_crystal_sound_play_tick = tick_count;
1278 progress.crystal_sound_intensity
1279 };
1280
1281 let pitch = 0.5 + intensity * rand::random::<f32>() * 1.2;
1282 let volume = 0.1 + intensity * 1.2;
1283 Some(EntityAmethystStepSound { volume, pitch })
1284 }
1285
1286 pub fn set_rotation(&self, rotation: (f32, f32)) {
1288 self.state.lock().rotation = normalize_rotation(rotation);
1289 }
1290
1291 pub fn set_no_physics(&self, no_physics: bool) {
1293 self.state.lock().no_physics = no_physics;
1294 }
1295
1296 pub fn set_air_supply(&self, air_supply: i32) {
1298 self.save_data.lock().air_supply = air_supply;
1299 }
1300
1301 pub fn set_portal_cooldown(&self, portal_cooldown: i32) {
1303 self.save_data.lock().portal_cooldown = portal_cooldown;
1304 }
1305
1306 pub fn set_as_inside_portal(&self, portal: PortalKind, entry_position: BlockPos) {
1308 let mut portal_process = self.portal_process.lock();
1309 match portal_process.as_mut() {
1310 Some(process) if process.is_same_portal(portal) => {
1311 process.set_as_inside_portal(entry_position);
1312 }
1313 _ => {
1314 *portal_process = Some(PortalProcessor::new(portal, entry_position));
1315 }
1316 }
1317 }
1318
1319 pub fn process_portal_teleportation(
1321 &self,
1322 allowed_to_teleport: bool,
1323 transition_time: i32,
1324 ) -> Option<PortalProcessResult> {
1325 self.portal_process.lock().as_mut().map(|process| {
1326 process.process_portal_teleportation(allowed_to_teleport, transition_time)
1327 })
1328 }
1329
1330 pub fn clear_portal_process(&self) {
1332 *self.portal_process.lock() = None;
1333 }
1334
1335 pub fn set_no_gravity(&self, no_gravity: bool) {
1337 self.save_data.lock().no_gravity = no_gravity;
1338 }
1339
1340 pub fn set_invulnerable(&self, invulnerable: bool) {
1342 self.save_data.lock().invulnerable = invulnerable;
1343 }
1344
1345 pub fn set_custom_name(&self, custom_name: Option<TextComponent>) {
1347 self.save_data.lock().custom_name = custom_name;
1348 }
1349
1350 pub fn set_custom_name_visible(&self, visible: bool) {
1352 self.save_data.lock().custom_name_visible = visible;
1353 }
1354
1355 pub fn set_silent(&self, silent: bool) {
1357 self.save_data.lock().silent = silent;
1358 }
1359
1360 pub fn set_glowing(&self, glowing: bool) {
1362 self.save_data.lock().glowing = glowing;
1363 }
1364
1365 pub fn add_tag(&self, tag: String) -> bool {
1367 self.save_data.lock().add_tag(tag)
1368 }
1369
1370 pub fn remove_tag(&self, tag: &str) -> bool {
1372 self.save_data.lock().tags.remove(tag)
1373 }
1374
1375 pub fn set_custom_data(&self, custom_data: NbtCompound) {
1377 self.save_data.lock().custom_data = custom_data;
1378 }
1379
1380 pub fn mark_velocity_sync(&self) {
1382 self.state.lock().needs_velocity_sync = true;
1383 }
1384
1385 pub fn clear_velocity_sync(&self) {
1387 self.state.lock().needs_velocity_sync = false;
1388 }
1389
1390 pub fn mark_hurt(&self) {
1392 self.state.lock().hurt_marked = true;
1393 }
1394
1395 pub fn clear_hurt_mark(&self) {
1397 self.state.lock().hurt_marked = false;
1398 }
1399
1400 pub fn set_fall_distance(&self, fall_distance: f64) {
1402 self.state.lock().fall_distance = fall_distance;
1403 }
1404
1405 pub fn accumulate_fall_distance(&self, vertical_movement: f64) {
1407 self.state.lock().fall_distance -= f64::from(vertical_movement as f32);
1408 }
1409
1410 pub fn reset_fall_distance(&self) {
1412 self.set_fall_distance(0.0);
1413 }
1414
1415 pub fn remaining_fire_ticks(&self) -> i32 {
1417 self.state.lock().fire_freeze.remaining_fire_ticks()
1418 }
1419
1420 pub fn set_remaining_fire_ticks(&self, remaining_fire_ticks: i32) {
1422 self.state.lock().fire_freeze.remaining_fire_ticks = remaining_fire_ticks;
1423 }
1424
1425 pub fn ticks_frozen(&self) -> i32 {
1427 self.state.lock().fire_freeze.ticks_frozen()
1428 }
1429
1430 pub fn set_ticks_frozen(&self, ticks_frozen: i32) {
1432 self.state.lock().fire_freeze.ticks_frozen = ticks_frozen;
1433 }
1434
1435 pub fn is_in_powder_snow(&self) -> bool {
1437 self.state.lock().fire_freeze.is_in_powder_snow()
1438 }
1439
1440 pub fn was_in_powder_snow(&self) -> bool {
1442 self.state.lock().fire_freeze.was_in_powder_snow()
1443 }
1444
1445 pub fn set_visual_fire(&self, has_visual_fire: bool) {
1447 self.state.lock().fire_freeze.has_visual_fire = has_visual_fire;
1448 }
1449
1450 pub fn has_visual_fire(&self) -> bool {
1452 self.state.lock().fire_freeze.has_visual_fire()
1453 }
1454
1455 pub fn is_on_fire(&self, fire_immune: bool) -> bool {
1457 !fire_immune && self.remaining_fire_ticks() > 0
1458 }
1459
1460 pub fn is_freezing(&self) -> bool {
1462 self.state.lock().fire_freeze.is_freezing()
1463 }
1464
1465 pub fn is_fully_frozen(&self, ticks_required_to_freeze: i32) -> bool {
1467 self.state
1468 .lock()
1469 .fire_freeze
1470 .is_fully_frozen(ticks_required_to_freeze)
1471 }
1472
1473 pub fn advance_powder_snow_contact_for_base_tick(&self) {
1475 let mut state = self.state.lock();
1476 state.fire_freeze.was_in_powder_snow = state.fire_freeze.is_in_powder_snow;
1477 state.fire_freeze.is_in_powder_snow = false;
1478 }
1479
1480 pub fn advance_fire_tick(&self, fire_immune: bool, in_lava: bool) -> bool {
1484 let mut state = self.state.lock();
1485 if state.fire_freeze.remaining_fire_ticks <= 0 {
1486 return false;
1487 }
1488
1489 if fire_immune {
1490 state.fire_freeze.remaining_fire_ticks = state.fire_freeze.remaining_fire_ticks.min(0);
1491 return false;
1492 }
1493
1494 let should_damage = state.fire_freeze.remaining_fire_ticks % 20 == 0 && !in_lava;
1495 state.fire_freeze.remaining_fire_ticks -= 1;
1496 should_damage
1497 }
1498
1499 pub fn clear_freeze(&self) {
1501 self.set_ticks_frozen(0);
1502 }
1503
1504 pub fn clear_fire(&self) {
1506 let mut state = self.state.lock();
1507 state.fire_freeze.remaining_fire_ticks = state.fire_freeze.remaining_fire_ticks.min(0);
1508 }
1509
1510 pub fn ignite_for_ticks(&self, number_of_ticks: i32, remaining_fire_ticks_cap: Option<i32>) {
1512 let mut state = self.state.lock();
1513 Self::ignite_for_ticks_in_state(
1514 &mut state.fire_freeze,
1515 number_of_ticks,
1516 remaining_fire_ticks_cap,
1517 );
1518 }
1519
1520 pub fn apply_inside_block_effect(
1522 &self,
1523 effect_type: InsideBlockEffectType,
1524 can_freeze: bool,
1525 fire_immune: bool,
1526 fire_ignite_extra_ticks: i32,
1527 ticks_required_to_freeze: i32,
1528 remaining_fire_ticks_cap: Option<i32>,
1529 ) {
1530 let mut state = self.state.lock();
1531 match effect_type {
1532 InsideBlockEffectType::Freeze => {
1533 state.fire_freeze.is_in_powder_snow = true;
1534 if can_freeze {
1535 state.fire_freeze.ticks_frozen =
1536 ticks_required_to_freeze.min(state.fire_freeze.ticks_frozen + 1);
1537 }
1538 }
1539 InsideBlockEffectType::ClearFreeze => {
1540 state.fire_freeze.ticks_frozen = 0;
1541 }
1542 InsideBlockEffectType::FireIgnite => {
1543 Self::apply_fire_ignite(
1544 &mut state.fire_freeze,
1545 fire_immune,
1546 fire_ignite_extra_ticks,
1547 remaining_fire_ticks_cap,
1548 );
1549 }
1550 InsideBlockEffectType::LavaIgnite => {
1551 if !fire_immune {
1552 Self::ignite_for_ticks_in_state(
1553 &mut state.fire_freeze,
1554 LAVA_IGNITE_TICKS,
1555 remaining_fire_ticks_cap,
1556 );
1557 }
1558 }
1559 InsideBlockEffectType::Extinguish => {
1560 state.fire_freeze.remaining_fire_ticks =
1561 state.fire_freeze.remaining_fire_ticks.min(0);
1562 }
1563 }
1564 }
1565
1566 fn apply_fire_ignite(
1567 fire_freeze: &mut EntityFireFreezeState,
1568 fire_immune: bool,
1569 fire_ignite_extra_ticks: i32,
1570 remaining_fire_ticks_cap: Option<i32>,
1571 ) {
1572 if fire_immune {
1573 return;
1574 }
1575
1576 if fire_freeze.remaining_fire_ticks < 0 {
1577 Self::set_remaining_fire_ticks_in_state(
1578 fire_freeze,
1579 fire_freeze.remaining_fire_ticks + 1,
1580 remaining_fire_ticks_cap,
1581 );
1582 } else if fire_ignite_extra_ticks > 0 {
1583 Self::set_remaining_fire_ticks_in_state(
1584 fire_freeze,
1585 fire_freeze.remaining_fire_ticks + fire_ignite_extra_ticks,
1586 remaining_fire_ticks_cap,
1587 );
1588 }
1589
1590 if fire_freeze.remaining_fire_ticks >= 0 {
1591 Self::ignite_for_ticks_in_state(
1592 fire_freeze,
1593 FIRE_IGNITE_TICKS,
1594 remaining_fire_ticks_cap,
1595 );
1596 }
1597 }
1598
1599 fn ignite_for_ticks_in_state(
1600 fire_freeze: &mut EntityFireFreezeState,
1601 number_of_ticks: i32,
1602 remaining_fire_ticks_cap: Option<i32>,
1603 ) {
1604 if fire_freeze.remaining_fire_ticks < number_of_ticks {
1605 Self::set_remaining_fire_ticks_in_state(
1606 fire_freeze,
1607 number_of_ticks,
1608 remaining_fire_ticks_cap,
1609 );
1610 }
1611 fire_freeze.ticks_frozen = 0;
1612 }
1613
1614 fn set_remaining_fire_ticks_in_state(
1615 fire_freeze: &mut EntityFireFreezeState,
1616 remaining_fire_ticks: i32,
1617 remaining_fire_ticks_cap: Option<i32>,
1618 ) {
1619 fire_freeze.remaining_fire_ticks =
1620 Self::cap_remaining_fire_ticks(remaining_fire_ticks, remaining_fire_ticks_cap);
1621 }
1622
1623 fn cap_remaining_fire_ticks(
1624 remaining_fire_ticks: i32,
1625 remaining_fire_ticks_cap: Option<i32>,
1626 ) -> i32 {
1627 remaining_fire_ticks_cap.map_or(remaining_fire_ticks, |cap| remaining_fire_ticks.min(cap))
1628 }
1629
1630 pub fn dampen_fall_distance_in_lava(&self) {
1632 let mut state = self.state.lock();
1633 if state.fluid_contact.lava_height() > 0.0 {
1634 state.fall_distance *= 0.5;
1635 }
1636 }
1637
1638 pub fn reset_fall_distance_in_water(&self) {
1640 let mut state = self.state.lock();
1641 if state.fluid_contact.water_height() > 0.0 {
1642 state.fall_distance = 0.0;
1643 }
1644 }
1645
1646 pub fn set_on_ground(&self, on_ground: bool) {
1648 let mut state = self.state.lock();
1649 state.movement_flags = state.movement_flags.with_on_ground(on_ground);
1650 if !on_ground {
1651 state.ground_contact = EntityGroundContact::airborne();
1652 }
1653 }
1654
1655 pub fn set_movement_flags(
1657 &self,
1658 movement_flags: EntityMovementFlags,
1659 ground_contact: EntityGroundContact,
1660 ) {
1661 let mut state = self.state.lock();
1662 state.movement_flags = movement_flags;
1663 state.ground_contact = ground_contact;
1664 }
1665
1666 pub fn set_ground_contact(&self, ground_contact: EntityGroundContact) {
1668 self.state.lock().ground_contact = ground_contact;
1669 }
1670
1671 pub fn set_fluid_contact(&self, fluid_contact: EntityFluidContact) {
1673 self.state.lock().fluid_contact = fluid_contact;
1674 }
1675
1676 pub fn set_fluid_contact_for_base_tick(&self, fluid_contact: EntityFluidContact) {
1681 let mut state = self.state.lock();
1682 state.was_eye_in_water = state.fluid_contact.eye_in_water();
1683 state.fluid_contact = fluid_contact;
1684 }
1685
1686 pub fn set_on_ground_with_movement(
1688 &self,
1689 on_ground: bool,
1690 horizontal_collision: bool,
1691 ground_contact: EntityGroundContact,
1692 ) {
1693 let mut state = self.state.lock();
1694 state.movement_flags = state
1695 .movement_flags
1696 .with_on_ground(on_ground)
1697 .with_horizontal_collision(horizontal_collision);
1698 state.ground_contact = ground_contact;
1699 }
1700
1701 pub fn clear_collision_flags(&self) {
1703 let mut state = self.state.lock();
1704 state.movement_flags = state.movement_flags.without_collisions();
1705 }
1706
1707 pub fn limit_piston_movement(&self, movement: DVec3, current_game_time: i64) -> DVec3 {
1709 self.state
1710 .lock()
1711 .piston_movement
1712 .limit_movement(movement, current_game_time)
1713 }
1714
1715 pub fn make_stuck_in_block(&self, speed_multiplier: DVec3) {
1717 let mut state = self.state.lock();
1718 state.fall_distance = 0.0;
1719 state.stuck_speed_multiplier = speed_multiplier;
1720 }
1721
1722 #[must_use]
1724 pub fn consume_stuck_speed_multiplier(&self, movement: DVec3, apply_multiplier: bool) -> DVec3 {
1725 let mut state = self.state.lock();
1726 if state.stuck_speed_multiplier.length_squared() <= STUCK_SPEED_MULTIPLIER_EPSILON {
1727 return movement;
1728 }
1729
1730 let stuck_speed_multiplier = state.stuck_speed_multiplier;
1731 state.stuck_speed_multiplier = DVec3::ZERO;
1732 state.velocity = DVec3::ZERO;
1733
1734 if apply_multiplier {
1735 movement * stuck_speed_multiplier
1736 } else {
1737 movement
1738 }
1739 }
1740}
1741
1742#[cfg(test)]
1743mod tests;