basalt_mc_protocol/state.rs
1/// The connection states in the Minecraft protocol.
2///
3/// A Minecraft connection progresses through these states in order:
4/// Handshake → Status or Login → Configuration → Play. Each state
5/// has its own set of valid packets (both serverbound and clientbound),
6/// and the packet ID space is independent per state — the same ID
7/// can mean different packets in different states.
8///
9/// The Handshake state is special: it contains only one serverbound
10/// packet that determines whether the connection transitions to
11/// Status (server list ping) or Login (joining the game).
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum ConnectionState {
14 /// Initial state. The client sends a single Handshake packet that
15 /// declares the protocol version and whether it wants Status or Login.
16 Handshake,
17
18 /// Server list ping. The client requests server info (MOTD, player
19 /// count, icon) and latency measurement. No authentication occurs.
20 Status,
21
22 /// Authentication and encryption setup. The client and server exchange
23 /// login credentials, enable encryption (AES/CFB-8), and optionally
24 /// enable compression. Ends with Login Success.
25 Login,
26
27 /// Post-login configuration. Registry data, resource packs, and
28 /// feature flags are exchanged before entering gameplay. Added in
29 /// Minecraft 1.20.2.
30 Configuration,
31
32 /// Active gameplay. The majority of packets (movement, block changes,
33 /// chat, entity updates, chunk data) are exchanged in this state.
34 Play,
35}
36
37impl ConnectionState {
38 /// Returns the protocol name of this state, used in error messages
39 /// and debug output.
40 pub fn as_str(&self) -> &'static str {
41 match self {
42 Self::Handshake => "handshake",
43 Self::Status => "status",
44 Self::Login => "login",
45 Self::Configuration => "configuration",
46 Self::Play => "play",
47 }
48 }
49}
50
51impl std::fmt::Display for ConnectionState {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.write_str(self.as_str())
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn as_str_all_states() {
63 assert_eq!(ConnectionState::Handshake.as_str(), "handshake");
64 assert_eq!(ConnectionState::Status.as_str(), "status");
65 assert_eq!(ConnectionState::Login.as_str(), "login");
66 assert_eq!(ConnectionState::Configuration.as_str(), "configuration");
67 assert_eq!(ConnectionState::Play.as_str(), "play");
68 }
69
70 #[test]
71 fn display_all_states() {
72 assert_eq!(ConnectionState::Handshake.to_string(), "handshake");
73 assert_eq!(ConnectionState::Status.to_string(), "status");
74 assert_eq!(ConnectionState::Login.to_string(), "login");
75 assert_eq!(ConnectionState::Configuration.to_string(), "configuration");
76 assert_eq!(ConnectionState::Play.to_string(), "play");
77 }
78}