deepslate 0.3.1

A high-performance Minecraft server proxy written in Rust.
Documentation
//! Concrete event types fired by the proxy.
//!
//! Each event is a plain struct. Events that can be cancelled or have their
//! outcome modified implement [`ResultedEvent`] with a domain-specific result
//! enum.
//!
//! Notification-only events (no result) are simple structs that handlers can
//! read but not influence.

use std::sync::Arc;

use super::result::{EventResult, ResultedEvent};
use crate::event::PlayerInfo;
use crate::server::ServerId;

// ---------------------------------------------------------------------------
// LoginEvent
// ---------------------------------------------------------------------------

/// Fired after a player completes authentication but before they are connected
/// to a backend server.
///
/// Handlers can deny the login by setting the result to [`LoginResult::Deny`].
pub struct LoginEvent {
    /// Information about the connecting player.
    pub player: Arc<PlayerInfo>,
    result: LoginResult,
}

impl LoginEvent {
    /// Create a new login event (defaults to allowed).
    #[must_use]
    pub const fn new(player: Arc<PlayerInfo>) -> Self {
        Self {
            player,
            result: LoginResult::Allow,
        }
    }

    /// Deny the login with a disconnect message.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// event.deny("You are not allowed to join.");
    /// ```
    pub fn deny(&mut self, reason: impl Into<String>) {
        self.result = LoginResult::Deny(reason.into());
    }

    /// Allow the login, re-allowing it if a previous handler denied it.
    pub fn allow(&mut self) {
        self.result = LoginResult::Allow;
    }
}

/// Result for [`LoginEvent`].
#[derive(Debug, Clone)]
pub enum LoginResult {
    /// Allow the player to connect.
    Allow,
    /// Deny the connection with a reason (sent as a disconnect message).
    Deny(String),
}

impl EventResult for LoginResult {
    fn is_allowed(&self) -> bool {
        matches!(self, Self::Allow)
    }
}

impl ResultedEvent for LoginEvent {
    type Result = LoginResult;

    fn result(&self) -> &Self::Result {
        &self.result
    }

    fn set_result(&mut self, result: Self::Result) {
        self.result = result;
    }
}

// ---------------------------------------------------------------------------
// ChooseServerEvent
// ---------------------------------------------------------------------------

/// Fired to determine which backend server a player should initially connect to.
///
/// Handlers can override the default server selection by setting the result to
/// [`ChooseServerResult::Override`].
pub struct ChooseServerEvent {
    /// Information about the connecting player.
    pub player: Arc<PlayerInfo>,
    result: ChooseServerResult,
}

impl ChooseServerEvent {
    /// Create a new event (defaults to using the configured try-order).
    #[must_use]
    pub const fn new(player: Arc<PlayerInfo>) -> Self {
        Self {
            player,
            result: ChooseServerResult::Default,
        }
    }

    /// Override the server selection to send the player to a specific server.
    ///
    /// Accepts a [`ServerId`] reference (the common case with typed constants):
    ///
    /// ```rust,ignore
    /// event.set_server(&LOBBY);
    /// ```
    pub fn set_server(&mut self, server: &ServerId) {
        self.result = ChooseServerResult::Override(server.id.to_owned());
    }
}

/// Result for [`ChooseServerEvent`].
#[derive(Debug, Clone)]
pub enum ChooseServerResult {
    /// Use the configured try-order to select a server.
    Default,
    /// Connect to a specific server, overriding the try-order.
    Override(String),
}

impl EventResult for ChooseServerResult {
    fn is_allowed(&self) -> bool {
        true // always "allowed" — the question is which server, not whether to allow
    }
}

impl From<ServerId> for ChooseServerResult {
    /// Convert a [`ServerId`] into an override result.
    ///
    /// This allows plugins to write `event.set_result(LOBBY.into())` when
    /// using a typed server constant.
    fn from(server: ServerId) -> Self {
        Self::Override(server.id.to_owned())
    }
}

impl ResultedEvent for ChooseServerEvent {
    type Result = ChooseServerResult;

    fn result(&self) -> &Self::Result {
        &self.result
    }

    fn set_result(&mut self, result: Self::Result) {
        self.result = result;
    }
}

// ---------------------------------------------------------------------------
// ServerSwitchEvent
// ---------------------------------------------------------------------------

/// Fired when a player requests a server switch (e.g., via `/server <name>`).
///
/// Handlers can deny the switch by setting the result to
/// [`ServerSwitchResult::Deny`].
pub struct ServerSwitchEvent {
    /// Information about the player.
    pub player: Arc<PlayerInfo>,
    /// The server the player is currently on.
    pub from: String,
    /// The server the player wants to switch to.
    pub to: String,
    result: ServerSwitchResult,
}

impl ServerSwitchEvent {
    /// Create a new event (defaults to allowed).
    #[must_use]
    pub const fn new(player: Arc<PlayerInfo>, from: String, to: String) -> Self {
        Self {
            player,
            from,
            to,
            result: ServerSwitchResult::Allow,
        }
    }

    /// Deny the server switch with a message sent to the player.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// event.deny("You cannot switch to that server right now.");
    /// ```
    pub fn deny(&mut self, reason: impl Into<String>) {
        self.result = ServerSwitchResult::Deny(reason.into());
    }

    /// Allow the server switch, re-allowing it if a previous handler denied it.
    pub fn allow(&mut self) {
        self.result = ServerSwitchResult::Allow;
    }
}

/// Result for [`ServerSwitchEvent`].
#[derive(Debug, Clone)]
pub enum ServerSwitchResult {
    /// Allow the server switch.
    Allow,
    /// Deny the switch with a reason (sent as a chat message).
    Deny(String),
}

impl EventResult for ServerSwitchResult {
    fn is_allowed(&self) -> bool {
        matches!(self, Self::Allow)
    }
}

impl ResultedEvent for ServerSwitchEvent {
    type Result = ServerSwitchResult;

    fn result(&self) -> &Self::Result {
        &self.result
    }

    fn set_result(&mut self, result: Self::Result) {
        self.result = result;
    }
}

// ---------------------------------------------------------------------------
// ServerConnectedEvent
// ---------------------------------------------------------------------------

/// Fired after a player has successfully connected to a backend server.
///
/// This is a notification-only event — handlers cannot cancel it.
pub struct ServerConnectedEvent {
    /// Information about the player.
    pub player: Arc<PlayerInfo>,
    /// The ID of the server the player connected to.
    pub server_id: String,
}

impl ServerConnectedEvent {
    /// Create a new event.
    #[must_use]
    pub const fn new(player: Arc<PlayerInfo>, server_id: String) -> Self {
        Self { player, server_id }
    }
}

// ---------------------------------------------------------------------------
// DisconnectEvent
// ---------------------------------------------------------------------------

/// Fired when a player disconnects from the proxy.
///
/// This is a notification-only event — handlers cannot cancel it.
pub struct DisconnectEvent {
    /// Information about the disconnecting player.
    pub player: Arc<PlayerInfo>,
}

impl DisconnectEvent {
    /// Create a new event.
    #[must_use]
    pub const fn new(player: Arc<PlayerInfo>) -> Self {
        Self { player }
    }
}

// ---------------------------------------------------------------------------
// PingEvent
// ---------------------------------------------------------------------------

/// Fired when the proxy receives a server list ping.
///
/// Handlers can override the status response by setting the result to
/// [`PingResult::Override`].
pub struct PingEvent {
    /// The default status response JSON.
    pub default_response: serde_json::Value,
    /// The protocol version of the pinging client.
    pub client_protocol: i32,
    /// The hostname the client used to connect (normalised from the handshake
    /// packet's `server_address` field).
    ///
    /// Plugins can use this to return a different MOTD per virtual host.
    pub virtual_host: String,
    result: PingResult,
}

impl PingEvent {
    /// Create a new event (defaults to using the built-in response).
    #[must_use]
    pub const fn new(
        default_response: serde_json::Value,
        client_protocol: i32,
        virtual_host: String,
    ) -> Self {
        Self {
            default_response,
            client_protocol,
            virtual_host,
            result: PingResult::Default,
        }
    }

    /// Return the current effective response, taking into account any override
    /// set by a previous handler.
    ///
    /// This ensures that multiple handlers compose correctly — e.g. one handler
    /// calls [`set_motd`](Self::set_motd), a later one calls
    /// [`set_players`](Self::set_players), and both changes apply.
    fn effective_response(&self) -> serde_json::Value {
        match &self.result {
            PingResult::Override(json) => json.clone(),
            PingResult::Default => self.default_response.clone(),
        }
    }

    /// Override just the MOTD text in the status response.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// event.set_motd("§a§lSurvival§r §7— direct connect");
    /// ```
    pub fn set_motd(&mut self, motd: impl Into<String>) {
        let mut response = self.effective_response();
        response["description"]["text"] = serde_json::Value::String(motd.into());
        self.result = PingResult::Override(response);
    }

    /// Override the online player count in the status response.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// event.set_players(42);
    /// ```
    pub fn set_players(&mut self, online: i32) {
        let mut response = self.effective_response();
        response["players"]["online"] = serde_json::Value::from(online);
        self.result = PingResult::Override(response);
    }

    /// Override the max player count in the status response.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// event.set_max_players(100);
    /// ```
    pub fn set_max_players(&mut self, max: i32) {
        let mut response = self.effective_response();
        response["players"]["max"] = serde_json::Value::from(max);
        self.result = PingResult::Override(response);
    }

    /// Override the entire status response JSON.
    ///
    /// This is an escape hatch for cases where the convenience methods above
    /// are not sufficient. Most handlers should prefer [`set_motd`](Self::set_motd),
    /// [`set_players`](Self::set_players), or [`set_max_players`](Self::set_max_players).
    pub fn set_response(&mut self, response: serde_json::Value) {
        self.result = PingResult::Override(response);
    }
}

/// Result for [`PingEvent`].
#[derive(Debug, Clone)]
pub enum PingResult {
    /// Use the default status response.
    Default,
    /// Use a custom status response.
    Override(serde_json::Value),
}

impl EventResult for PingResult {
    fn is_allowed(&self) -> bool {
        true // always "allowed" — the question is which response, not whether to respond
    }
}

impl ResultedEvent for PingEvent {
    type Result = PingResult;

    fn result(&self) -> &Self::Result {
        &self.result
    }

    fn set_result(&mut self, result: Self::Result) {
        self.result = result;
    }
}