1pub mod prelude;
6
7use err_rs::{ErrorLevel, ErrorLevelProvider};
8use log::trace;
9use nimble_assent::{Assent, AssentCallback, UpdateState};
10use nimble_seer::{Seer, SeerCallback, SeerError};
11use std::fmt::{Debug, Display};
12use tick_id::TickId;
13use tick_queue::QueueError;
14
15#[derive(Debug)]
16pub enum RectifyError {
17 WrongTickId {
18 expected: TickId,
19 encountered: TickId,
20 },
21 SeerError(SeerError),
22 QueueError(QueueError),
23}
24
25impl From<SeerError> for RectifyError {
26 fn from(value: SeerError) -> Self {
27 Self::SeerError(value)
28 }
29}
30
31impl From<QueueError> for RectifyError {
32 fn from(value: QueueError) -> Self {
33 Self::QueueError(value)
34 }
35}
36
37impl ErrorLevelProvider for RectifyError {
38 fn error_level(&self) -> ErrorLevel {
39 match self {
40 Self::WrongTickId { .. } | Self::QueueError(_) => ErrorLevel::Critical,
41 Self::SeerError(err) => err.error_level(),
42 }
43 }
44}
45
46pub trait RectifyCallback {
48 fn on_copy_from_authoritative(&mut self);
49}
50
51pub trait RectifyCallbacks<StepT>:
52 AssentCallback<StepT> + SeerCallback<StepT> + RectifyCallback
53{
54}
55
56impl<T, StepT> RectifyCallbacks<StepT> for T where
57 T: AssentCallback<StepT> + SeerCallback<StepT> + RectifyCallback
58{
59}
60
61#[derive(Debug)]
64pub struct Rectify<Game: RectifyCallbacks<StepT>, StepT: Clone + Debug> {
65 assent: Assent<Game, StepT>,
66 seer: Seer<Game, StepT>,
67 settings: Settings,
68}
69
70impl<Game: RectifyCallbacks<StepT>, StepT: Clone + Debug + Display> Default
71 for Rectify<Game, StepT>
72{
73 fn default() -> Self {
74 Self::new(Settings::default())
75 }
76}
77
78#[derive(Default, Debug, Copy, Clone)]
79pub struct Settings {
80 pub assent: nimble_assent::Settings,
81 pub seer: nimble_seer::Settings,
82}
83
84impl<Game: RectifyCallbacks<StepT>, StepT: Clone + Debug + Display> Rectify<Game, StepT> {
85 #[must_use]
91 pub fn new(settings: Settings) -> Self {
92 let assent = Assent::new(settings.assent);
93 let seer = Seer::new(settings.seer);
94
95 Self {
96 assent,
97 seer,
98 settings,
99 }
100 }
101
102 #[must_use]
103 pub const fn seer(&self) -> &Seer<Game, StepT> {
104 &self.seer
105 }
106
107 #[must_use]
108 pub const fn settings(&self) -> Settings {
109 self.settings
110 }
111
112 #[must_use]
113 pub const fn assent(&self) -> &Assent<Game, StepT> {
114 &self.assent
115 }
116
117 pub fn push_predicted(&mut self, tick_id: TickId, step: StepT) -> Result<(), RectifyError> {
127 if let Some(end_tick_id) = self.assent.end_tick_id() {
128 self.seer.received_authoritative(end_tick_id);
129 }
130 trace!("added predicted step {}", &step);
131 self.seer.push(tick_id, step)?;
132 Ok(())
133 }
134
135 #[must_use]
136 pub const fn waiting_for_authoritative_tick_id(&self) -> TickId {
137 self.assent.next_expected_tick_id()
138 }
139
140 pub fn push_authoritatives_with_check(
145 &mut self,
146 step_for_tick_id: TickId,
147 steps: &[StepT],
148 ) -> Result<(), RectifyError> {
149 let mut current_tick = step_for_tick_id;
150 for step in steps {
151 self.push_authoritative_with_check(current_tick, step.clone())?;
152 current_tick = TickId(current_tick.0 + 1);
153 }
154
155 Ok(())
156 }
157 #[allow(clippy::missing_panics_doc)]
169 pub fn push_authoritative_with_check(
170 &mut self,
171 step_for_tick_id: TickId,
172 step: StepT,
173 ) -> Result<(), RectifyError> {
174 if let Some(end_tick_id) = self.assent.end_tick_id() {
175 if end_tick_id + 1 != step_for_tick_id {
176 Err(RectifyError::WrongTickId {
177 encountered: step_for_tick_id,
178 expected: end_tick_id + 1,
179 })?;
180 }
181 }
182 self.assent.push(step_for_tick_id, step)?;
183 self.seer.received_authoritative(
184 self.assent
185 .end_tick_id()
186 .expect("we know that there is an end tick, since we pushed to it previously"),
187 );
188
189 Ok(())
190 }
191
192 pub fn update(&mut self, game: &mut Game) {
198 let consumed_all_knowledge = self.assent.update(game);
199 if consumed_all_knowledge != UpdateState::DidNotConsumeAllKnowledge {
200 game.on_copy_from_authoritative();
201 self.seer.update(game);
202 }
203 }
204}