Skip to main content

infrarust_config/models/
server.rs

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 = "full")]
13    // Full,
14    #[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    //TODO
32    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    /// Useful for proxy-to-proxy setups where the downstream proxy expects a different domain
138    #[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
194//TODO: Move this in a motd_Crate
195const 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        // backend_domain should take precedence over rewrite_domain
227        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        // addresses is empty by default
262
263        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}