1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! The set of traits that need to be implemented to tell CrystalOrb how to use your external
//! networking library to send and receive messages.

use serde::{de::DeserializeOwned, Serialize};
use std::{error::Error, fmt::Debug};

/// Used for identifying each connection. Note that this is how the
/// [`Server`](crate::server::Server) determines the
/// [`client_id`](crate::client::stage::Ready::client_id) for each client, so they should be unique
/// among all clients that is or has once connected during the server's uptime.
pub type ConnectionHandleType = usize;

/// CrystalOrb needs an external networking library before it can be used. Such networking library
/// will be responsible for sending three kinds of messages:
///
/// 1. [`ClockSyncMessage`](crate::clocksync::ClockSyncMessage)
/// 2. `Timestamped<SnapshotType>` (see [`Timestamped`](crate::timestamp::Timestamped) and
///    [`SnapshotType`](crate::world::World::SnapshotType)).
/// 3. `Timestamped<CommandType>` (see [`Timestamped`](crate::timestamp::Timestamped) and
///    [`CommandType`](crate::world::World::CommandType)).
///
/// In theory, the external networking library would not be responsible for understanding these
/// messages. It would only be responsible for sending and receiving them.
///
/// How these message channels get multiplexed is up to you / the external networking library.
/// Whether or not these message channels are reliable or unreliable, ordered or unordered, is also
/// up to you / the external networking library. CrystalOrb is written assuming that
/// `ClockSyncMessage` and `SnapshotType` are unreliable and unordered, while `CommandType` is
/// reliable but unordered.
///
/// This interface is based off on the interface provided by the
/// [`bevy_networking_turbulence`](https://github.com/smokku/bevy_networking_turbulence) plugin. See
/// [`crystalorb-bevy-networking-turbulence`](https://github.com/ErnWong/crystalorb/tree/crates/crystalorb-bevy-networking-turbulence)
/// for an example for integrating with `bevy_networking_turbulence`.
pub trait NetworkResource {
    /// The [`Connection`] structure that CrystalOrb will use to send/receive messages from a
    /// specific remote machine. This may probably be a wrapper to a mutable reference to some
    /// connection type that is used by your external networking library of choice, in which
    /// case a generic lifetime parameter is provided here that you can use.
    type ConnectionType<'a>: Connection;

    /// Iterate through the available connections. For servers, this would be the list of current
    /// client connections that are still alive. For the client, this would only contain the
    /// connection to the server once the connection has been established.
    fn connections<'a>(
        &'a mut self,
    ) -> Box<dyn Iterator<Item = (ConnectionHandleType, Self::ConnectionType<'a>)> + 'a>;

    /// Get a specific connection given its connection handle.
    fn get_connection(&mut self, handle: ConnectionHandleType) -> Option<Self::ConnectionType<'_>>;

    /// Optional: Send the given message to all active connections. A default implementation is
    /// already given that uses [`NetworkResource::connections`] and [`Connection::send`].
    ///
    /// CrystalOrb will invoke this method with the three message types as specified in
    /// [`NetworkResource`].
    fn broadcast_message<MessageType>(&mut self, message: MessageType)
    where
        MessageType: Debug + Clone + Serialize + DeserializeOwned + Send + Sync + 'static,
    {
        // Adapted from bevy_networking_turbulence.
        for (_, mut connection) in self.connections() {
            connection.send(message.clone());
            connection.flush::<MessageType>();
        }
    }

    /// Optional: Send the given message to the given connection. A default implementation is
    /// already given that uses [`NetworkResource::get_connection`] and [`Connection::send`].
    ///
    /// CrystalOrb will invoke this method with the three message types as specified in
    /// [`NetworkResource`].
    ///
    /// # Errors
    ///
    /// Returns [`NotFound`](std::io::ErrorKind::NotFound) [`std::io::Error`] if a connection with
    /// the given `handle` could not be found.
    fn send_message<MessageType>(
        &mut self,
        handle: ConnectionHandleType,
        message: MessageType,
    ) -> Result<Option<MessageType>, Box<dyn Error + Send>>
    where
        MessageType: Debug + Clone + Serialize + DeserializeOwned + Send + Sync + 'static,
    {
        // Adapted from bevy_networking_turbulence.
        match self.get_connection(handle) {
            Some(mut connection) => {
                let unsent = connection.send(message);
                connection.flush::<MessageType>();
                Ok(unsent)
            }
            None => Err(Box::new(std::io::Error::new(
                std::io::ErrorKind::NotFound,
                "No such connection",
            ))),
        }
    }
}

/// Representation of a connection to a specific remote machine that allows CrystalOrb to send and
/// receive messages.
pub trait Connection {
    /// Interface for CrystalOrb to request the next message received, if there is one.
    ///
    /// CrystalOrb will invoke this method with the three message types as specified in
    /// [`NetworkResource`].
    fn recv<MessageType>(&mut self) -> Option<MessageType>
    where
        MessageType: Debug + Clone + Serialize + DeserializeOwned + Send + Sync + 'static;

    /// Interface for CrystalOrb to try sending a message to the connection's destination. If
    /// unsuccessful, the message should be returned. Otherwise, `None` should be returned.
    ///
    /// CrystalOrb will invoke this method with the three message types as specified in
    /// [`NetworkResource`].
    fn send<MessageType>(&mut self, message: MessageType) -> Option<MessageType>
    where
        MessageType: Debug + Clone + Serialize + DeserializeOwned + Send + Sync + 'static;

    /// Interface that CrystalOrb would use to ensure that any pending messages are sent.
    ///
    /// CrystalOrb will invoke this method with the three message types as specified in
    /// [`NetworkResource`].
    fn flush<MessageType>(&mut self)
    where
        MessageType: Debug + Clone + Serialize + DeserializeOwned + Send + Sync + 'static;
}