// SPDX-License-Identifier: AGPL-3.0-only //! Session-level concepts describing the relationship between a client and the server it is playing against. /// Which kind of server a client session is running against. /// /// Deliberately not part of the wire protocol. The client already knows the answer without asking: it either spawned a server in-process or dialled a socket. A server-declared field would be redundant at best and spoofable at worst, so the value is constructed client-side from facts the client already holds. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ServerKind { /// The server runs in this process, backing single-player. Integrated, /// The server is a separate process reached over the network. Dedicated { /// Whether the server's address is off this machine. Decided from the `SocketAddr` the client dialled, not from anything the server says. remote: bool, }, } impl ServerKind { /// Classifies a dedicated server from the address the client dialled. /// /// A loopback address means the process is on this machine (a locally hosted server), which is distinct from an integrated one: it is still a separate process reached over a socket. #[must_use] pub const fn dedicated(addr: std::net::SocketAddr) -> Self { Self::Dedicated { remote: !addr.ip().is_loopback(), } } /// Returns a short human-readable label for the session's server kind. #[must_use] pub const fn label(self) -> &'static str { match self { Self::Integrated => "integrated", Self::Dedicated { remote: false } => "dedicated (local)", Self::Dedicated { remote: true } => "dedicated (remote)", } } } #[cfg(test)] #[path = "tests/session.rs"] mod tests;