steel_core/world/
sleep.rs1use 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 #[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 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}