Repository navigation
Expand file tree
/
Copy pathpacket.rs
More file actions
118 lines (103 loc) · 3.92 KB
/
Copy pathpacket.rs
File metadata and controls
118 lines (103 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! A condensed port of the C++ library's example.cpp: a game packet with a unified serialize
//! function, measured, written, sent "over the network", and read back defensively.
use serialize::{MeasureStream, ReadStream, Result, Serialize, Stream, WriteStream};
const MAX_PLAYERS: usize = 16;
#[derive(Default, Debug, PartialEq)]
struct Player {
id: i32,
x: f32,
y: f32,
health: i32,
alive: bool,
}
impl Serialize for Player {
fn serialize<S: Stream>(&mut self, stream: &mut S) -> Result<(), S::Error> {
stream.serialize_int(&mut self.id, 0, 65535)?;
stream.serialize_compressed_float(&mut self.x, -1000.0, 1000.0, 0.01)?;
stream.serialize_compressed_float(&mut self.y, -1000.0, 1000.0, 0.01)?;
stream.serialize_int(&mut self.health, 0, 100)?;
stream.serialize_bool(&mut self.alive)?;
Ok(())
}
}
#[derive(Default, Debug, PartialEq)]
struct WorldStatePacket {
sequence: u16,
players: Vec<Player>,
map_name: String,
}
impl Serialize for WorldStatePacket {
fn serialize<S: Stream>(&mut self, stream: &mut S) -> Result<(), S::Error> {
stream.serialize_u16(&mut self.sequence)?;
// a serialized value that controls a loop must be validated before use: serialize_int
// bounds it to [0,MAX_PLAYERS] and ? aborts on failure, so a malicious packet can
// never drive this loop with garbage
let mut num_players = self.players.len() as i32;
stream.serialize_int(&mut num_players, 0, MAX_PLAYERS as i32)?;
if S::IS_READING {
self.players = (0..num_players).map(|_| Player::default()).collect();
}
for player in &mut self.players {
stream.serialize_object(player)?;
}
stream.serialize_string(&mut self.map_name, 64)?;
Ok(())
}
}
fn main() -> Result {
let mut packet = WorldStatePacket {
sequence: 1000,
players: vec![
Player {
id: 57,
x: 100.0,
y: 75.5,
health: 100,
alive: true,
},
Player {
id: 12,
x: -12.25,
y: 3.0,
health: 30,
alive: true,
},
Player {
id: 9,
x: 0.0,
y: 0.0,
health: 0,
alive: false,
},
],
map_name: "pressure".to_string(),
};
// measure how many bytes the packet needs (conservative: aligns count as 7 bits).
// measures cannot fail — the error type is uninhabited, so Ok is the only pattern
let mut measure = MeasureStream::new();
packet.serialize(&mut measure).unwrap();
println!("measured packet size: {} bytes", measure.bytes_processed());
// write the packet. writes cannot fail either: the writer is trusted
let mut buffer = [0u8; 256]; // multiple of 8 bytes, comfortably above the measure
let mut writer = WriteStream::new(&mut buffer);
packet.serialize(&mut writer).unwrap();
writer.flush();
let packet_bytes = writer.bytes_processed() as usize;
println!("wrote packet: {packet_bytes} bytes");
// ... the wire ...
// read it back. the buffer extends past the packet data, so reads stay on the fast path
let mut received = WorldStatePacket::default();
let mut reader = ReadStream::new(&buffer, packet_bytes);
received.serialize(&mut reader)?;
// a packet reader reads the end (STANDARD.md, format 2.0): a byte left over, or a set bit
// in the final byte's padding, means these are not the bytes the writer reported
reader.serialize_end()?;
assert_eq!(received.sequence, packet.sequence);
assert_eq!(received.players.len(), packet.players.len());
assert_eq!(received.map_name, packet.map_name);
println!(
"read packet back: sequence {} map {:?}",
received.sequence, received.map_name
);
Ok(())
}