9bcc7d33f3cca961b54465acfcbc6f4cbe83be86
[minecraft_web.git] / backend / src / minecraft_controller.rs
1 use sysinfo::{ ProcessExt, SystemExt };
2
3 use std::{ fs, time::SystemTime };
4
5 use chrono::{ DateTime, offset::Local };
6
7 #[derive(Clone, Debug)]
8 pub struct MinecraftExe {
9 memory: u64, // [kB].
10 load_average_5min: f64, // [%].
11 uptime: u64, // [s].
12 world_size: u64, // [B].
13 active_players: Vec<String>,
14 last_backup: Option<SystemTime>,
15 }
16
17 impl MinecraftExe {
18 pub fn format_memory(&self) -> String {
19 format_byte_size(self.memory * 1024, 2)
20 }
21
22 pub fn format_load_average(&self) -> String {
23 format!("{:.2} %", self.load_average_5min)
24 }
25
26 pub fn format_uptime(&self) -> String {
27 let mins = self.uptime / 60;
28 let hours = mins / 60;
29 let days = hours / 24;
30 format!("{}d {}h {}min", days, hours - 24 * days, mins - 60 * hours)
31 }
32
33 pub fn format_world_size(&self) -> String {
34 format_byte_size(self.world_size, 2)
35 }
36
37 pub fn format_active_players(&self) -> String {
38 /* Commented because the player list isn't correct (the number is).
39 if self.active_players.len() == 0 {
40 String::from("<none>")
41 } else {
42 self.active_players.join(", ")
43 }*/
44 self.active_players.len().to_string()
45 }
46
47 pub fn format_last_backup(&self) -> String {
48 match self.last_backup {
49 Some(t) => {
50 let datetime: DateTime<Local> = t.into();
51 datetime.format("%d/%m/%Y %T").to_string()
52 },
53 None => String::from("?")
54 }
55 }
56 }
57
58 const BINARY_PREFIXES: [&str; 8] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"];
59
60 fn format_byte_size(bytes: u64, precision: usize) -> String {
61 for i in 0 .. 8 {
62 let mut size: u64 = 1;
63 size *= 1024u64.pow(i as u32);
64
65 if bytes < 1024 {
66 return format!("{} {}", std::cmp::max(0u64, bytes), BINARY_PREFIXES[i]);
67 }
68 else if bytes < 1024 * size {
69 return format!("{:.prec$} {}", bytes as f64 / size as f64, BINARY_PREFIXES[i], prec = precision);
70 }
71 }
72
73 String::from("")
74 }
75
76 const MINECRAFT_PROCESS_NAME: &str = "java";
77
78 #[cfg(target_os = "linux")]
79 const STRING_BEFORE_CHARACTER_NAME: &str = "Got character ZDOID from";
80
81 #[cfg(target_os = "linux")]
82 const STRING_BEFORE_NB_OF_CONNECTIONS: &str = "Connections";
83
84 // It doesn't work for the moment, it only scan the connection event and do not treat disconnections.
85 fn get_active_players(rcon_password: &str) -> Vec<String> {
86 let mut client = minecraft_client_rs::Client::new("127.0.0.1:25575".to_string()).unwrap();
87
88 let players =
89 match client.authenticate(rcon_password.to_string()) {
90 Ok(_) => {
91 match client.send_command("list".to_string()) {
92 Ok(resp) => {
93 println!("{}", resp.body);
94 Vec::new()
95 },
96 Err(_e) => {
97 println!("Error asking seed");
98 Vec::new()
99 },
100 }
101 },
102 Err(_e) => {
103 println!("Authentication error");
104 Vec::new()
105 },
106 };
107
108 client.close().unwrap();
109
110 players
111 }
112
113 fn get_last_backup_datetime(backup_path: &str) -> Option<SystemTime> {
114 let mut times =
115 fs::read_dir(backup_path).ok()?.filter_map(
116 |e| {
117 let dir = e.ok()?;
118 if dir.path().is_file() { Some(dir.metadata().ok()?.modified().ok()?) } else { None }
119 }
120 )
121 .collect::<Vec<SystemTime>>();
122
123 times.sort();
124
125 Some(times.last()?.clone())
126 }
127
128 pub fn get_minecraft_executable_information(world_path: &str, backup_path: &str, rcon_password: &str) -> Option<MinecraftExe> {
129 let mut system = sysinfo::System::new_all();
130 system.refresh_system();
131 let processes = system.get_process_by_name(MINECRAFT_PROCESS_NAME);
132
133 // TODO: find the correct process by checking the correct jar name in parameters.
134 if processes.len() >= 1 {
135 let process = processes.first().unwrap();
136
137 let world_size = match std::fs::metadata(world_path) { Ok(f) => f.len(), Err(_) => 0u64 };
138
139 Some(
140 MinecraftExe {
141 memory: process.memory(),
142 load_average_5min: system.get_load_average().five / system.get_processors().len() as f64 * 100.,
143 uptime: std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() - process.start_time(),
144 world_size,
145 active_players: get_active_players(rcon_password),
146 last_backup: get_last_backup_datetime(backup_path)
147 }
148 )
149 } else {
150 None
151 }
152 }