naia_shared/messages/channels/channel.rs
1use crate::named::Named;
2
3/// Marker trait for types that represent a named communication channel.
4pub trait Channel: Named + 'static {}
5
6/// Configuration for a channel: delivery mode, traffic direction, and priority criticality.
7#[derive(Clone)]
8pub struct ChannelSettings {
9 /// Delivery semantics (ordered/unordered, reliable/unreliable, or tick-buffered).
10 pub mode: ChannelMode,
11 /// Which endpoint(s) may send on this channel.
12 pub direction: ChannelDirection,
13 /// Priority tier used by the unified priority-sort send loop. Contributes
14 /// `base_gain()` per tick of message age to each message's on-the-fly
15 /// accumulator. Defaults via `ChannelCriticality::default_for(&mode)`.
16 pub criticality: ChannelCriticality,
17}
18
19impl ChannelSettings {
20 /// Creates a `ChannelSettings` with the given mode and direction, deriving default criticality from the mode.
21 pub fn new(mode: ChannelMode, direction: ChannelDirection) -> Self {
22 if mode.tick_buffered() && direction != ChannelDirection::ClientToServer {
23 panic!("TickBuffered Messages are only allowed to be sent from Client to Server");
24 }
25
26 let criticality = ChannelCriticality::default_for(&mode);
27 Self {
28 mode,
29 direction,
30 criticality,
31 }
32 }
33
34 /// Override the channel's priority tier. Builder-style.
35 pub fn with_criticality(mut self, criticality: ChannelCriticality) -> Self {
36 self.criticality = criticality;
37 self
38 }
39
40 /// Returns `true` if this channel guarantees delivery (all reliable modes).
41 pub fn reliable(&self) -> bool {
42 match &self.mode {
43 ChannelMode::UnorderedUnreliable => false,
44 ChannelMode::SequencedUnreliable => false,
45 ChannelMode::UnorderedReliable(_) => true,
46 ChannelMode::SequencedReliable(_) => true,
47 ChannelMode::OrderedReliable(_) => true,
48 ChannelMode::TickBuffered(_) => false,
49 }
50 }
51
52 /// Returns `true` if this channel uses tick-buffered delivery.
53 pub fn tick_buffered(&self) -> bool {
54 self.mode.tick_buffered()
55 }
56
57 /// Returns `true` if the client may send on this channel.
58 pub fn can_send_to_server(&self) -> bool {
59 match &self.direction {
60 ChannelDirection::ClientToServer => true,
61 ChannelDirection::ServerToClient => false,
62 ChannelDirection::Bidirectional => true,
63 }
64 }
65
66 /// Returns `true` if the server may send on this channel.
67 pub fn can_send_to_client(&self) -> bool {
68 match &self.direction {
69 ChannelDirection::ClientToServer => false,
70 ChannelDirection::ServerToClient => true,
71 ChannelDirection::Bidirectional => true,
72 }
73 }
74
75 /// Returns `true` if this channel supports bidirectional reliable request/response messaging.
76 pub fn can_request_and_respond(&self) -> bool {
77 self.reliable() && self.can_send_to_server() && self.can_send_to_client()
78 }
79}
80
81/// Tuning parameters for reliable channel delivery and backpressure.
82#[derive(Clone)]
83pub struct ReliableSettings {
84 /// Multiplier on the current RTT that sets the retransmit timeout.
85 pub rtt_resend_factor: f32,
86 /// Maximum messages to deliver per tick per connection. `None` = unlimited.
87 pub max_messages_per_tick: Option<u16>,
88 /// Maximum number of unacknowledged messages buffered per connection on
89 /// this channel. When the queue is full, `Server::send_message` /
90 /// `Client::send_message` returns
91 /// `Err(NaiaServerError::MessageQueueFull)` /
92 /// `Err(NaiaClientError::MessageQueueFull)` and the caller must decide
93 /// whether to retry or discard. `None` = unlimited (not recommended for
94 /// production servers). Default: `Some(1024)`.
95 pub max_queue_depth: Option<usize>,
96}
97
98impl ReliableSettings {
99 /// Returns the default `ReliableSettings` (RTT factor 1.5, unlimited throughput, queue cap 1 024).
100 pub const fn default() -> Self {
101 Self {
102 rtt_resend_factor: 1.5,
103 max_messages_per_tick: None,
104 max_queue_depth: Some(1024),
105 }
106 }
107}
108
109/// Capacity settings for a tick-buffered channel.
110#[derive(Clone)]
111pub struct TickBufferSettings {
112 /// Describes a maximum of messages that may be kept in the buffer.
113 /// Oldest messages are pruned out first.
114 pub message_capacity: usize,
115}
116
117impl TickBufferSettings {
118 /// Returns the default `TickBufferSettings` with a message capacity of 64.
119 pub const fn default() -> Self {
120 Self {
121 message_capacity: 64,
122 }
123 }
124}
125
126/// Delivery semantics for a channel.
127#[derive(Clone)]
128pub enum ChannelMode {
129 /// Messages are delivered at most once with no ordering guarantee.
130 UnorderedUnreliable,
131 /// Only the latest message per sequence slot is delivered; older ones are silently dropped.
132 SequencedUnreliable,
133 /// Every message is delivered exactly once; arrival order is not guaranteed.
134 UnorderedReliable(ReliableSettings),
135 /// Every message is delivered exactly once; only the latest-sequenced message is surfaced.
136 SequencedReliable(ReliableSettings),
137 /// Every message is delivered exactly once in the original send order.
138 OrderedReliable(ReliableSettings),
139 /// Messages are held in a fixed-capacity buffer tied to a specific server tick.
140 TickBuffered(TickBufferSettings),
141}
142
143impl ChannelMode {
144 /// Returns `true` if this mode is `TickBuffered`.
145 pub fn tick_buffered(&self) -> bool {
146 matches!(self, ChannelMode::TickBuffered(_))
147 }
148}
149
150/// Permitted send direction(s) for a channel.
151#[derive(Clone, Eq, PartialEq)]
152pub enum ChannelDirection {
153 /// Only the client may send on this channel.
154 ClientToServer,
155 /// Only the server may send on this channel.
156 ServerToClient,
157 /// Both endpoints may send on this channel.
158 Bidirectional,
159}
160
161/// Priority tier for a channel in the unified priority-sort send loop.
162///
163/// Each message's accumulator grows per tick by `base_gain()` × tick-age.
164/// Higher criticality → faster accumulator growth → earlier eligibility in the
165/// sorted drain. Reliable channels never drop items; criticality only changes
166/// when they egress relative to other channels and entity bundles.
167#[derive(Clone, Copy, Debug, Eq, PartialEq)]
168pub enum ChannelCriticality {
169 /// Background traffic (e.g. non-urgent unreliable). `base_gain() = 0.5`.
170 Low,
171 /// Default tier. `base_gain() = 1.0`.
172 Normal,
173 /// Control traffic that must head the queue (e.g. auth, connection
174 /// lifecycle, critical RPCs). `base_gain() = 10.0`.
175 High,
176}
177
178impl ChannelCriticality {
179 /// Default tier applied by `ChannelSettings::new` based on channel mode.
180 /// TickBuffered → High (must land in the right tick window). Everything
181 /// else → Normal. Callers can override via `with_criticality()`.
182 pub const fn default_for(mode: &ChannelMode) -> Self {
183 match mode {
184 ChannelMode::TickBuffered(_) => ChannelCriticality::High,
185 _ => ChannelCriticality::Normal,
186 }
187 }
188
189 /// Per-tick priority gain applied to every queued message on this channel.
190 pub const fn base_gain(&self) -> f32 {
191 match self {
192 ChannelCriticality::Low => 0.5,
193 ChannelCriticality::Normal => 1.0,
194 ChannelCriticality::High => 10.0,
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 // A-BDD-5: Channel built with with_criticality(Low) on a normally-Normal
204 // mode gets Low base_gain in sort.
205 #[test]
206 fn with_criticality_overrides_mode_default() {
207 let s = ChannelSettings::new(
208 ChannelMode::UnorderedReliable(ReliableSettings::default()),
209 ChannelDirection::Bidirectional,
210 );
211 assert_eq!(s.criticality, ChannelCriticality::Normal);
212 let s2 = s.with_criticality(ChannelCriticality::Low);
213 assert_eq!(s2.criticality, ChannelCriticality::Low);
214 assert!((s2.criticality.base_gain() - 0.5).abs() < f32::EPSILON);
215 }
216
217 #[test]
218 fn tick_buffered_defaults_to_high() {
219 let s = ChannelSettings::new(
220 ChannelMode::TickBuffered(TickBufferSettings::default()),
221 ChannelDirection::ClientToServer,
222 );
223 assert_eq!(s.criticality, ChannelCriticality::High);
224 assert!((s.criticality.base_gain() - 10.0).abs() < f32::EPSILON);
225 }
226
227 #[test]
228 fn unreliable_defaults_to_normal() {
229 let s = ChannelSettings::new(
230 ChannelMode::UnorderedUnreliable,
231 ChannelDirection::Bidirectional,
232 );
233 assert_eq!(s.criticality, ChannelCriticality::Normal);
234 }
235
236 // A-BDD-6 support: base_gain ordering. High > Normal > Low.
237 #[test]
238 fn base_gain_ordering() {
239 let high = ChannelCriticality::High.base_gain();
240 let normal = ChannelCriticality::Normal.base_gain();
241 let low = ChannelCriticality::Low.base_gain();
242 assert!(high > normal);
243 assert!(normal > low);
244 }
245}