naia-server 0.25.0

A server that uses either UDP or WebRTC communication to send/receive messages to/from connected clients, and syncs registered Entities/Components to clients to whom they are in-scope.
use std::{collections::HashMap, marker::PhantomData, mem, net::SocketAddr, vec::IntoIter};

use naia_shared::{Message, MessageContainer, MessageKind};

use crate::{events::world_events, user::UserKey, NaiaServerError};

/// Event container for transport-level events: new connections, auth messages, errors, and queued disconnects.
pub struct MainEvents {
    auths: HashMap<MessageKind, Vec<(UserKey, MessageContainer)>>,
    connections: Vec<UserKey>,
    queued_disconnects: Vec<UserKey>,
    errors: Vec<NaiaServerError>,
    world_packets: Vec<(UserKey, SocketAddr, Box<[u8]>)>,

    empty: bool,
}

impl Default for MainEvents {
    fn default() -> Self {
        Self {
            auths: HashMap::new(),
            connections: Vec::new(),
            queued_disconnects: Vec::new(),
            errors: Vec::new(),
            world_packets: Vec::new(),

            empty: true,
        }
    }
}

impl MainEvents {
    // Public

    /// Returns `true` if no events are pending.
    pub fn is_empty(&self) -> bool {
        self.empty
    }

    /// Drains and returns all events of type `V`.
    pub fn read<V: MainEvent>(&mut self) -> V::Iter {
        V::iter(self)
    }

    /// Returns `true` if at least one event of type `V` is pending.
    pub fn has<V: MainEvent>(&self) -> bool {
        V::has(self)
    }

    /// Merges all events from `other` into `self`.
    pub fn append(&mut self, other: Self) {
        self.auths.extend(other.auths);
        self.connections.extend(other.connections);
        self.queued_disconnects.extend(other.queued_disconnects);
        self.errors.extend(other.errors);
        self.world_packets.extend(other.world_packets);

        if !other.empty {
            self.empty = false;
        }
    }

    // These methods are exposed for adapter crates ... prefer using Events.read::<SomeEvent>() instead.
    /// Returns `true` if any auth messages are pending. Prefer `read::<AuthEvent<M>>()`.
    pub fn has_auths(&self) -> bool {
        !self.auths.is_empty()
    }
    /// Drains the raw auth message map. Prefer `read::<AuthEvent<M>>()` over this method.
    pub fn take_auths(&mut self) -> HashMap<MessageKind, Vec<(UserKey, MessageContainer)>> {
        mem::take(&mut self.auths)
    }

    // Crate-public

    pub(crate) fn push_connection(&mut self, user_key: &UserKey) {
        self.connections.push(*user_key);
        self.empty = false;
    }

    pub(crate) fn push_auth(&mut self, user_key: &UserKey, auth_message: MessageContainer) {
        let message_type_id = auth_message.kind();
        let list = self.auths.entry(message_type_id).or_default();
        list.push((*user_key, auth_message));
        self.empty = false;
    }

    pub(crate) fn push_error(&mut self, error: NaiaServerError) {
        self.errors.push(error);
        self.empty = false;
    }

    pub(crate) fn push_world_packet(
        &mut self,
        user_key: UserKey,
        user_addr: SocketAddr,
        payload: Box<[u8]>,
    ) {
        self.world_packets.push((user_key, user_addr, payload));
        self.empty = false;
    }

    pub(crate) fn push_queued_disconnect(&mut self, user_key: &UserKey) {
        self.queued_disconnects.push(*user_key);
        self.empty = false;
    }
}

/// Marker trait for types that can be read from [`MainEvents`].
pub trait MainEvent {
    /// Iterator type yielded by [`MainEvents::read`].
    type Iter;

    /// Drains all events of this type and returns an iterator.
    fn iter(events: &mut MainEvents) -> Self::Iter;

    /// Returns `true` if at least one event of this type is pending.
    fn has(events: &MainEvents) -> bool;
}

// ConnectEvent
/// Fires when a new client completes the handshake and is ready to receive entities.
pub struct ConnectEvent;
impl MainEvent for ConnectEvent {
    type Iter = IntoIter<UserKey>;

    fn iter(events: &mut MainEvents) -> Self::Iter {
        let list = std::mem::take(&mut events.connections);
        IntoIterator::into_iter(list)
    }

    fn has(events: &MainEvents) -> bool {
        !events.connections.is_empty()
    }
}

// Error Event
/// Fires when a transport or protocol error occurs; carries a [`NaiaServerError`].
pub struct ErrorEvent;
impl MainEvent for ErrorEvent {
    type Iter = IntoIter<NaiaServerError>;

    fn iter(events: &mut MainEvents) -> Self::Iter {
        let list = std::mem::take(&mut events.errors);
        IntoIterator::into_iter(list)
    }

    fn has(events: &MainEvents) -> bool {
        !events.errors.is_empty()
    }
}

// Auth Event
/// Fires when a client sends an auth message during the handshake; yields `(UserKey, M)` pairs.
pub struct AuthEvent<M: Message> {
    phantom_m: PhantomData<M>,
}
impl<M: Message> MainEvent for AuthEvent<M> {
    type Iter = IntoIter<(UserKey, M)>;

    fn iter(events: &mut MainEvents) -> Self::Iter {
        let message_kind: MessageKind = MessageKind::of::<M>();
        if let Some(messages) = events.auths.remove(&message_kind) {
            IntoIterator::into_iter(world_events::read_messages(messages))
        } else {
            IntoIterator::into_iter(Vec::new())
        }
    }

    fn has(events: &MainEvents) -> bool {
        let message_kind: MessageKind = MessageKind::of::<M>();
        events.auths.contains_key(&message_kind)
    }
}

// QueuedDisconnectEvent
/// Fires when the server has scheduled a user disconnect; the disconnect completes on the next tick.
pub struct QueuedDisconnectEvent;
impl MainEvent for QueuedDisconnectEvent {
    type Iter = IntoIter<UserKey>;

    fn iter(events: &mut MainEvents) -> Self::Iter {
        let list = std::mem::take(&mut events.queued_disconnects);
        IntoIterator::into_iter(list)
    }

    fn has(events: &MainEvents) -> bool {
        !events.queued_disconnects.is_empty()
    }
}

// WorldPacketEvent
/// Fires when a raw world-server packet is received from a client; used by adapter crates only.
pub struct WorldPacketEvent;
impl MainEvent for WorldPacketEvent {
    type Iter = IntoIter<(UserKey, SocketAddr, Box<[u8]>)>;

    fn iter(events: &mut MainEvents) -> Self::Iter {
        let list = std::mem::take(&mut events.world_packets);
        IntoIterator::into_iter(list)
    }

    fn has(events: &MainEvents) -> bool {
        !events.world_packets.is_empty()
    }
}