Skip to main content

steel_core/world/
sleep.rs

1use steel_protocol::packets::game::CSystemChat;
2use steel_registry::vanilla_game_rules::{
3    ADVANCE_TIME, ADVANCE_WEATHER, PLAYERS_SLEEPING_PERCENTAGE,
4};
5use steel_utils::Identifier;
6use text_components::{TextComponent, translation::TranslatedMessage};
7
8use super::{World, sleep_status::SleepStatus};
9use crate::entity::LivingEntity as _;
10
11const WAKE_UP_FROM_SLEEP_TIME_MARKER: Identifier = Identifier::vanilla_static("wake_up_from_sleep");
12
13impl World {
14    /// Returns whether this world can skip night.
15    #[must_use]
16    pub fn can_sleep_through_nights(&self) -> bool {
17        self.players_sleeping_percentage() <= 100
18    }
19
20    fn players_sleeping_percentage(&self) -> i32 {
21        self.get_game_rule(&PLAYERS_SLEEPING_PERCENTAGE)
22    }
23
24    /// Updates vanilla sleeping player counts and broadcasts the sleep status overlay.
25    pub fn update_sleeping_player_list(&self) {
26        if self.players.is_empty() {
27            return;
28        }
29
30        let mut updated_status = SleepStatus::default();
31        self.players.iter_players(|_, player| {
32            updated_status.add_player(player);
33            true
34        });
35
36        let mut sleep_status = self.sleep_status.lock();
37        let changed = sleep_status.update(updated_status);
38        if changed {
39            self.announce_sleep_status(*sleep_status);
40        }
41    }
42
43    fn announce_sleep_status(&self, sleep_status: SleepStatus) {
44        if !self.can_sleep_through_nights() {
45            return;
46        }
47
48        let percentage = self.players_sleeping_percentage();
49        let message = if sleep_status.are_enough_sleeping(percentage) {
50            TranslatedMessage {
51                key: "sleep.skipping_night".into(),
52                fallback: None,
53                args: None,
54            }
55            .component()
56        } else {
57            TranslatedMessage {
58                key: "sleep.players_sleeping".into(),
59                fallback: None,
60                args: Some(
61                    vec![
62                        TextComponent::from(sleep_status.amount_sleeping().to_string()),
63                        TextComponent::from(sleep_status.sleepers_needed(percentage).to_string()),
64                    ]
65                    .into(),
66                ),
67            }
68            .component()
69        };
70
71        self.broadcast_to_all_with(|player| CSystemChat::new(&message, true, player));
72    }
73
74    pub(super) fn tick_sleeping_players(&self) {
75        if self.players.is_empty() {
76            return;
77        }
78
79        let percentage = self.players_sleeping_percentage();
80        let sleepers_needed = {
81            let sleep_status = self.sleep_status.lock();
82            if !sleep_status.are_enough_sleeping(percentage) {
83                return;
84            }
85            sleep_status.sleepers_needed(percentage)
86        };
87
88        let mut deep_sleepers = 0;
89        self.players.iter_players(|_, player| {
90            if player.is_sleeping_long_enough() {
91                deep_sleepers += 1;
92            }
93            deep_sleepers < sleepers_needed
94        });
95
96        if deep_sleepers < sleepers_needed {
97            return;
98        }
99
100        if self.get_game_rule(&ADVANCE_TIME) {
101            self.move_day_time_to_next_morning();
102        }
103
104        self.wake_up_all_players();
105        if self.get_game_rule(&ADVANCE_WEATHER) && self.is_raining() {
106            self.reset_weather_cycle();
107        }
108    }
109
110    fn move_day_time_to_next_morning(&self) {
111        let Some(clock) = self.dimension_type.default_clock else {
112            return;
113        };
114        let _ = self.move_clock_to_time_marker(clock, &WAKE_UP_FROM_SLEEP_TIME_MARKER);
115    }
116
117    fn wake_up_all_players(&self) {
118        self.sleep_status.lock().remove_all_sleepers();
119        self.players.iter_players(|_, player| {
120            if player.is_sleeping() {
121                player.stop_sleep_in_bed(false, false);
122            }
123            true
124        });
125    }
126
127    fn reset_weather_cycle(&self) {
128        let mut level_data = self.level_data.write();
129        level_data.set_rain_time(0);
130        level_data.set_raining(false);
131        level_data.set_thunder_time(0);
132        level_data.set_thundering(false);
133    }
134}