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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#![allow(clippy::float_cmp)]
use crate::{
ffi, flat, game::GameTickPacket, game_deserialize::deserialize_game_tick_packet, rlbot::RLBot,
};
use std::{
error::Error,
mem,
time::{Duration, Instant},
};
/// An iterator-like object that yields packets from the game as they occur.
pub struct Packeteer<'a> {
rlbot: &'a RLBot,
ratelimiter: ratelimit::Limiter,
prev_game_time: f32,
}
impl<'a> Packeteer<'a> {
pub(crate) fn new(rlbot: &'a RLBot) -> Self {
// The goal is never to miss any packets. But if we poll too often, the
// game crashes, so it's a fine line. With an interval of 3ms we can
// catch 333 updates per second. That should be plenty.
let ratelimiter = ratelimit::Builder::new()
.interval(Duration::from_millis(3))
.build();
Self {
rlbot,
ratelimiter,
prev_game_time: 0.0,
}
}
/// Blocks until we receive the next unique [`GameTickPacket`], and then
/// returns it.
///
/// # Errors
///
/// This function returns an error if ten seconds pass without a new
/// packet being received. The assumption is that the game froze or
/// crashed, and waiting longer will not help.
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<GameTickPacket, Box<dyn Error>> {
self.next_flatbuffer().map(deserialize_game_tick_packet)
}
/// Polls for the next unique [`GameTickPacket`].
///
/// If there is a packet that is newer than the previous packet, it is
/// returned. Otherwise, `None` is returned.
pub fn try_next(&mut self) -> Option<GameTickPacket> {
self.try_next_flat().map(deserialize_game_tick_packet)
}
/// Blocks until we receive the next unique [`ffi::LiveDataPacket`], and
/// then returns it.
///
/// # Errors
///
/// This function returns an error if ten seconds pass without a new
/// packet being received. The assumption is that the game froze or
/// crashed, and waiting longer will not help.
#[deprecated(
note = "the struct-based methods are deprecated; use the flatbuffer equivalents instead"
)]
#[allow(deprecated)]
pub fn next_ffi(&mut self) -> Result<ffi::LiveDataPacket, Box<dyn Error>> {
self.spin(Self::try_next_ffi)
}
/// Polls for the next unique [`ffi::LiveDataPacket`].
///
/// If there is a packet that is newer than the previous packet, it is
/// returned. Otherwise, `None` is returned.
#[deprecated(
note = "the struct-based methods are deprecated; use the flatbuffer equivalents instead"
)]
#[allow(deprecated)]
pub fn try_next_ffi(&mut self) -> Result<Option<ffi::LiveDataPacket>, Box<dyn Error>> {
let mut packet = unsafe { mem::uninitialized() };
self.rlbot
.interface()
.update_live_data_packet(&mut packet)?;
let game_time = packet.GameInfo.TimeSeconds;
if game_time != self.prev_game_time {
self.prev_game_time = game_time;
Ok(Some(packet))
} else {
Ok(None)
}
}
/// Blocks until we receive the next unique [`flat::GameTickPacket`], and
/// then returns it.
///
/// # Errors
///
/// This function returns an error if ten seconds pass without a new
/// packet being received. The assumption is that the game froze or
/// crashed, and waiting longer will not help.
pub fn next_flatbuffer<'fb>(&mut self) -> Result<flat::GameTickPacket<'fb>, Box<dyn Error>> {
self.spin(|this| Ok(this.try_next_flat()))
}
/// Polls for the next unique [`flat::GameTickPacket`].
///
/// If there is a packet that is newer than the previous packet, it is
/// returned. Otherwise, `None` is returned.
pub fn try_next_flat<'fb>(&mut self) -> Option<flat::GameTickPacket<'fb>> {
if let Some(packet) = self.rlbot.interface().update_live_data_packet_flatbuffer() {
let game_time = packet.gameInfo().map(|gi| gi.secondsElapsed());
if let Some(game_time) = game_time {
if game_time != self.prev_game_time {
self.prev_game_time = game_time;
return Some(packet);
}
}
}
None
}
/// Keep trying `f` until the timeout elapses.
fn spin<R>(
&mut self,
f: impl Fn(&mut Self) -> Result<Option<R>, Box<dyn Error>>,
) -> Result<R, Box<dyn Error>> {
let start = Instant::now();
loop {
self.ratelimiter.wait();
if let Some(tick) = f(self)? {
return Ok(tick);
}
let elapsed = Instant::now() - start;
if elapsed > Duration::from_secs(10) {
return Err(From::from("no physics tick received after ten seconds"));
}
}
}
}