Skip to content

Commit c8118af

Browse files
committed
customize serialization
1 parent 3e31b73 commit c8118af

13 files changed

Lines changed: 251 additions & 96 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ serde = { optional = true, version = "1.0.219", features = ["derive"] }
2424

2525
[dev-dependencies]
2626
pretty_assertions = "1.4.1"
27+
serde_json = "1.0.140"
2728

2829
[features]
2930
ci = []

src/game_server/server.rs

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
use super::team;
2-
use crate::{GenericClient, GenericServer};
3-
4-
use crate::{ClientSlots, GeoInfo, Player, QtvStream, ServerType, SoftwareType, Spectator, Team};
2+
use crate::{
3+
ClientSlots, GenericClient, GenericServer, GeoInfo, Player, QtvStream, ServerType,
4+
SoftwareType, Spectator, Team,
5+
};
56
use quake_serverinfo::Settings;
67
use quake_text::unicode;
78

9+
#[cfg(feature = "serde")]
10+
use serde::ser::SerializeStruct;
11+
812
/// Represents a server where clients connect as [`Player`] or [`Spectator`].
913
#[derive(Debug, Clone, PartialEq)]
10-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14+
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
1115
pub struct GameServer {
1216
pub(crate) software_type: SoftwareType,
13-
pub(crate) address: String,
1417
pub(crate) ip: String,
1518
pub(crate) port: u16,
1619
pub(crate) settings: Settings,
@@ -31,8 +34,8 @@ impl GameServer {
3134
self.software_type.clone()
3235
}
3336

34-
pub fn address(&self) -> &str {
35-
&self.address
37+
pub fn address(&self) -> String {
38+
format!("{}:{}", self.ip(), self.port())
3639
}
3740

3841
pub fn ip(&self) -> &str {
@@ -46,16 +49,16 @@ impl GameServer {
4649
&self.settings
4750
}
4851

49-
pub fn teams(&self) -> impl Iterator<Item = &Team> {
50-
self.teams.iter()
52+
pub fn teams(&self) -> &[Team] {
53+
&self.teams
5154
}
5255

53-
pub fn players(&self) -> impl Iterator<Item = &Player> {
54-
self.players.iter()
56+
pub fn players(&self) -> &[Player] {
57+
&self.players
5558
}
5659

57-
pub fn spectators(&self) -> impl Iterator<Item = &Spectator> {
58-
self.spectators.iter()
60+
pub fn spectators(&self) -> &[Spectator] {
61+
&self.spectators
5962
}
6063

6164
pub fn qtv_stream(&self) -> Option<&QtvStream> {
@@ -89,7 +92,7 @@ impl GameServer {
8992

9093
impl From<&GenericServer> for GameServer {
9194
fn from(server: &GenericServer) -> Self {
92-
let mut clients: Vec<GenericClient> = server.clients().cloned().collect();
95+
let mut clients: Vec<GenericClient> = server.clients().into();
9396
clients.sort();
9497

9598
let is_teamplay = server.settings().teamplay.is_some_and(|tp| tp > 0);
@@ -108,7 +111,6 @@ impl From<&GenericServer> for GameServer {
108111

109112
Self {
110113
software_type: server.software_type(),
111-
address: server.address().to_string(),
112114
ip: server.ip().to_string(),
113115
port: server.port(),
114116
settings: server.settings().to_owned(),
@@ -121,6 +123,29 @@ impl From<&GenericServer> for GameServer {
121123
}
122124
}
123125

126+
#[cfg(feature = "serde")]
127+
impl serde::Serialize for GameServer {
128+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129+
where
130+
S: serde::Serializer,
131+
{
132+
let mut state = serializer.serialize_struct("GameServer", 12)?;
133+
state.serialize_field("server_type", &self.server_type())?;
134+
state.serialize_field("software_type", &self.software_type())?;
135+
state.serialize_field("address", &self.address())?;
136+
state.serialize_field("ip", self.ip())?;
137+
state.serialize_field("port", &self.port())?;
138+
state.serialize_field("settings", self.settings())?;
139+
state.serialize_field("player_slots", &self.player_slots())?;
140+
state.serialize_field("spectator_slots", &self.spectator_slots())?;
141+
state.serialize_field("teams", self.teams())?;
142+
state.serialize_field("players", self.players())?;
143+
state.serialize_field("spectators", self.spectators())?;
144+
state.serialize_field("geo", self.geo())?;
145+
state.end()
146+
}
147+
}
148+
124149
#[cfg(test)]
125150
#[cfg_attr(coverage_nightly, coverage(off))]
126151
mod tests {
@@ -134,7 +159,6 @@ mod tests {
134159
let generic = GenericServer {
135160
server_type: ServerType::GameServer,
136161
software_type: SoftwareType::Mvdsv,
137-
address: "10.10.10.10".to_string(),
138162
ip: "10.10.10.10".to_string(),
139163
port: 28501,
140164
settings: Settings {
@@ -175,9 +199,9 @@ mod tests {
175199
assert_eq!(server.port(), generic.port());
176200
assert_eq!(server.player_slots(), ClientSlots::new(2, 4));
177201
assert_eq!(server.spectator_slots(), ClientSlots::new(1, 8));
178-
assert_eq!(server.teams().count(), 1);
179-
assert_eq!(server.players().count(), 2);
180-
assert_eq!(server.spectators().count(), 1);
202+
assert_eq!(server.teams().len(), 1);
203+
assert_eq!(server.players().len(), 2);
204+
assert_eq!(server.spectators().len(), 1);
181205
assert_eq!(server.qtv_stream(), None);
182206
assert_eq!(server.geo(), generic.geo());
183207
assert!(!server.is_empty());
@@ -201,6 +225,36 @@ mod tests {
201225
..Default::default()
202226
};
203227
let server = GameServer::from(&generic);
204-
assert_eq!(server.teams().count(), 0);
228+
assert_eq!(server.teams().len(), 0);
229+
}
230+
231+
#[cfg(feature = "serde")]
232+
#[test]
233+
fn test_serialization() -> anyhow::Result<()> {
234+
let server = GameServer {
235+
software_type: SoftwareType::Mvdsv,
236+
ip: "10.10.10.10".to_string(),
237+
port: 28000,
238+
settings: Settings::default(),
239+
teams: vec![],
240+
players: vec![],
241+
spectators: vec![],
242+
qtv_stream: None,
243+
geo: GeoInfo {
244+
country_code: Some("US".to_string()),
245+
country_name: Some("United States".to_string()),
246+
city: Some("New York".to_string()),
247+
region: Some("North America".to_string()),
248+
coords: Some(Coords::new(40.7128, -74.0060)),
249+
},
250+
};
251+
252+
let server_json = r#"{"server_type":"game_server","software_type":"mvdsv","address":"10.10.10.10:28000","ip":"10.10.10.10","port":28000,"settings":{"admin":null,"broadcast":null,"city":null,"coords":null,"countrycode":null,"deathmatch":null,"epoch":null,"fpd":null,"fraglimit":null,"gamedir":null,"hostname":null,"hostport":null,"ktxmode":null,"ktxver":null,"map":null,"matchtag":null,"maxclients":null,"maxfps":null,"maxspectators":null,"mode":null,"needpass":null,"pm_ktjump":null,"progs":null,"qvm":null,"serverdemo":null,"status":null,"sv_antilag":null,"teamplay":null,"timelimit":null,"version":null,"z_ext":null},"player_slots":{"total":0,"used":0,"free":0},"spectator_slots":{"total":0,"used":0,"free":0},"teams":[],"players":[],"spectators":[],"geo":{"country_code":"US","country_name":"United States","city":"New York","region":"North America","coords":{"lat":40.7128,"lng":-74.006}}}"#;
253+
254+
// ensure round-trip serialization
255+
assert_eq!(serde_json::to_string(&server)?, server_json);
256+
assert_eq!(serde_json::from_str::<GameServer>(server_json)?, server);
257+
258+
Ok(())
205259
}
206260
}

src/game_server/spectator.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use crate::GenericClient;
44
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
55
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66
pub struct Spectator {
7-
id: u32,
8-
name: String,
9-
auth_cc: String,
10-
is_bot: bool,
7+
pub(super) id: u32,
8+
pub(super) name: String,
9+
pub(super) auth_cc: String,
10+
pub(super) is_bot: bool,
1111
}
1212

1313
impl From<&GenericClient> for Spectator {

src/generic_server/server.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ pub use quake_serverinfo::Settings;
77
pub struct GenericServer {
88
pub(crate) server_type: ServerType,
99
pub(crate) software_type: SoftwareType,
10-
pub(crate) address: String,
1110
pub(crate) ip: String,
1211
pub(crate) port: u16,
1312
pub(crate) settings: Settings,
@@ -26,8 +25,8 @@ impl GenericServer {
2625
self.software_type.clone()
2726
}
2827

29-
pub fn address(&self) -> &str {
30-
&self.address
28+
pub fn address(&self) -> String {
29+
format!("{}:{}", self.ip(), self.port())
3130
}
3231

3332
pub fn ip(&self) -> &str {
@@ -42,16 +41,16 @@ impl GenericServer {
4241
&self.settings
4342
}
4443

45-
pub fn clients(&self) -> impl Iterator<Item = &GenericClient> {
46-
self.clients.iter()
44+
pub fn clients(&self) -> &[GenericClient] {
45+
&self.clients
4746
}
4847

4948
pub fn players(&self) -> impl Iterator<Item = &GenericClient> {
50-
self.clients().filter(|client| !client.is_spectator())
49+
self.clients().iter().filter(|client| !client.is_spectator())
5150
}
5251

5352
pub fn spectators(&self) -> impl Iterator<Item = &GenericClient> {
54-
self.clients().filter(|client| client.is_spectator())
53+
self.clients().iter().filter(|client| client.is_spectator())
5554
}
5655

5756
pub fn qtv_stream(&self) -> Option<&QtvStream> {

src/net/query.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,10 @@ fn compose_server(
7979
let server = GenericServer {
8080
server_type: ServerType::from_version(&version),
8181
software_type: SoftwareType::from_version(&version),
82-
address: format!("{}:{}", ip, hostport.port()),
8382
ip,
8483
port: hostport.port(),
8584
settings: status_res.settings().clone(),
86-
clients: status_res.clients().cloned().collect(),
85+
clients: status_res.clients().into(),
8786
qtv_stream,
8887
geo: GeoInfo::from(status_res.settings()),
8988
};

src/net/svc_status/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ impl StatusResponse {
3333
&self.settings
3434
}
3535

36-
pub fn clients(&self) -> impl Iterator<Item = &GenericClient> {
37-
self.clients.iter()
36+
pub fn clients(&self) -> &[GenericClient] {
37+
&self.clients
3838
}
3939

4040
pub fn qtv_stream(&self) -> &Option<QtvStream> {

src/proxy_server/client.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use crate::GenericClient;
44
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
55
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66
pub struct ProxyClient {
7-
id: u32,
8-
time: u32,
9-
name: String,
7+
pub(super) id: u32,
8+
pub(super) time: u32,
9+
pub(super) name: String,
1010
}
1111

1212
impl From<&GenericClient> for ProxyClient {

0 commit comments

Comments
 (0)