1pub mod minecraft {
7 pub const VERSION: &str = "1.21.4";
8 pub const PROTOCOL_VERSION: usize = 769;
9}
10
11pub mod messages {
13 use colored::*;
14 use once_cell::sync::Lazy;
15
16 use super::minecraft::VERSION;
17
18 pub static SERVER_STARTING: Lazy<String> = Lazy::new(|| {
19 format!("Starting minecraft server version {}", VERSION)
20 .bold()
21 .to_string()
22 });
23
24 pub static SERVER_STARTED: Lazy<String> =
25 Lazy::new(|| "[ SERVER STARTED ]".bright_green().bold().to_string());
26
27 pub static SERVER_SHUTDOWN_SUCCESS: Lazy<String> =
28 Lazy::new(|| "[ SERVER SHUT DOWN ]".bright_red().bold().to_string());
29
30 pub static PLAY_PACKET_NOTIFIER: Lazy<String> =
31 Lazy::new(|| "[ SEND PLAY PACKET HERE! ]".bright_red().bold().to_string());
32
33 pub static SERVER_SHUTDOWN_ERROR: Lazy<String> = Lazy::new(|| {
34 "[ SERVER SHUT DOWN WITH ERROR ]"
35 .bright_red()
36 .bold()
37 .to_string()
38 });
39
40 pub static SERVER_SHUTDOWN_CTRL_C: Lazy<String> = Lazy::new(|| {
41 "[ SERVER SHUT DOWN WITH CTRL+C ]"
42 .bright_red()
43 .bold()
44 .to_string()
45 });
46
47 pub static GREET: Lazy<String> =
48 Lazy::new(|| "Hello, world from Cactus!".green().bold().to_string());
49}
50
51pub mod file_paths {
53 pub const PROPERTIES: &str = "server.properties";
55 pub const EULA: &str = "eula.txt";
56 pub const OPERATORS: &str = "ops.json";
57 pub const WHITELIST: &str = "whitelist.json";
58 pub const BANNED_IP: &str = "banned-ips.json";
59 pub const BANNED_PLAYERS: &str = "banned-players.json";
60 pub const USERCACHE: &str = "usercache.json";
61 pub const SESSION: &str = "session.lock";
62 pub const SERVER_ICON: &str = "server-icon.png";
63
64 pub const LOGGED_IPS: &str = "logged_ips.txt";
66 pub const LEVEL: &str = "world/level.dat";
67}
68
69pub mod cactus {
70 pub const DO_LOG_IPS_SLP: bool = true;
71}
72
73pub mod directory_paths {
74 pub const WORLDS_DIRECTORY: &str = "world/";
75 pub const THE_END: &str = "world/DIM1/";
76 pub const NETHER: &str = "world/DIM-1/";
77 pub const OVERWORLD: &str = "world/region/";
78 pub const LOGS: &str = "logs/";
79}
80
81pub mod file_contents {
82 use crate::time;
83
84 pub fn eula() -> String {
86 let mut content = String::new();
87
88 content += "# By changing the setting below to 'true' you are indicating your agreement to our EULA (https://aka.ms/MinecraftEULA).\n";
89 content += &format!("# {}", time::get_formatted_time());
90 content += "\neula=false";
91 content
92 }
93
94 pub fn server_properties() -> String {
96 const SERVER_PROPERTIES_INNER: &str = r#"accepts-transfers=false
97allow-flight=false
98allow-nether=true
99broadcast-console-to-ops=true
100broadcast-rcon-to-ops=true
101bug-report-link=
102difficulty=normal
103enable-command-block=false
104enable-jmx-monitoring=false
105enable-query=false
106enable-rcon=false
107enable-status=true
108enforce-secure-profile=true
109enforce-whitelist=false
110entity-broadcast-range-percentage=100
111force-gamemode=false
112function-permission-level=2
113gamemode=survival
114generate-structures=true
115generator-settings={}
116hardcore=false
117hide-online-players=false
118initial-disabled-packs=
119initial-enabled-packs=vanilla
120level-name=world
121level-seed=
122level-type=minecraft\:normal
123log-ips=true
124max-chained-neighbor-updates=1000000
125max-players=20
126max-tick-time=60000
127max-world-size=29999984
128motd=A beautiful CactusMC server!
129network-compression-threshold=256
130online-mode=true
131op-permission-level=4
132player-idle-timeout=0
133prevent-proxy-connections=false
134pvp=true
135query.port=25565
136rate-limit=0
137rcon.password=
138rcon.port=25575
139region-file-compression=deflate
140require-resource-pack=false
141resource-pack=
142resource-pack-id=
143resource-pack-prompt=
144resource-pack-sha1=
145server-ip=
146server-port=25565
147simulation-distance=10
148spawn-animals=true
149spawn-monsters=true
150spawn-npcs=true
151spawn-protection=16
152sync-chunk-writes=true
153text-filtering-config=
154use-native-transport=true
155view-distance=10
156white-list=false"#;
157
158 format!(
159 "# Minecraft server properties\n# {}\n{}",
160 time::get_formatted_time(),
161 SERVER_PROPERTIES_INNER
162 )
163 }
164}
165
166pub mod protocol {
168 use base64::{engine::general_purpose, Engine};
169 use image::{GenericImageView, ImageFormat};
170 use log::error;
171 use serde_json::json;
172
173 use crate::{config::Settings, gracefully_exit};
174
175 use super::file_paths::SERVER_ICON;
176
177 fn get_favicon() -> Result<String, Box<dyn std::error::Error>> {
180 let file_data = std::fs::read(SERVER_ICON)?;
181
182 let format = image::guess_format(&file_data)?;
184 if format != ImageFormat::Png {
185 return Err("The server icon must be in PNG format".into());
186 }
187
188 let img = image::load_from_memory_with_format(&file_data, format)?;
190 if img.dimensions() != (64, 64) {
191 return Err("The server icon must have dimensions of 64x64".into());
192 }
193
194 let base64_icon = general_purpose::STANDARD.encode(file_data);
196
197 let favicon = format!("data:image/png;base64,{base64_icon}");
199 Ok(favicon)
200 }
201
202 pub fn status_response_json() -> String {
204 let config = Settings::new();
205
206 let version_name = super::minecraft::VERSION;
207 let protocol = super::minecraft::PROTOCOL_VERSION;
208 let max_players = config.max_players;
209
210 let online_players = 0;
212
213 let description_text = config.motd;
214
215 if let Err(err) = get_favicon() {
217 error!("Server icon not found: {err}. Shutting down the server...");
218 gracefully_exit(crate::ExitCode::Failure);
219 }
220 let favicon = get_favicon().unwrap();
221
222 let enforces_secure_chat = config.enforce_secure_profile;
223
224 let json_data = json!({
225 "version": {
226 "name": version_name,
227 "protocol": protocol
228 },
229 "players": {
230 "max": max_players,
231 "online": online_players,
232 },
233 "description": {
234 "text": description_text
235 },
236 "favicon": favicon,
237 "enforcesSecureChat": enforces_secure_chat
238 });
239
240 serde_json::to_string(&json_data).unwrap()
241 }
242}