Skip to main content

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}