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
/*
 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/nimble-rust/nimble
 * Licensed under the MIT License. See LICENSE in the project root for license information.
 */
pub mod prelude;

use nimble_assent::{Assent, AssentCallback, UpdateState};
use nimble_seer::{Seer, SeerCallback};
use tick_id::TickId;

/// A callback trait that allows a game to handle the event when the authoritative state
pub trait RectifyCallback {
    fn on_copy_from_authoritative(&mut self);
}

/// The `Rectify` struct coordinates between the [`Assent`] and [`Seer`] components, managing
/// authoritative and predicted game states.
#[derive(Debug)]
pub struct Rectify<
    Game: AssentCallback<StepT> + SeerCallback<StepT> + RectifyCallback,
    StepT: Clone,
> {
    assent: Assent<Game, StepT>,
    seer: Seer<Game, StepT>,
}

impl<Game: AssentCallback<StepT> + SeerCallback<StepT> + RectifyCallback, StepT: Clone> Default
    for Rectify<Game, StepT>
{
    fn default() -> Self {
        Self::new()
    }
}

impl<
        Game: AssentCallback<StepT> + SeerCallback<StepT> + RectifyCallback,
        StepT: std::clone::Clone,
    > Rectify<Game, StepT>
{
    /// Creates a new `Rectify` instance, initializing both [`Assent`] and [`Seer`] components.
    ///
    /// # Returns
    ///
    /// A new `Rectify` instance.
    pub fn new() -> Self {
        let assent = Assent::new();
        let seer = Seer::new();

        Self { assent, seer }
    }

    pub fn seer(&self) -> &Seer<Game, StepT> {
        &self.seer
    }

    pub fn assent(&self) -> &Assent<Game, StepT> {
        &self.assent
    }

    /// Pushes a predicted step into the [`Seer`] component.
    ///
    /// # Arguments
    ///
    /// * `step` - The predicted step to be pushed.
    pub fn push_predicted(&mut self, step: StepT) {
        if let Some(end_tick_id) = self.assent.end_tick_id() {
            self.seer.received_authoritative(end_tick_id);
        }
        self.seer.push(step)
    }

    pub fn waiting_for_authoritative_tick_id(&self) -> Option<TickId> {
        self.assent.end_tick_id().map(|end_tick_id| end_tick_id + 1)
    }

    /// Pushes an authoritative step into the [`Assent`] component. This method is used to
    /// add new steps that have been determined by the authoritative host.
    ///
    /// # Arguments
    ///
    /// * `step` - The authoritative step to be pushed.
    pub fn push_authoritative(&mut self, step: StepT) {
        self.assent.push(step);
        self.seer
            .received_authoritative(self.assent.end_tick_id().unwrap());
    }

    /// Pushes an authoritative step into the [`Assent`] component. This method is used to
    /// add new steps that have been determined by the authoritative host.
    ///
    /// # Arguments
    ///
    /// * `step` - The authoritative step to be pushed.
    pub fn push_authoritative_with_check(
        &mut self,
        step_for_tick_id: TickId,
        step: StepT,
    ) -> Result<(), String> {
        if let Some(end_tick_id) = self.assent.end_tick_id() {
            if end_tick_id + 1 != step_for_tick_id {
                Err(format!(
                    "encountered {} but expected {}",
                    step_for_tick_id,
                    end_tick_id + 1
                ))?;
            }
        }
        self.assent.push(step);
        self.seer
            .received_authoritative(self.assent.end_tick_id().unwrap());

        Ok(())
    }

    /// Updates the authoritative state. If all the authoritative state has been calculated
    /// it predicts from the last authoritative state.
    /// # Arguments
    ///
    /// * `game` - A mutable reference to the game implementing the necessary callback traits.
    pub fn update(&mut self, game: &mut Game) {
        let consumed_all_knowledge = self.assent.update(game);
        if consumed_all_knowledge == UpdateState::ConsumedAllKnowledge {
            game.on_copy_from_authoritative();
        }

        self.seer.update(game);
    }
}