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
use crate::{ffi, flat, rlbot::RLBot};
use std::{
error::Error,
mem,
time::{Duration, Instant},
};
pub struct Physicist<'a> {
rlbot: &'a RLBot,
ratelimiter: ratelimit::Limiter,
prev_ball_frame: i32,
}
impl<'a> Physicist<'a> {
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
pub(crate) fn new(rlbot: &'a RLBot) -> Self {
let ratelimiter = ratelimit::Builder::new()
.interval(Duration::from_millis(1))
.build();
Self {
rlbot,
ratelimiter,
prev_ball_frame: 0,
}
}
#[allow(clippy::should_implement_trait)]
#[deprecated(
note = "the struct-based methods are deprecated; use the flatbuffer equivalents instead"
)]
#[allow(deprecated)]
pub fn next(&mut self) -> Result<ffi::RigidBodyTick, Box<dyn Error>> {
self.spin(|this| Ok(this.try_next()?), Self::DEFAULT_TIMEOUT)
}
#[deprecated(
note = "the struct-based methods are deprecated; use the flatbuffer equivalents instead"
)]
#[allow(deprecated)]
pub fn try_next(&mut self) -> Result<Option<ffi::RigidBodyTick>, Box<dyn Error>> {
let mut result = unsafe { mem::uninitialized() };
self.rlbot.interface().update_rigid_body_tick(&mut result)?;
if result.Ball.State.Frame != self.prev_ball_frame {
self.prev_ball_frame = result.Ball.State.Frame;
Ok(Some(result))
} else {
Ok(None)
}
}
pub fn next_flat<'fb>(&mut self) -> Result<flat::RigidBodyTick<'fb>, Box<dyn Error>> {
self.spin(|this| Ok(this.try_next_flat()), Self::DEFAULT_TIMEOUT)
}
pub fn next_flat_with_timeout<'fb>(
&mut self,
timeout: Duration,
) -> Result<flat::RigidBodyTick<'fb>, Box<dyn Error>> {
self.spin(|this| Ok(this.try_next_flat()), timeout)
}
#[allow(clippy::redundant_closure)]
pub fn try_next_flat<'fb>(&mut self) -> Option<flat::RigidBodyTick<'fb>> {
if let Some(tick) = self.rlbot.interface().update_rigid_body_tick_flatbuffer() {
let ball = tick.ball();
match ball.as_ref().and_then(|b| b.state()).map(|s| s.frame()) {
Some(ball_frame) if ball_frame != self.prev_ball_frame => {
self.prev_ball_frame = ball_frame;
return Some(tick);
}
_ => {}
}
}
None
}
fn spin<R>(
&mut self,
f: impl Fn(&mut Self) -> Result<Option<R>, Box<dyn Error>>,
timeout: Duration,
) -> 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 > timeout {
return Err(From::from("no physics tick received within the timeout"));
}
}
}
}