1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use chrono::{DateTime, Utc};
9
10use crate::spatial::{RegionBounds, RegionCoordinate, WorldCoordinate};
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
15pub struct ServerId(pub String);
16
17impl ServerId {
18 pub fn new() -> Self {
20 Self(uuid::Uuid::new_v4().to_string())
21 }
22
23 pub fn from_string(s: impl Into<String>) -> Self {
25 Self(s.into())
26 }
27
28 pub fn as_str(&self) -> &str {
30 &self.0
31 }
32}
33
34impl std::fmt::Display for ServerId {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(f, "{}", self.0)
37 }
38}
39
40impl From<String> for ServerId {
41 fn from(s: String) -> Self {
42 Self(s)
43 }
44}
45
46impl From<&str> for ServerId {
47 fn from(s: &str) -> Self {
48 Self(s.to_string())
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "snake_case")]
55pub enum ServerStatus {
56 Starting,
58 Running,
60 Draining,
62 Stopped,
64 Error,
66}
67
68impl Default for ServerStatus {
69 fn default() -> Self {
70 Self::Starting
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ServerInfo {
77 pub id: ServerId,
79 pub name: String,
81 pub address: String,
83 pub region_coord: RegionCoordinate,
85 pub bounds: RegionBounds,
87 pub center: WorldCoordinate,
89 pub capacity: u32,
91 pub version: String,
93}
94
95impl ServerInfo {
96 pub fn new(
98 name: String,
99 address: String,
100 region_coord: RegionCoordinate,
101 bounds: RegionBounds,
102 capacity: u32,
103 ) -> Self {
104 Self {
105 id: ServerId::new(),
106 name,
107 address,
108 region_coord,
109 bounds,
110 center: bounds.center(),
111 capacity,
112 version: env!("CARGO_PKG_VERSION").to_string(),
113 }
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct ServerRegistration {
120 pub server: ServerInfo,
122 pub status: ServerStatus,
124 pub registered_at: DateTime<Utc>,
126 #[serde(default)]
128 pub metadata: std::collections::HashMap<String, serde_json::Value>,
129}
130
131impl ServerRegistration {
132 pub fn new(server: ServerInfo) -> Self {
134 Self {
135 server,
136 status: ServerStatus::Starting,
137 registered_at: Utc::now(),
138 metadata: std::collections::HashMap::new(),
139 }
140 }
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct ServerHeartbeat {
146 pub server_id: ServerId,
148 pub status: ServerStatus,
150 pub current_connections: u32,
152 pub load: f32,
154 pub timestamp: DateTime<Utc>,
156 #[serde(default)]
158 pub avg_tick_ms: f64,
159 #[serde(default)]
161 pub memory_bytes: u64,
162}
163
164impl ServerHeartbeat {
165 pub fn new(
167 server_id: ServerId,
168 status: ServerStatus,
169 current_connections: u32,
170 capacity: u32,
171 ) -> Self {
172 let load = if capacity > 0 {
173 current_connections as f32 / capacity as f32
174 } else {
175 0.0
176 };
177
178 Self {
179 server_id,
180 status,
181 current_connections,
182 load,
183 timestamp: Utc::now(),
184 avg_tick_ms: 0.0,
185 memory_bytes: 0,
186 }
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct RegistrationResponse {
193 pub success: bool,
195 pub server_id: ServerId,
197 pub message: String,
199 pub heartbeat_interval_secs: u32,
201 #[serde(default)]
203 pub adjacent_servers: Vec<ServerInfo>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct SpawnServerRequest {
209 pub region_coord: RegionCoordinate,
211 pub bounds: RegionBounds,
213 pub name: Option<String>,
215 #[serde(default)]
217 pub environment: std::collections::HashMap<String, String>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct SpawnServerResponse {
223 pub success: bool,
225 pub instance_id: String,
227 pub address: Option<String>,
229 pub error: Option<String>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ApiServerRegistration {
237 pub name: String,
239 pub address: String,
241 pub region_coord: RegionCoordinate,
243 pub center: WorldCoordinate,
245 pub bounds: f64,
247 pub capacity: u32,
249 #[serde(default)]
251 pub version: String,
252 #[serde(default)]
254 pub metadata: HashMap<String, serde_json::Value>,
255}
256
257impl ApiServerRegistration {
258 pub fn from_bounds(
260 name: String,
261 address: String,
262 region_coord: RegionCoordinate,
263 bounds: &RegionBounds,
264 capacity: u32,
265 ) -> Self {
266 Self {
267 name,
268 address,
269 region_coord,
270 center: bounds.center(),
271 bounds: bounds.half_extent(),
272 capacity,
273 version: String::new(),
274 metadata: HashMap::new(),
275 }
276 }
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct ApiRegistrationResponse {
282 pub success: bool,
283 pub server_id: String,
284 pub message: String,
285 pub heartbeat_interval_secs: u32,
286 #[serde(default)]
287 pub adjacent_servers: Vec<AdjacentServerInfo>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct AdjacentServerInfo {
293 pub server_id: String,
294 pub address: String,
295 pub region_coord: RegionCoordinate,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct ApiServerHeartbeat {
301 pub server_id: String,
302 pub current_connections: u32,
303 pub load: f32,
304 pub accepting_connections: bool,
305 #[serde(default)]
306 pub avg_tick_ms: f64,
307 #[serde(default)]
308 pub memory_bytes: u64,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct ApiHeartbeatResponse {
314 pub success: bool,
315 pub message: String,
316 #[serde(default)]
317 pub commands: Vec<ServerCommand>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
322#[serde(tag = "type")]
323pub enum ServerCommand {
324 PrepareShutdown { deadline_secs: u32 },
325 ConfigUpdate { config: serde_json::Value },
326 HealthCheck,
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 #[test]
334 fn test_server_registration() {
335 let info = ServerInfo::new(
336 "test-server".to_string(),
337 "127.0.0.1:8080".to_string(),
338 RegionCoordinate::center(),
339 RegionBounds::default(),
340 100,
341 );
342 let reg = ServerRegistration::new(info);
343 assert_eq!(reg.status, ServerStatus::Starting);
344 }
345
346 #[test]
347 fn test_heartbeat_load() {
348 let heartbeat = ServerHeartbeat::new(
349 ServerId::new(),
350 ServerStatus::Running,
351 50,
352 100,
353 );
354 assert!((heartbeat.load - 0.5).abs() < 0.001);
355 }
356}