Skip to main content

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}