Skip to content

Commit 7bb0bde

Browse files
ShaturUkoeHB
andauthored
Clients visibility (#174)
Co-authored-by: UkoeHB <[email protected]>
1 parent 316dc3c commit 7bb0bde

File tree

7 files changed

+1080
-38
lines changed

7 files changed

+1080
-38
lines changed

src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,9 @@ pub mod prelude {
432432
NetworkChannels, ReplicationChannel, RepliconCorePlugin,
433433
},
434434
server::{
435-
clients_info::ClientsInfo, has_authority, ClientEntityMap, ClientMapping, ServerPlugin,
436-
ServerSet, TickPolicy, SERVER_ID,
435+
clients_info::{client_visibility::ClientVisibility, ClientInfo, ClientsInfo},
436+
has_authority, ClientEntityMap, ClientMapping, ServerPlugin, ServerSet, TickPolicy,
437+
VisibilityPolicy, SERVER_ID,
437438
},
438439
ReplicationPlugins,
439440
};

src/network_event/server_event.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,7 @@ pub fn send_with<T>(
323323
}
324324
SendMode::Direct(client_id) => {
325325
if client_id != SERVER_ID {
326-
if let Some(client_info) = clients_info
327-
.iter()
328-
.find(|client_info| client_info.id() == client_id)
329-
{
326+
if let Some(client_info) = clients_info.get_client(client_id) {
330327
let message = serialize_with(client_info, None, &serialize_fn)?;
331328
server.send_message(client_info.id(), channel, message.bytes);
332329
}

src/server.rs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use bevy_renet::{
2727
use crate::replicon_core::{
2828
replication_rules::ReplicationRules, replicon_tick::RepliconTick, ReplicationChannel,
2929
};
30-
use clients_info::{ClientBuffers, ClientInfo, ClientsInfo};
30+
use clients_info::{client_visibility::Visibility, ClientBuffers, ClientInfo, ClientsInfo};
3131
use despawn_buffer::{DespawnBuffer, DespawnBufferPlugin};
3232
use removal_buffer::{RemovalBuffer, RemovalBufferPlugin};
3333
use replicated_archetypes_info::ReplicatedArchetypesInfo;
@@ -39,6 +39,9 @@ pub struct ServerPlugin {
3939
/// Tick configuration.
4040
pub tick_policy: TickPolicy,
4141

42+
/// Visibility configuration.
43+
pub visibility_policy: VisibilityPolicy,
44+
4245
/// The time after which updates will be considered lost if an acknowledgment is not received for them.
4346
///
4447
/// In practice updates will live at least `update_timeout`, and at most `2*update_timeout`.
@@ -49,6 +52,7 @@ impl Default for ServerPlugin {
4952
fn default() -> Self {
5053
Self {
5154
tick_policy: TickPolicy::MaxTickRate(30),
55+
visibility_policy: Default::default(),
5256
update_timeout: Duration::from_secs(10),
5357
}
5458
}
@@ -62,9 +66,9 @@ impl Plugin for ServerPlugin {
6266
RenetServerPlugin,
6367
NetcodeServerPlugin,
6468
))
65-
.init_resource::<ClientsInfo>()
6669
.init_resource::<ClientBuffers>()
6770
.init_resource::<ClientEntityMap>()
71+
.insert_resource(ClientsInfo::new(self.visibility_policy))
6872
.configure_sets(PreUpdate, ServerSet::Receive.after(RenetReceive))
6973
.configure_sets(PostUpdate, ServerSet::Send.before(RenetSend))
7074
.add_systems(
@@ -291,9 +295,12 @@ fn collect_changes(
291295
};
292296

293297
for entity in archetype.entities() {
294-
for (init_message, update_message) in messages.iter_mut() {
298+
for (init_message, update_message, client_info) in messages.iter_mut_with_info() {
295299
init_message.start_entity_data(entity.entity());
296300
update_message.start_entity_data(entity.entity());
301+
client_info
302+
.visibility_mut()
303+
.cache_visibility(entity.entity());
297304
}
298305

299306
// SAFETY: all replicated archetypes have marker component with table storage.
@@ -326,7 +333,12 @@ fn collect_changes(
326333

327334
let mut shared_bytes = None;
328335
for (init_message, update_message, client_info) in messages.iter_mut_with_info() {
329-
let new_entity = marker_added || client_info.just_connected;
336+
let visibility = client_info.visibility().cached_visibility();
337+
if visibility == Visibility::Hidden {
338+
continue;
339+
}
340+
341+
let new_entity = marker_added || visibility == Visibility::Gained;
330342
if new_entity || ticks.is_added(change_tick.last_run(), change_tick.this_run())
331343
{
332344
init_message.write_component(
@@ -352,7 +364,12 @@ fn collect_changes(
352364
}
353365

354366
for (init_message, update_message, client_info) in messages.iter_mut_with_info() {
355-
let new_entity = marker_added || client_info.just_connected;
367+
let visibility = client_info.visibility().cached_visibility();
368+
if visibility == Visibility::Hidden {
369+
continue;
370+
}
371+
372+
let new_entity = marker_added || visibility == Visibility::Gained;
356373
if new_entity || init_message.entity_data_size() != 0 {
357374
// If there is any insertion or we must initialize, include all updates into init message
358375
// and bump the last acknowledged tick to keep entity updates atomic.
@@ -367,8 +384,7 @@ fn collect_changes(
367384
}
368385
}
369386

370-
for (init_message, _, client_info) in messages.iter_mut_with_info() {
371-
client_info.just_connected = false;
387+
for (init_message, _) in messages.iter_mut() {
372388
init_message.end_array()?;
373389
}
374390

@@ -422,7 +438,11 @@ fn collect_despawns(
422438
}
423439
}
424440

425-
for (message, _) in messages.iter_mut() {
441+
for (message, _, client_info) in messages.iter_mut_with_info() {
442+
for entity in client_info.drain_lost_visibility() {
443+
message.write_entity(&mut None, entity)?;
444+
}
445+
426446
message.end_array()?;
427447
}
428448

@@ -496,6 +516,18 @@ pub enum TickPolicy {
496516
Manual,
497517
}
498518

519+
/// Controls how visibility will be managed via [`ClientVisibility`](clients_info::client_visibility::ClientVisibility).
520+
#[derive(Default, Debug, Clone, Copy)]
521+
pub enum VisibilityPolicy {
522+
/// All entities are visible by default and visibility can't be changed.
523+
#[default]
524+
All,
525+
/// All entities are hidden by default and should be explicitly registered to be visible.
526+
Blacklist,
527+
/// All entities are visible by default and should be explicitly registered to be hidden.
528+
Whitelist,
529+
}
530+
499531
/**
500532
A resource that exists on the server for mapping server entities to
501533
entities that clients have already spawned. The mappings are sent to clients as part of replication

src/server/clients_info.rs

Lines changed: 104 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
pub mod client_visibility;
2+
13
use std::mem;
24

35
use bevy::{
@@ -7,11 +9,15 @@ use bevy::{
79
};
810
use bevy_renet::renet::ClientId;
911

10-
use crate::replicon_core::replicon_tick::RepliconTick;
12+
use crate::{replicon_core::replicon_tick::RepliconTick, server::VisibilityPolicy};
13+
use client_visibility::ClientVisibility;
1114

1215
/// Stores meta-information about connected clients.
13-
#[derive(Default, Resource)]
14-
pub struct ClientsInfo(Vec<ClientInfo>);
16+
#[derive(Resource, Default)]
17+
pub struct ClientsInfo {
18+
info: Vec<ClientInfo>,
19+
policy: VisibilityPolicy,
20+
}
1521

1622
/// Reusable buffers for [`ClientsInfo`] and [`ClientInfo`].
1723
#[derive(Default, Resource)]
@@ -28,19 +34,73 @@ pub(crate) struct ClientBuffers {
2834
}
2935

3036
impl ClientsInfo {
31-
/// Returns an iterator over clients information.
32-
pub(crate) fn iter(&self) -> impl Iterator<Item = &ClientInfo> {
33-
self.0.iter()
37+
pub(super) fn new(policy: VisibilityPolicy) -> Self {
38+
Self {
39+
info: Default::default(),
40+
policy,
41+
}
42+
}
43+
44+
/// Returns a reference to a connected client's info.
45+
///
46+
/// This operation is *O*(*n*).
47+
/// See also [`Self::get_client`] for the fallible version.
48+
///
49+
/// # Panics
50+
///
51+
/// Panics if the passed client ID is not connected.
52+
pub fn client(&self, client_id: ClientId) -> &ClientInfo {
53+
self.get_client(client_id)
54+
.unwrap_or_else(|| panic!("{client_id:?} should be connected"))
3455
}
3556

36-
/// Returns a mutable iterator over clients information.
37-
pub(super) fn iter_mut(&mut self) -> impl Iterator<Item = &mut ClientInfo> {
38-
self.0.iter_mut()
57+
/// Returns a mutable reference to a connected client's info.
58+
///
59+
/// This operation is *O*(*n*).
60+
/// See also [`Self::get_client_mut`] for the fallible version.
61+
///
62+
/// # Panics
63+
///
64+
/// Panics if the passed client ID is not connected.
65+
pub fn client_mut(&mut self, client_id: ClientId) -> &mut ClientInfo {
66+
self.get_client_mut(client_id)
67+
.unwrap_or_else(|| panic!("{client_id:?} should be connected"))
3968
}
4069

41-
/// Returns number of connected clients.
42-
pub(super) fn len(&self) -> usize {
43-
self.0.len()
70+
/// Returns a reference to a connected client's info.
71+
///
72+
/// This operation is *O*(*n*).
73+
/// See also [`Self::client`] for the panicking version.
74+
pub fn get_client(&self, client_id: ClientId) -> Option<&ClientInfo> {
75+
self.info.iter().find(|info| info.id == client_id)
76+
}
77+
78+
/// Returns a mutable reference to a connected client's info.
79+
///
80+
/// This operation is *O*(*n*).
81+
/// See also [`Self::client`] for the panicking version.
82+
pub fn get_client_mut(&mut self, client_id: ClientId) -> Option<&mut ClientInfo> {
83+
self.info.iter_mut().find(|info| info.id == client_id)
84+
}
85+
86+
/// Returns an iterator over client information.
87+
pub fn iter(&self) -> impl Iterator<Item = &ClientInfo> {
88+
self.info.iter()
89+
}
90+
91+
/// Returns a mutable iterator over client information.
92+
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut ClientInfo> {
93+
self.info.iter_mut()
94+
}
95+
96+
/// Returns the number of connected clients.
97+
pub fn len(&self) -> usize {
98+
self.info.len()
99+
}
100+
101+
/// Returns `true` if no clients are connected.
102+
pub fn is_empty(&self) -> bool {
103+
self.info.is_empty()
44104
}
45105

46106
/// Initializes a new [`ClientInfo`] for this client.
@@ -51,22 +111,22 @@ impl ClientsInfo {
51111
client_info.reset(client_id);
52112
client_info
53113
} else {
54-
ClientInfo::new(client_id)
114+
ClientInfo::new(client_id, self.policy)
55115
};
56116

57-
self.0.push(client_info);
117+
self.info.push(client_info);
58118
}
59119

60120
/// Removes info for the client.
61121
///
62122
/// Keeps allocated memory in the buffers for reuse.
63123
pub(super) fn remove(&mut self, client_buffers: &mut ClientBuffers, client_id: ClientId) {
64124
let index = self
65-
.0
125+
.info
66126
.iter()
67127
.position(|info| info.id == client_id)
68128
.expect("clients info should contain all connected clients");
69-
let mut client_info = self.0.remove(index);
129+
let mut client_info = self.info.remove(index);
70130
client_buffers.entities.extend(client_info.drain_entities());
71131
client_buffers.info.push(client_info);
72132
}
@@ -75,23 +135,23 @@ impl ClientsInfo {
75135
///
76136
/// Keeps allocated memory in the buffers for reuse.
77137
pub(super) fn clear(&mut self, client_buffers: &mut ClientBuffers) {
78-
for mut client_info in self.0.drain(..) {
138+
for mut client_info in self.info.drain(..) {
79139
client_buffers.entities.extend(client_info.drain_entities());
80140
client_buffers.info.push(client_info);
81141
}
82142
}
83143
}
84144

85-
pub(crate) struct ClientInfo {
145+
pub struct ClientInfo {
86146
/// Client's ID.
87147
id: ClientId,
88148

89-
/// Indicates whether the client connected in this tick.
90-
pub(super) just_connected: bool,
91-
92149
/// Lowest tick for use in change detection for each entity.
93150
ticks: EntityHashMap<Entity, Tick>,
94151

152+
/// Entity visibility settings.
153+
visibility: ClientVisibility,
154+
95155
/// The last tick in which a replicated entity was spawned, despawned, or gained/lost a component from the perspective
96156
/// of the client.
97157
///
@@ -108,11 +168,11 @@ pub(crate) struct ClientInfo {
108168
}
109169

110170
impl ClientInfo {
111-
fn new(id: ClientId) -> Self {
171+
fn new(id: ClientId, policy: VisibilityPolicy) -> Self {
112172
Self {
113173
id,
114-
just_connected: true,
115174
ticks: Default::default(),
175+
visibility: ClientVisibility::new(policy),
116176
change_tick: Default::default(),
117177
updates: Default::default(),
118178
next_update_index: Default::default(),
@@ -124,6 +184,16 @@ impl ClientInfo {
124184
self.id
125185
}
126186

187+
/// Returns a reference to the client's visibility settings.
188+
pub fn visibility(&self) -> &ClientVisibility {
189+
&self.visibility
190+
}
191+
192+
/// Returns a mutable reference to the client's visibility settings.
193+
pub fn visibility_mut(&mut self) -> &mut ClientVisibility {
194+
&mut self.visibility
195+
}
196+
127197
/// Clears all entities for unacknowledged updates, returning them as an iterator.
128198
///
129199
/// Keeps the allocated memory for reuse.
@@ -138,7 +208,7 @@ impl ClientInfo {
138208
/// Keeps the allocated memory for reuse.
139209
fn reset(&mut self, id: ClientId) {
140210
self.id = id;
141-
self.just_connected = true;
211+
self.visibility.clear();
142212
self.ticks.clear();
143213
self.updates.clear();
144214
self.next_update_index = 0;
@@ -229,10 +299,20 @@ impl ClientInfo {
229299
/// Removes a despawned entity tracked by this client.
230300
pub fn remove_despawned(&mut self, entity: Entity) {
231301
self.ticks.remove(&entity);
302+
self.visibility.remove_despawned(entity);
232303
// We don't clean up `self.updates` for efficiency reasons.
233304
// `Self::acknowledge()` will properly ignore despawned entities.
234305
}
235306

307+
/// Drains all entities for which visibility was lost during this tick.
308+
///
309+
/// Internal cleanup happens lazily during the iteration.
310+
pub(super) fn drain_lost_visibility(&mut self) -> impl Iterator<Item = Entity> + '_ {
311+
self.visibility.drain_lost_visibility().inspect(|entity| {
312+
self.ticks.remove(entity);
313+
})
314+
}
315+
236316
/// Removes all updates older then `min_timestamp`.
237317
///
238318
/// Keeps allocated memory in the buffers for reuse.

0 commit comments

Comments
 (0)