veloren_server_cli/
shutdown_coordinator.rs1use crate::settings::Settings;
2use common::comp::{Content, chat::ChatType};
3use common_net::msg::ServerGeneral;
4use server::Server;
5use std::{
6 ops::Add,
7 sync::{
8 Arc,
9 atomic::{AtomicBool, Ordering},
10 },
11 time::{Duration, Instant},
12};
13use tracing::{error, info};
14
15pub(crate) struct ShutdownCoordinator {
19 last_shutdown_msg: Instant,
22 msg_interval: Duration,
24 shutdown_initiated_at: Option<Instant>,
26 shutdown_grace_period: Duration,
28 shutdown_message: String,
31 shutdown_signal: Arc<AtomicBool>,
33}
34
35impl ShutdownCoordinator {
36 pub fn new(shutdown_signal: Arc<AtomicBool>) -> Self {
37 Self {
38 last_shutdown_msg: Instant::now(),
39 msg_interval: Duration::from_secs(30),
40 shutdown_initiated_at: None,
41 shutdown_grace_period: Duration::from_secs(0),
42 shutdown_message: String::new(),
43 shutdown_signal,
44 }
45 }
46
47 pub fn initiate_shutdown(
51 &mut self,
52 server: &mut Server,
53 grace_period: Duration,
54 message: String,
55 ) {
56 if self.shutdown_initiated_at.is_none() {
57 self.shutdown_grace_period = grace_period;
58 self.shutdown_initiated_at = Some(Instant::now());
59 self.shutdown_message = message;
60
61 self.send_shutdown_msg(server);
63 } else {
64 error!("Shutdown already in progress")
65 }
66 }
67
68 pub fn abort_shutdown(&mut self, server: &mut Server) {
71 if self.shutdown_initiated_at.is_some() {
72 self.shutdown_initiated_at = None;
73 ShutdownCoordinator::send_msg(server, "The shutdown has been aborted".to_owned());
74 } else {
75 error!("There is no shutdown in progress");
76 }
77 }
78
79 pub fn check(&mut self, server: &mut Server, settings: &Settings) -> bool {
84 self.check_shutdown_signal(server, settings);
86
87 if let Some(shutdown_initiated_at) = self.shutdown_initiated_at {
90 if Instant::now() > shutdown_initiated_at.add(self.shutdown_grace_period) {
91 info!("Shutting down");
92 return true;
93 }
94
95 if let Some(time_until_shutdown) = self.time_until_shutdown() {
97 if time_until_shutdown <= Duration::from_secs(10) {
98 self.msg_interval = Duration::from_secs(1);
99 }
100 }
101
102 if self.last_shutdown_msg + self.msg_interval <= Instant::now() {
105 self.send_shutdown_msg(server);
106 }
107 }
108
109 false
110 }
111
112 fn check_shutdown_signal(&mut self, server: &mut Server, settings: &Settings) {
118 if self.shutdown_signal.load(Ordering::Relaxed) && self.shutdown_initiated_at.is_none() {
119 info!("Received shutdown signal, initiating graceful shutdown");
120 let grace_period =
121 Duration::from_secs(u64::from(settings.update_shutdown_grace_period_secs));
122 let shutdown_message = settings.update_shutdown_message.to_owned();
123 self.initiate_shutdown(server, grace_period, shutdown_message);
124
125 self.shutdown_signal.store(false, Ordering::Relaxed);
128 }
129 }
130
131 fn send_shutdown_msg(&mut self, server: &mut Server) {
134 if let Some(time_until_shutdown) = self.time_until_shutdown() {
135 let msg = format!(
136 "{} in {}",
137 self.shutdown_message,
138 ShutdownCoordinator::duration_to_text(time_until_shutdown)
139 );
140 ShutdownCoordinator::send_msg(server, msg);
141 self.last_shutdown_msg = Instant::now();
142 }
143 }
144
145 fn time_until_shutdown(&self) -> Option<Duration> {
147 let shutdown_initiated_at = self.shutdown_initiated_at?;
148 let shutdown_time = shutdown_initiated_at + self.shutdown_grace_period;
149
150 shutdown_time.checked_duration_since(Instant::now())
154 }
155
156 fn send_msg(server: &mut Server, msg: String) {
158 info!("{}", &msg);
159 server.notify_players(ServerGeneral::server_msg(
160 ChatType::CommandError,
161 Content::Plain(msg),
162 ));
163 }
164
165 fn duration_to_text(duration: Duration) -> String {
169 let secs = duration.as_secs_f32().round() as i32 % 60;
170 let mins = duration.as_secs_f32().round() as i32 / 60;
171
172 let mut text = String::new();
173 if mins > 0 {
174 text.push_str(format!("{}m", mins).as_str())
175 }
176 if secs > 0 {
177 text.push_str(format!("{}s", secs).as_str())
178 }
179 text
180 }
181}