1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! # Protocol Version Constants and Negotiation -- Spec §5.2 & §25
//!
//! This module defines the version-related constants and negotiation
//! logic for the Rift/1 protocol. The protocol uses a two-part version
//! number (`major.minor`) encoded as a single `u16`:
//!
//! ```text
//! encoded = major << 8 | minor
//! ```
//!
//! For example, Rift/1.0 encodes as `0x0100` (256 decimal).
//!
//! ## Versioning Rules (§25)
//!
//! * **Major version** bumps indicate backwards-incompatible wire-format
//! changes. A server MUST reject clients that advertise an
//! unsupported major version.
//! * **Minor version** bumps indicate backwards-compatible additions
//! (e.g. new optional fields). A server SHOULD accept clients with
//! an older minor version and simply ignore unknown fields.
//!
//! ## Negotiation Flow (§5.2)
//!
//! 1. The client sends its encoded version in the Hello frame.
//! 2. The server extracts the major version and checks it against
//! [`SUPPORTED_MAJOR`].
//! 3. If the major version is not in the supported range, the server
//! rejects the connection with a [`ProtocolVersionUnsupported`](super::error_code::ErrorCode::ProtocolVersionUnsupported)
//! error.
//! 4. Otherwise the server accepts the client's major version via
//! [`negotiate_major`].
//!
//! ## Constants
//!
//! | Constant | Description |
//! |---------------------|----------------------------------------------|
//! | `PROTOCOL_NAME` | Wire protocol identifier (`"rift"`) |
//! | `PROTOCOL_MAJOR` | Current major version (1) |
//! | `PROTOCOL_MINOR` | Current minor version (0) |
//! | `SUPPORTED_MAJOR` | Range of major versions the server accepts |
use RangeInclusive;
/// Protocol name on the wire -- spec §5.2 (`hello.protocol`).
///
/// This is the identifier string the client sends in its Hello frame to
/// declare which protocol it speaks. The server uses this value to
/// multiplex between different protocols on the same port.
pub const PROTOCOL_NAME: &str = "rift";
/// Current major version.
///
/// Bumped on backwards-incompatible wire-format changes. A server that
/// implements Rift/`X`.* will reject clients that advertise a different
/// major version.
pub const PROTOCOL_MAJOR: u16 = 1;
/// Current minor version.
///
/// Bumped on backwards-compatible additions (new optional fields, new
/// frame types, etc.). A server MUST accept clients with an older
/// minor version and ignore any fields it does not understand.
pub const PROTOCOL_MINOR: u16 = 0;
/// Supported major-version range offered to clients during hello.
///
/// The server will accept connections from clients whose major version
/// falls within this inclusive range. For Rift/1 the range is
/// `1..=1`, meaning only major version 1 is accepted.
pub const SUPPORTED_MAJOR: = 1..=1;
/// Encoded protocol version (`major << 8 | minor`).
///
/// This is the value transmitted in the `version` field of every Hello
/// frame and in the frame header. Because it is a `const fn`, it is
/// evaluated at compile time and inlined at every call site.
///
/// ```rust
/// use rifts::protocol::version::encoded_version;
///
/// assert_eq!(encoded_version(), 0x0100);
/// ```
pub const
/// Negotiate the highest mutually-supported major version.
///
/// Returns `Some(major)` if the client's major version is within
/// [`SUPPORTED_MAJOR`], or `None` if the client's major version is
/// unsupported.
///
/// For Rift/1 this always returns `Some(1)` for valid clients; the
/// helper exists so that a future Rift/2 server can still accept
/// Rift/1 clients by extending the supported range.
///
/// ```rust
/// use rifts::protocol::version::negotiate_major;
///
/// assert_eq!(negotiate_major(1), Some(1));
/// assert_eq!(negotiate_major(0), None);
/// assert_eq!(negotiate_major(2), None);
/// ```