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
129
130
131
132
//! 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};

use crate::{clocksync::ClockSyncMessage, timestamp::Timestamped, world::World};

/// 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<WorldType: World> {
    /// 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<WorldType>
    where
        Self: 'a,
        WorldType: 'a;

    /// 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<WorldType: World> {
    /// Interface for CrystalOrb to receive the next command message.
    fn recv_command(&mut self) -> Option<Timestamped<WorldType::CommandType>>;
    /// Interface for CrystalOrb to receive the next snapshot message.
    fn recv_snapshot(&mut self) -> Option<Timestamped<WorldType::SnapshotType>>;
    /// Interface for CrystalOrb to receive the next clock sync message.
    fn recv_clock_sync(&mut self) -> Option<ClockSyncMessage>;

    /// 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;
}