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
145
146
147
148
149
150
151
152
153
154
155
use crate::{character::CharacterId, ViewDistances};
use serde::{Deserialize, Serialize};
use specs::Component;
use std::time::{Duration, Instant};
use vek::*;

#[derive(Debug)]
pub struct Presence {
    pub terrain_view_distance: ViewDistance,
    pub entity_view_distance: ViewDistance,
    /// If mutating this (or the adding/replacing the Presence component as a
    /// whole), make sure the mapping of `CharacterId` in `IdMaps` is
    /// updated!
    pub kind: PresenceKind,
    pub lossy_terrain_compression: bool,
}

impl Presence {
    pub fn new(view_distances: ViewDistances, kind: PresenceKind) -> Self {
        let now = Instant::now();
        Self {
            terrain_view_distance: ViewDistance::new(view_distances.terrain, now),
            entity_view_distance: ViewDistance::new(view_distances.entity, now),
            kind,
            lossy_terrain_compression: false,
        }
    }
}

impl Component for Presence {
    type Storage = specs::DenseVecStorage<Self>;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PresenceKind {
    Spectator,
    // Note: we don't know if this character ID is valid and associated with the player until the
    // character has loaded successfully. The ID should only be trusted and included in the
    // mapping when the variant is changed to `Character`.
    LoadingCharacter(CharacterId),
    Character(CharacterId),
    Possessor,
}

impl PresenceKind {
    /// Check if the presence represents a control of a character, and thus
    /// certain in-game messages from the client such as control inputs
    /// should be handled.
    pub fn controlling_char(&self) -> bool { matches!(self, Self::Character(_) | Self::Possessor) }

    pub fn character_id(&self) -> Option<CharacterId> {
        if let Self::Character(character_id) = self {
            Some(*character_id)
        } else {
            None
        }
    }

    /// Controls whether this entity is synced to other clients.
    ///
    /// Note, if it ends up being useful this could be generalized to an
    /// independent component that is required for any entity to be synced
    /// (as an independent component it could use NullStorage).
    pub fn sync_me(&self) -> bool {
        match self {
            Self::Spectator | Self::LoadingCharacter(_) => false,
            Self::Character(_) | Self::Possessor => true,
        }
    }
}

#[derive(PartialEq, Debug, Clone, Copy)]
enum Direction {
    Up,
    Down,
}

/// Distance from the [Presence] from which the world is loaded and information
/// is synced to clients.
///
/// We limit the frequency that changes in the view distance change direction
/// (e.g. shifting from increasing the value to decreasing it). This is useful
/// since we want to avoid rapid cycles of shrinking and expanding of the view
/// distance.
#[derive(Debug, Clone, Copy)]
pub struct ViewDistance {
    direction: Direction,
    last_direction_change_time: Instant,
    target: Option<u32>,
    current: u32,
}

impl ViewDistance {
    /// Minimum time allowed between changes in direction of value adjustments.
    const TIME_PER_DIR_CHANGE: Duration = Duration::from_millis(300);

    pub fn new(start_value: u32, now: Instant) -> Self {
        Self {
            direction: Direction::Up,
            last_direction_change_time: now.checked_sub(Self::TIME_PER_DIR_CHANGE).unwrap_or(now),
            target: None,
            current: start_value,
        }
    }

    /// Returns the current value.
    pub fn current(&self) -> u32 { self.current }

    /// Applies deferred change based on the whether the time to apply it has
    /// been reached.
    pub fn update(&mut self, now: Instant) {
        if let Some(target_val) = self.target {
            if now.saturating_duration_since(self.last_direction_change_time)
                > Self::TIME_PER_DIR_CHANGE
            {
                self.last_direction_change_time = now;
                self.current = target_val;
                self.target = None;
            }
        }
    }

    /// Sets the target value.
    ///
    /// If this hasn't been changed recently or it is in the same direction as
    /// the previous change it will be applied immediately. Otherwise, it
    /// will be deferred to a later time (limiting the frequency of changes
    /// in the change direction).
    pub fn set_target(&mut self, new_target: u32, now: Instant) {
        use core::cmp::Ordering;
        let new_direction = match new_target.cmp(&self.current) {
            Ordering::Equal => return, // No change needed.
            Ordering::Less => Direction::Down,
            Ordering::Greater => Direction::Up,
        };

        // Change is in the same direction as before so we can just apply it.
        if new_direction == self.direction {
            self.current = new_target;
            self.target = None;
        // If it has already been a while since the last direction change we can
        // directly apply the request and switch the direction.
        } else if now.saturating_duration_since(self.last_direction_change_time)
            > Self::TIME_PER_DIR_CHANGE
        {
            self.direction = new_direction;
            self.last_direction_change_time = now;
            self.current = new_target;
            self.target = None;
        // Otherwise, we need to defer the request.
        } else {
            self.target = Some(new_target);
        }
    }
}