1use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8use chrono::{DateTime, Utc};
9
10use crate::spatial::{RegionBounds, RegionCoordinate, WorldCoordinate};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub struct ServerId(pub Uuid);
15
16impl ServerId {
17 pub fn new() -> Self {
19 Self(Uuid::new_v4())
20 }
21
22 pub fn from_str(s: &str) -> Result<Self, uuid::Error> {
24 Uuid::parse_str(s).map(Self)
25 }
26}
27
28impl Default for ServerId {
29 fn default() -> Self {
30 Self::new()
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
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum ServerStatus {
44 Starting,
46 Running,
48 Draining,
50 Stopped,
52 Error,
54}
55
56impl Default for ServerStatus {
57 fn default() -> Self {
58 Self::Starting
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ServerInfo {
65 pub id: ServerId,
67 pub name: String,
69 pub address: String,
71 pub region_coord: RegionCoordinate,
73 pub bounds: RegionBounds,
75 pub center: WorldCoordinate,
77 pub capacity: u32,
79 pub version: String,
81}
82
83impl ServerInfo {
84 pub fn new(
86 name: String,
87 address: String,
88 region_coord: RegionCoordinate,
89 bounds: RegionBounds,
90 capacity: u32,
91 ) -> Self {
92 Self {
93 id: ServerId::new(),
94 name,
95 address,
96 region_coord,
97 bounds,
98 center: bounds.center(),
99 capacity,
100 version: env!("CARGO_PKG_VERSION").to_string(),
101 }
102 }
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct ServerRegistration {
108 pub server: ServerInfo,
110 pub status: ServerStatus,
112 pub registered_at: DateTime<Utc>,
114 #[serde(default)]
116 pub metadata: std::collections::HashMap<String, serde_json::Value>,
117}
118
119impl ServerRegistration {
120 pub fn new(server: ServerInfo) -> Self {
122 Self {
123 server,
124 status: ServerStatus::Starting,
125 registered_at: Utc::now(),
126 metadata: std::collections::HashMap::new(),
127 }
128 }
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct ServerHeartbeat {
134 pub server_id: ServerId,
136 pub status: ServerStatus,
138 pub current_connections: u32,
140 pub load: f32,
142 pub timestamp: DateTime<Utc>,
144 #[serde(default)]
146 pub avg_tick_ms: f64,
147 #[serde(default)]
149 pub memory_bytes: u64,
150}
151
152impl ServerHeartbeat {
153 pub fn new(
155 server_id: ServerId,
156 status: ServerStatus,
157 current_connections: u32,
158 capacity: u32,
159 ) -> Self {
160 let load = if capacity > 0 {
161 current_connections as f32 / capacity as f32
162 } else {
163 0.0
164 };
165
166 Self {
167 server_id,
168 status,
169 current_connections,
170 load,
171 timestamp: Utc::now(),
172 avg_tick_ms: 0.0,
173 memory_bytes: 0,
174 }
175 }
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct RegistrationResponse {
181 pub success: bool,
183 pub server_id: ServerId,
185 pub message: String,
187 pub heartbeat_interval_secs: u32,
189 #[serde(default)]
191 pub adjacent_servers: Vec<ServerInfo>,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct SpawnServerRequest {
197 pub region_coord: RegionCoordinate,
199 pub bounds: RegionBounds,
201 pub name: Option<String>,
203 #[serde(default)]
205 pub environment: std::collections::HashMap<String, String>,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct SpawnServerResponse {
211 pub success: bool,
213 pub instance_id: String,
215 pub address: Option<String>,
217 pub error: Option<String>,
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_server_registration() {
227 let info = ServerInfo::new(
228 "test-server".to_string(),
229 "127.0.0.1:8080".to_string(),
230 RegionCoordinate::center(),
231 RegionBounds::default(),
232 100,
233 );
234 let reg = ServerRegistration::new(info);
235 assert_eq!(reg.status, ServerStatus::Starting);
236 }
237
238 #[test]
239 fn test_heartbeat_load() {
240 let heartbeat = ServerHeartbeat::new(
241 ServerId::new(),
242 ServerStatus::Running,
243 50,
244 100,
245 );
246 assert!((heartbeat.load - 0.5).abs() < 0.001);
247 }
248}