1use infrarust_protocol::minecraft::java::status::clientbound_response::PlayerSampleJSON;
2use infrarust_server_manager::LocalServerConfig;
3use serde::Deserialize;
4
5use super::{cache::CacheConfig, filter::FilterConfig};
6
7#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Default)]
8pub enum ProxyModeEnum {
9 #[serde(rename = "passthrough")]
10 #[default]
11 Passthrough,
12 #[serde(rename = "client_only")]
15 ClientOnly,
16 #[serde(rename = "offline")]
17 Offline,
18 #[serde(rename = "server_only")]
19 ServerOnly,
20 #[serde(rename = "zerocopy_passthrough")]
21 ZeroCopyPassthrough,
22
23 #[serde(skip)]
24 Status,
25}
26
27#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Hash, Eq)]
28pub enum ManagerType {
29 Pterodactyl,
30 Crafty,
31 Docker,
33 Local,
34 Custom,
35}
36
37#[derive(Debug, Clone, Deserialize)]
38pub struct MotdConfig {
39 #[serde(default)]
40 pub enabled: bool,
41 pub text: Option<String>,
42 pub version_name: Option<String>,
43 pub max_players: Option<i32>,
44 pub online_players: Option<i32>,
45 pub protocol_version: Option<i32>,
46 pub samples: Option<Vec<PlayerSampleJSON>>,
47 pub favicon: Option<String>,
48}
49
50impl Default for MotdConfig {
51 fn default() -> Self {
52 MotdConfig {
53 enabled: false,
54 text: Some(String::new()),
55 max_players: Some(0),
56 online_players: Some(0),
57 protocol_version: Some(0),
58 samples: Some(Vec::new()),
59 version_name: Some(String::new()),
60 favicon: None,
61 }
62 }
63}
64
65impl MotdConfig {
66 pub fn default_unreachable() -> Self {
67 let version = env!("CARGO_PKG_VERSION");
68
69 MotdConfig {
70 enabled: true,
71 text: Some("This server seems to be offline".to_string()),
72 max_players: Some(0),
73 online_players: Some(0),
74 protocol_version: Some(0),
75 samples: Some(Vec::new()),
76 version_name: Some(("Infrarust v").to_string() + version),
77 favicon: Some(FAVICON.to_string()),
78 }
79 }
80
81 pub fn is_empty(&self) -> bool {
82 self.text.is_none()
83 && self.version_name.is_none()
84 && self.max_players.is_none()
85 && self.online_players.is_none()
86 && self.protocol_version.is_none()
87 && self.samples.is_none()
88 && self.favicon.is_none()
89 }
90}
91
92#[derive(Debug, Clone, Deserialize)]
93pub struct PlayerInfo {
94 pub max: i32,
95 pub online: i32,
96 pub sample: Option<Vec<PlayerSample>>,
97}
98
99#[derive(Debug, Clone, Deserialize)]
100pub struct PlayerSample {
101 pub name: String,
102 pub id: String,
103}
104
105#[derive(Debug, Clone, Deserialize)]
106pub struct VersionInfo {
107 pub name: String,
108 pub protocol: i32,
109}
110
111#[derive(Debug, Clone, Deserialize)]
112pub struct ServerManagerConfig {
113 pub provider_name: ManagerType,
114 pub server_id: String,
115 pub empty_shutdown_time: Option<u64>,
116 pub local_provider: Option<LocalServerConfig>,
117}
118
119#[derive(Debug, Clone, Deserialize)]
120pub struct ServerConfig {
121 pub domains: Vec<String>,
122 pub addresses: Vec<String>,
123 #[serde(rename = "sendProxyProtocol")]
124 pub send_proxy_protocol: Option<bool>,
125 #[serde(rename = "proxyMode")]
126 pub proxy_mode: Option<ProxyModeEnum>,
127 pub filters: Option<FilterConfig>,
128 pub caches: Option<CacheConfig>,
129
130 #[serde(default)]
131 pub motds: ServerMotds,
132 pub server_manager: Option<ServerManagerConfig>,
133
134 #[serde(rename = "configId", default)]
135 pub config_id: String,
136 pub proxy_protocol_version: Option<u8>,
137 #[serde(rename = "backendDomain")]
139 pub backend_domain: Option<String>,
140 #[serde(rename = "rewriteDomain", default)]
141 pub rewrite_domain: bool,
142}
143
144impl Default for ServerConfig {
145 fn default() -> Self {
146 ServerConfig {
147 domains: Vec::new(),
148 addresses: Vec::new(),
149 send_proxy_protocol: Some(false),
150 proxy_mode: Some(ProxyModeEnum::default()),
151 config_id: String::new(),
152 filters: None,
153 caches: None,
154 motds: ServerMotds::default(),
155 server_manager: None,
156 proxy_protocol_version: Some(2),
157 backend_domain: None,
158 rewrite_domain: false,
159 }
160 }
161}
162
163impl ServerConfig {
164 pub fn is_empty(&self) -> bool {
165 self.domains.is_empty() && self.addresses.is_empty()
166 }
167 pub fn get_effective_backend_domain(&self) -> Option<String> {
168 if let Some(ref domain) = self.backend_domain {
169 return Some(domain.clone());
170 }
171 if self.rewrite_domain {
172 return self
173 .addresses
174 .first()
175 .and_then(|addr| addr.split(':').next().map(|s| s.to_string()));
176 }
177 None
178 }
179}
180
181#[derive(Debug, Deserialize, Clone, Default)]
182pub struct ServerMotds {
183 pub unknown: Option<MotdConfig>,
184 pub unreachable: Option<MotdConfig>,
185 pub online: Option<MotdConfig>,
186 pub offline: Option<MotdConfig>,
187 pub starting: Option<MotdConfig>,
188 pub stopping: Option<MotdConfig>,
189 pub crashed: Option<MotdConfig>,
190 pub shutting_down: Option<MotdConfig>,
191 pub unable_status: Option<MotdConfig>,
192}
193
194const FAVICON: &str = "data:image/png;base64,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";
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_get_effective_backend_domain_none() {
203 let config = ServerConfig::default();
204 assert_eq!(config.get_effective_backend_domain(), None);
205 }
206
207 #[test]
208 fn test_get_effective_backend_domain_explicit() {
209 let mut config = ServerConfig::default();
210 config.backend_domain = Some("explicit.domain.com".to_string());
211 config.addresses = vec!["other.domain.com:25565".to_string()];
212
213 assert_eq!(
214 config.get_effective_backend_domain(),
215 Some("explicit.domain.com".to_string())
216 );
217 }
218
219 #[test]
220 fn test_get_effective_backend_domain_explicit_takes_precedence() {
221 let mut config = ServerConfig::default();
222 config.backend_domain = Some("explicit.domain.com".to_string());
223 config.rewrite_domain = true;
224 config.addresses = vec!["address.domain.com:25565".to_string()];
225
226 assert_eq!(
228 config.get_effective_backend_domain(),
229 Some("explicit.domain.com".to_string())
230 );
231 }
232
233 #[test]
234 fn test_get_effective_backend_domain_rewrite_ip_address() {
235 let mut config = ServerConfig::default();
236 config.rewrite_domain = true;
237 config.addresses = vec!["192.168.1.100:25565".to_string()];
238
239 assert_eq!(
240 config.get_effective_backend_domain(),
241 Some("192.168.1.100".to_string())
242 );
243 }
244
245 #[test]
246 fn test_get_effective_backend_domain_rewrite_no_port() {
247 let mut config = ServerConfig::default();
248 config.rewrite_domain = true;
249 config.addresses = vec!["example.com".to_string()];
250
251 assert_eq!(
252 config.get_effective_backend_domain(),
253 Some("example.com".to_string())
254 );
255 }
256
257 #[test]
258 fn test_get_effective_backend_domain_rewrite_empty_addresses() {
259 let mut config = ServerConfig::default();
260 config.rewrite_domain = true;
261 assert_eq!(config.get_effective_backend_domain(), None);
264 }
265
266 #[test]
267 fn test_get_effective_backend_domain_rewrite_disabled() {
268 let mut config = ServerConfig::default();
269 config.rewrite_domain = false;
270 config.addresses = vec!["example.com:25565".to_string()];
271
272 assert_eq!(config.get_effective_backend_domain(), None);
273 }
274}