Skip to main content

spru_bevy/client/
component.rs

1use std::{
2    collections::{HashMap, VecDeque, hash_map},
3    fmt, ops,
4};
5
6use bevy::prelude;
7use derive_where::derive_where;
8use spru::item;
9
10/// Specifies the Client the entity belongs to. This allows multiple Clients to co-exist
11/// inside the same World. Note that while this uses a [spru::player::Id] as the id,
12/// this does not mean the attached game piece belongs to that player, only that it
13/// is their 'view' of the game piece.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, prelude::Component)]
15#[component(storage = "SparseSet")]
16#[component(immutable)]
17pub struct ClientId(spru::player::Id);
18
19impl ClientId {
20    pub(crate) fn new(id: spru::player::Id) -> Self {
21        Self(id)
22    }
23}
24
25impl ops::Deref for ClientId {
26    type Target = spru::player::Id;
27
28    fn deref(&self) -> &Self::Target {
29        &self.0
30    }
31}
32
33impl fmt::Display for ClientId {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        fmt::Display::fmt(&self.0, f)
36    }
37}
38
39#[derive(Debug, prelude::Component)]
40#[component(storage = "SparseSet")]
41#[require(FromServer<Client>, ToServer<Client>, FromUser<Client>, EntityMap)]
42pub struct Runner<Client: super::ClientSSS> {
43    pub(crate) client: Client,
44}
45
46impl<Client: super::ClientSSS> Runner<Client> {
47    pub(crate) fn new(client: Client) -> Self {
48        Self { client }
49    }
50}
51
52#[derive_where(Debug; spru::common::signal::ToClient<Client::Common>)]
53#[derive_where(Default)]
54#[derive(prelude::Component)]
55#[component(storage = "SparseSet")]
56pub struct FromServer<Client: super::ClientSSS> {
57    queue: VecDeque<spru::common::signal::ToClient<Client::Common>>,
58}
59
60impl<Client: super::ClientSSS> FromServer<Client> {
61    pub fn len(&self) -> usize {
62        self.queue.len()
63    }
64
65    pub fn is_empty(&self) -> bool {
66        self.queue.is_empty()
67    }
68
69    pub(crate) fn enqueue(&mut self, signal: spru::common::signal::ToClient<Client::Common>) {
70        self.queue.push_back(signal);
71    }
72
73    pub fn dequeue(&mut self) -> Option<spru::common::signal::ToClient<Client::Common>> {
74        self.queue.pop_front()
75    }
76}
77
78#[derive_where(Debug; spru::common::signal::ToServer<Client::Common>)]
79#[derive_where(Default)]
80#[derive(prelude::Component)]
81#[component(storage = "SparseSet")]
82pub struct ToServer<Client: super::ClientSSS> {
83    queue: VecDeque<spru::common::signal::ToServer<Client::Common>>,
84}
85
86impl<Client: super::ClientSSS> ToServer<Client> {
87    pub fn len(&self) -> usize {
88        self.queue.len()
89    }
90
91    pub fn is_empty(&self) -> bool {
92        self.queue.is_empty()
93    }
94
95    pub fn enqueue(&mut self, signal: spru::common::signal::ToServer<Client::Common>) {
96        self.queue.push_back(signal);
97    }
98
99    pub(crate) fn dequeue(&mut self) -> Option<spru::common::signal::ToServer<Client::Common>> {
100        self.queue.pop_front()
101    }
102}
103
104#[derive_where(Debug; UserInput<Client>)]
105#[derive_where(Default)]
106#[derive(prelude::Component)]
107#[component(storage = "SparseSet")]
108pub struct FromUser<Client: super::ClientSSS> {
109    queue: VecDeque<UserInput<Client>>,
110}
111
112impl<Client: super::ClientSSS> FromUser<Client> {
113    pub fn len(&self) -> usize {
114        self.queue.len()
115    }
116
117    pub fn is_empty(&self) -> bool {
118        self.queue.is_empty()
119    }
120
121    pub fn stage_interaction(&mut self, interaction: Client::Interaction) {
122        self.queue
123            .push_back(UserInput::StageInteraction(interaction));
124    }
125
126    pub fn apply_interaction(&mut self, interaction_id: spru::interaction::Pending) {
127        self.queue
128            .push_back(UserInput::ApplyInteraction(Some(interaction_id)));
129    }
130
131    pub fn apply_all_interactions(&mut self) {
132        self.queue.push_back(UserInput::ApplyInteraction(None));
133    }
134
135    pub fn revert_interaction(&mut self, interaction_id: spru::interaction::Pending) {
136        self.queue
137            .push_back(UserInput::RevertInteraction(Some(interaction_id)));
138    }
139
140    pub fn revert_all_interactions(&mut self) {
141        self.queue.push_back(UserInput::RevertInteraction(None));
142    }
143
144    pub(crate) fn dequeue(&mut self) -> Option<UserInput<Client>> {
145        self.queue.pop_front()
146    }
147}
148
149#[allow(
150    clippy::enum_variant_names,
151    reason = "Future variants will not involve interactions"
152)]
153#[derive_where(Debug; Client::Interaction)]
154pub(crate) enum UserInput<Client: super::ClientSSS> {
155    StageInteraction(Client::Interaction),
156    ApplyInteraction(Option<spru::interaction::Pending>),
157    RevertInteraction(Option<spru::interaction::Pending>),
158}
159
160#[derive(Debug, prelude::Component)]
161pub struct Item<T: Send + Sync + 'static>(spru::Item<T>);
162
163impl<T: Send + Sync + 'static> Item<T> {
164    pub(crate) fn new(item: spru::Item<T>) -> Self {
165        Self(item)
166    }
167
168    pub(crate) fn item(&self) -> &spru::Item<T> {
169        &self.0
170    }
171
172    pub(crate) fn item_mut(&mut self) -> &mut spru::Item<T> {
173        &mut self.0
174    }
175
176    pub(crate) fn into_inner(self) -> spru::Item<T> {
177        self.0
178    }
179}
180
181impl<T: Send + Sync + 'static> ops::Deref for Item<T> {
182    type Target = T;
183
184    fn deref(&self) -> &Self::Target {
185        self.item()
186    }
187}
188
189#[derive(Debug, Default, prelude::Component)]
190#[component(storage = "SparseSet")]
191pub struct EntityMap {
192    map: HashMap<item::Id, prelude::Entity>,
193}
194
195impl EntityMap {
196    pub fn get<ID: Into<item::Id>>(&self, id: ID) -> super::BevyResult<prelude::Entity> {
197        let id = id.into();
198        self.map
199            .get(&id)
200            .copied()
201            .ok_or(super::BevyError::IdNotFound(id))
202    }
203
204    pub(crate) fn insert_as(
205        &mut self,
206        id: item::Id,
207        f: impl FnOnce() -> super::BevyResult<prelude::Entity>,
208    ) -> super::BevyResult<prelude::Entity> {
209        match self.map.entry(id) {
210            hash_map::Entry::Occupied(oe) => Err(super::BevyError::IdAlreadyExists(id, *oe.get())),
211            hash_map::Entry::Vacant(ve) => {
212                let entity = f()?;
213                ve.insert(entity);
214                Ok(entity)
215            }
216        }
217    }
218
219    pub(crate) fn remove_as<T>(
220        &mut self,
221        id: item::Id,
222        f: impl FnOnce(prelude::Entity) -> super::BevyResult<T>,
223    ) -> super::BevyResult<T> {
224        match self.map.entry(id) {
225            hash_map::Entry::Occupied(oe) => {
226                let value = f(*oe.get())?;
227                oe.remove();
228                Ok(value)
229            }
230            hash_map::Entry::Vacant(_) => Err(super::BevyError::IdNotFound(id)),
231        }
232    }
233}
234
235impl<ID: Into<item::Id>> ops::Index<ID> for EntityMap {
236    type Output = prelude::Entity;
237
238    fn index(&self, index: ID) -> &Self::Output {
239        &self.map[&index.into()]
240    }
241}