veloren_common/comp/
presence.rs

1use crate::{ViewDistances, character::CharacterId};
2use serde::{Deserialize, Serialize};
3use specs::Component;
4use std::time::{Duration, Instant};
5use vek::*;
6
7#[derive(Debug)]
8pub struct Presence {
9    pub terrain_view_distance: ViewDistance,
10    pub entity_view_distance: ViewDistance,
11    /// If mutating this (or the adding/replacing the Presence component as a
12    /// whole), make sure the mapping of `CharacterId` in `IdMaps` is
13    /// updated!
14    pub kind: PresenceKind,
15    pub lossy_terrain_compression: bool,
16}
17
18impl Presence {
19    pub fn new(view_distances: ViewDistances, kind: PresenceKind) -> Self {
20        let now = Instant::now();
21        Self {
22            terrain_view_distance: ViewDistance::new(view_distances.terrain, now),
23            entity_view_distance: ViewDistance::new(view_distances.entity, now),
24            kind,
25            lossy_terrain_compression: false,
26        }
27    }
28}
29
30impl Component for Presence {
31    type Storage = specs::DenseVecStorage<Self>;
32}
33
34#[derive(Debug, Clone, Copy)]
35/// The entity the player is currently spectating.
36pub struct SpectatingEntity(pub specs::Entity);
37
38impl Component for SpectatingEntity {
39    type Storage = specs::DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43pub enum PresenceKind {
44    Spectator,
45    // Note: we don't know if this character ID is valid and associated with the player until the
46    // character has loaded successfully. The ID should only be trusted and included in the
47    // mapping when the variant is changed to `Character`.
48    LoadingCharacter(CharacterId),
49    Character(CharacterId),
50    Possessor,
51}
52
53impl PresenceKind {
54    /// Check if the presence represents a control of a character, and thus
55    /// certain in-game messages from the client such as control inputs
56    /// should be handled.
57    pub fn controlling_char(&self) -> bool { matches!(self, Self::Character(_) | Self::Possessor) }
58
59    pub fn character_id(&self) -> Option<CharacterId> {
60        if let Self::Character(character_id) = self {
61            Some(*character_id)
62        } else {
63            None
64        }
65    }
66
67    /// Controls whether this entity is synced to other clients.
68    ///
69    /// Note, if it ends up being useful this could be generalized to an
70    /// independent component that is required for any entity to be synced
71    /// (as an independent component it could use NullStorage).
72    pub fn sync_me(&self) -> bool {
73        match self {
74            Self::Spectator | Self::LoadingCharacter(_) => false,
75            Self::Character(_) | Self::Possessor => true,
76        }
77    }
78}
79
80#[derive(PartialEq, Debug, Clone, Copy)]
81enum Direction {
82    Up,
83    Down,
84}
85
86/// Distance from the [Presence] from which the world is loaded and information
87/// is synced to clients.
88///
89/// We limit the frequency that changes in the view distance change direction
90/// (e.g. shifting from increasing the value to decreasing it). This is useful
91/// since we want to avoid rapid cycles of shrinking and expanding of the view
92/// distance.
93#[derive(Debug, Clone, Copy)]
94pub struct ViewDistance {
95    direction: Direction,
96    last_direction_change_time: Instant,
97    target: Option<u32>,
98    current: u32,
99}
100
101impl ViewDistance {
102    /// Minimum time allowed between changes in direction of value adjustments.
103    const TIME_PER_DIR_CHANGE: Duration = Duration::from_millis(300);
104
105    pub fn new(start_value: u32, now: Instant) -> Self {
106        Self {
107            direction: Direction::Up,
108            last_direction_change_time: now.checked_sub(Self::TIME_PER_DIR_CHANGE).unwrap_or(now),
109            target: None,
110            current: start_value,
111        }
112    }
113
114    /// Returns the current value.
115    pub fn current(&self) -> u32 { self.current }
116
117    /// Applies deferred change based on the whether the time to apply it has
118    /// been reached.
119    pub fn update(&mut self, now: Instant) {
120        if let Some(target_val) = self.target {
121            if now.saturating_duration_since(self.last_direction_change_time)
122                > Self::TIME_PER_DIR_CHANGE
123            {
124                self.last_direction_change_time = now;
125                self.current = target_val;
126                self.target = None;
127            }
128        }
129    }
130
131    /// Sets the target value.
132    ///
133    /// If this hasn't been changed recently or it is in the same direction as
134    /// the previous change it will be applied immediately. Otherwise, it
135    /// will be deferred to a later time (limiting the frequency of changes
136    /// in the change direction).
137    pub fn set_target(&mut self, new_target: u32, now: Instant) {
138        use core::cmp::Ordering;
139        let new_direction = match new_target.cmp(&self.current) {
140            Ordering::Equal => return, // No change needed.
141            Ordering::Less => Direction::Down,
142            Ordering::Greater => Direction::Up,
143        };
144
145        // Change is in the same direction as before so we can just apply it.
146        if new_direction == self.direction {
147            self.current = new_target;
148            self.target = None;
149        // If it has already been a while since the last direction change we can
150        // directly apply the request and switch the direction.
151        } else if now.saturating_duration_since(self.last_direction_change_time)
152            > Self::TIME_PER_DIR_CHANGE
153        {
154            self.direction = new_direction;
155            self.last_direction_change_time = now;
156            self.current = new_target;
157            self.target = None;
158        // Otherwise, we need to defer the request.
159        } else {
160            self.target = Some(new_target);
161        }
162    }
163}