From 104fea81e582b9de5e231ddf7904e30045af16c3 Mon Sep 17 00:00:00 2001 From: David LeGare Date: Sat, 5 Oct 2019 15:38:02 -0500 Subject: [PATCH 01/22] Derive Debug for ProgressCounter and ProgressCounterTracker --- amethyst_assets/src/progress.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amethyst_assets/src/progress.rs b/amethyst_assets/src/progress.rs index 646b638548..e6475ffb24 100644 --- a/amethyst_assets/src/progress.rs +++ b/amethyst_assets/src/progress.rs @@ -43,7 +43,7 @@ impl Progress for () { /// A progress tracker which is passed to the `Loader` /// in order to check how many assets are loaded. -#[derive(Default)] +#[derive(Default, Debug)] pub struct ProgressCounter { errors: Arc>>, num_assets: usize, @@ -123,7 +123,7 @@ impl<'a> Progress for &'a mut ProgressCounter { } /// Progress tracker for `ProgressCounter`. -#[derive(Default)] +#[derive(Default, Debug)] pub struct ProgressCounterTracker { errors: Arc>>, num_failed: Arc, From c104486fd15e4a023f651da2f486e9b6a7c9ad75 Mon Sep 17 00:00:00 2001 From: David LeGare Date: Sat, 5 Oct 2019 15:58:02 -0500 Subject: [PATCH 02/22] Update CHANGELOG.md --- docs/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 608fdeecbd..c02142f0bb 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,8 @@ The format is based on [Keep a Changelog][kc], and this project adheres to ### Added +* Implement `Debug` for `ProgressCounter` and `ProgressCounterTracker`. ([#1973]) + ### Changed * Use a premultiplied view_proj matrix in vertex shaders. ([#1964]) From 8ed3773956f25b36e2b8c66e3490e4f4acf39392 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Mon, 23 Sep 2019 01:38:17 -0400 Subject: [PATCH 03/22] Rewrite the network library to provide a nicer baseline for the future --- Cargo.toml | 2 +- amethyst_network/Cargo.toml | 15 +- amethyst_network/README.md | 26 +- amethyst_network/src/bundle.rs | 60 --- amethyst_network/src/connection.rs | 224 ----------- amethyst_network/src/error.rs | 53 --- amethyst_network/src/lib.rs | 144 +------ amethyst_network/src/net_event.rs | 365 ------------------ amethyst_network/src/network_socket.rs | 200 ---------- amethyst_network/src/server/config.rs | 49 --- amethyst_network/src/server/host.rs | 54 --- amethyst_network/src/server/mod.rs | 4 - amethyst_network/src/simulation.rs | 19 + amethyst_network/src/simulation/client.rs | 19 + amethyst_network/src/simulation/events.rs | 13 + amethyst_network/src/simulation/message.rs | 29 ++ .../src/simulation/requirements.rs | 36 ++ amethyst_network/src/simulation/resource.rs | 210 ++++++++++ amethyst_network/src/simulation/timing.rs | 148 +++++++ amethyst_network/src/simulation/transport.rs | 176 +++++++++ .../src/simulation/transport/laminar.rs | 136 +++++++ .../src/simulation/transport/socket.rs | 6 + .../src/simulation/transport/udp.rs | 133 +++++++ amethyst_network/src/test.rs | 133 ------- examples/headless_server/main.rs | 75 ---- examples/net_client/main.rs | 73 ++-- examples/net_server/main.rs | 149 ++++--- 27 files changed, 1075 insertions(+), 1476 deletions(-) delete mode 100644 amethyst_network/src/bundle.rs delete mode 100644 amethyst_network/src/connection.rs delete mode 100644 amethyst_network/src/error.rs delete mode 100644 amethyst_network/src/net_event.rs delete mode 100644 amethyst_network/src/network_socket.rs delete mode 100644 amethyst_network/src/server/config.rs delete mode 100644 amethyst_network/src/server/host.rs delete mode 100644 amethyst_network/src/server/mod.rs create mode 100644 amethyst_network/src/simulation.rs create mode 100644 amethyst_network/src/simulation/client.rs create mode 100644 amethyst_network/src/simulation/events.rs create mode 100644 amethyst_network/src/simulation/message.rs create mode 100644 amethyst_network/src/simulation/requirements.rs create mode 100644 amethyst_network/src/simulation/resource.rs create mode 100644 amethyst_network/src/simulation/timing.rs create mode 100644 amethyst_network/src/simulation/transport.rs create mode 100644 amethyst_network/src/simulation/transport/laminar.rs create mode 100644 amethyst_network/src/simulation/transport/socket.rs create mode 100644 amethyst_network/src/simulation/transport/udp.rs delete mode 100644 amethyst_network/src/test.rs delete mode 100644 examples/headless_server/main.rs diff --git a/Cargo.toml b/Cargo.toml index b56d750b44..8c7d9e6fb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ license = "MIT/Apache-2.0" travis-ci = { repository = "amethyst/amethyst", branch = "master" } [features] -default = ["animation", "audio", "locale", "network", "renderer"] +default = ["animation", "audio", "locale", "network", "renderer", "sentry"] vulkan = ["amethyst_rendy/vulkan"] metal = ["amethyst_rendy/metal"] diff --git a/amethyst_network/Cargo.toml b/amethyst_network/Cargo.toml index 47959b2b3b..57e1b14e50 100644 --- a/amethyst_network/Cargo.toml +++ b/amethyst_network/Cargo.toml @@ -4,7 +4,8 @@ version = "0.6.1" authors = [ "Joël Lupien (Jojolepro) ", "Lucio Franco (LucioFranco) ", - "Timon Post (TimonPost) " + "Timon Post (TimonPost) ", + "Justin LeFebvre (jstnlef) " ] edition = "2018" description = "Amethyst networking crate" @@ -24,13 +25,7 @@ nightly = [ "amethyst_core/nightly" ] [dependencies] amethyst_core = { path = "../amethyst_core", version = "0.8.1" } amethyst_error = { path = "../amethyst_error", version = "0.3.0" } -serde = { version = "1", features = ["derive"] } -shrev = "1.0" -shred = "0.9" -bincode = "1.0" -log = "0.4.6" -uuid = { version = "0.7.1", features = ["v4","serde"] } +bytes = "0.4" +laminar = "0.3" +log = "0.4" thread_profiler = { version = "0.3" , optional = true } -laminar = "0.2.3" -err-derive = "0.1" -crossbeam-channel = "0.3.9" diff --git a/amethyst_network/README.md b/amethyst_network/README.md index 449c720a43..f1c5fc1dfb 100644 --- a/amethyst_network/README.md +++ b/amethyst_network/README.md @@ -13,25 +13,27 @@ [s5]: https://img.shields.io/discord/425678876929163284.svg?logo=discord [l5]: https://discord.gg/GnP5Whs -The networking crate for the `amethyst` game engine. This crate provides the API and functionality which application developers will normally use to develop multiplayer games. The main engine can be found at https://amethyst.rs. +This is the networking crate for the `amethyst` game engine. This crate provides the building blocks which +application developers can use to develop online multiplayer games. The main engine can be found at https://amethyst.rs. -This project is still at an early stage. We are currently designing and implementing a fast/robust networking system on top of specs. To exercise our implementation, we are creating a small test game which we will make public when we feel it's in a good place. Eventually, as we gain more confidence in our solution, we will move stable functionality over from that game to amethyst network. +This project is still at an early stage. We are currently designing and implementing a robust networking system on +top of specs. To exercise our implementation, we are creating a small test game which we will make public when we feel +it's in a good place. Eventually, as we gain more confidence in our solution, we will move stable functionality over +from that game to amethyst network. Currently, amethyst network supports: -- Reliable (ordered, sequenced) UDP. -- Unreliable (sequenced) UDP. -- Connect/Disconnect events from clients. -- Automatic creation of `NetConnection` on client connect. -- Automatic Fragmentation of big packets - -We use [laminar](https://github.com/amethyst/laminar) as the application layer communication protocol. +- `NetworkSimulationTime` resource to decouple simulation frame rate from ECS frame rate +- An API abstraction for various transport layer network systems +- Implementations of the [laminar](https://github.com/amethyst/laminar) and UDP transport layers +- Connection lifecycle management ## Contribution -Unless you explicitly state otherwise, any Contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any -additional terms or conditions. +Unless you explicitly state otherwise, any Contribution intentionally submitted for inclusion in the work by you, as +defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -For more information or help, please come find us on the amethyst discord server's `#net` channel. We are working on architecture, design, and roadmaps and can definitely use some helping hands, don't hesitate :). +For more information or help, please come find us on the amethyst discord server's `#net` channel. We are working on +architecture, design, and roadmaps and can definitely use some helping hands, don't hesitate :). ## License diff --git a/amethyst_network/src/bundle.rs b/amethyst_network/src/bundle.rs deleted file mode 100644 index 9614eb2852..0000000000 --- a/amethyst_network/src/bundle.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::{marker::PhantomData, net::SocketAddr}; - -use serde::{de::DeserializeOwned, Serialize}; - -use amethyst_core::{bundle::SystemBundle, ecs::World, shred::DispatcherBuilder}; -use amethyst_error::{Error, ResultExt}; - -use crate::{server::ServerConfig, NetSocketSystem}; - -/// A convenience bundle to create the infrastructure needed to send and receive network messages. -#[allow(missing_debug_implementations)] // TODO: Revisit for laminar -pub struct NetworkBundle { - /// the configuration used for the networking crate. - config: ServerConfig, - _data: PhantomData, -} - -impl NetworkBundle { - /// Creates a new NetworkBundle. - /// - /// `receive_addr`: this is the address on which we will receive incoming packets. - /// `send_addr`: this is the address from which we will send outgoing packets. - pub fn new(udp_socket_addr: SocketAddr) -> Self { - let config = ServerConfig { - udp_socket_addr, - ..Default::default() - }; - - NetworkBundle { - config, - _data: PhantomData, - } - } - - /// Construct a new `NetworkBundle` with the specified configuration. - pub fn from_config(config: ServerConfig) -> NetworkBundle { - NetworkBundle { - config, - _data: PhantomData, - } - } -} - -impl<'a, 'b, T> SystemBundle<'a, 'b> for NetworkBundle -where - T: Send + Sync + PartialEq + Serialize + Clone + DeserializeOwned + 'static, -{ - /// Build the networking bundle by adding the networking system to the application. - fn build( - self, - _world: &mut World, - builder: &mut DispatcherBuilder<'_, '_>, - ) -> Result<(), Error> { - let socket_system = NetSocketSystem::::new(self.config) - .with_context(|_| Error::from_string("Failed to open network system."))?; - builder.add(socket_system, "net_socket", &[]); - - Ok(()) - } -} diff --git a/amethyst_network/src/connection.rs b/amethyst_network/src/connection.rs deleted file mode 100644 index 3b51a1715e..0000000000 --- a/amethyst_network/src/connection.rs +++ /dev/null @@ -1,224 +0,0 @@ -//! Network Connection and states. - -use serde::{Deserialize, Serialize}; -use shrev::{EventChannel, EventIterator, ReaderId}; -use std::{ - fmt::{Debug, Formatter}, - net::SocketAddr, -}; -use uuid::Uuid; - -use amethyst_core::ecs::{Component, VecStorage}; - -use crate::NetEvent; - -// TODO: Think about relationship between NetConnection and NetIdentity. - -/// A remote connection to some endpoint. -/// -/// This type is a `Component`, and it is used by systems too: -/// - Queue received data into this type -/// - Read the queued data from the user for transmission. -/// -/// # Remark -/// Note that this type does not perform any reading or writing, this is done only within systems. -/// This type acts as a container for to send and received data. -#[derive(Serialize)] -#[serde(bound = "")] -#[allow(missing_debug_implementations)] // TODO: Revisit, this is just because derivative not included in net -pub struct NetConnection { - /// The target remote socket address who is listening for incoming packets. - pub target_addr: SocketAddr, - /// The state of the connection. - pub state: ConnectionState, - // The buffer of events to be sent. - #[serde(skip)] - send_buffer: EventChannel>, - /// The buffer of events that have been received. - #[serde(skip)] - pub(crate) receive_buffer: EventChannel>, - /// The buffer used by `NetSocketSystem` that allows it to immediately send events upon receiving a new `NetConnection`. - #[serde(skip)] - send_reader: ReaderId>, -} - -impl NetConnection { - /// Construct a new `NetConnection`. - /// - /// - `SocketAddr`: the remote endpoint, from here the data will be send to and received from. - pub fn new(target_addr: SocketAddr) -> Self { - let mut send_buffer = EventChannel::new(); - let send_reader = send_buffer.register_reader(); - - NetConnection { - target_addr, - state: ConnectionState::Connecting, - send_buffer, - receive_buffer: EventChannel::>::new(), - send_reader, - } - } - - /// This function is used ONLY by `NetSocketSystem`. - /// - /// Most users both create the connection and send messages on the same frame, - /// we need a way to read those. Since the `NetSocketSystem` runs after the creation of the NetConnection, - /// it cannot possibly have registered his reader early enough to catch the initial messages that the user wants to send. - /// - /// The downside of this is that you are forced to take NetConnection mutably inside of NetSocketSystem. - /// If someone finds a better solution, please open a PR. - pub(crate) fn send_buffer_early_read(&mut self) -> EventIterator<'_, NetEvent> { - self.send_buffer.read(&mut self.send_reader) - } - - /// Queues the given event iterator of events for transmission. - /// This function writes the passed event iterator to a buffer which can be read by a `System`. - /// It is the job of the `System` to send those events to the remote client. - pub fn queue_iter(&mut self, iter: I) - where - I: IntoIterator>, - I::IntoIter: ExactSizeIterator, - { - self.send_buffer.iter_write(iter); - } - - /// Queues the given vector of events for transmission. - /// This function writes the passed event vector to a buffer which can be read by a `System`. - /// It is the job of the `System` to send those events to the remote client. - pub fn queue_vec(&mut self, events: &mut Vec>) { - self.send_buffer.drain_vec_write(events); - } - - /// Queues a single event for transmission. - /// This function writes the passed event to a buffer which can be read by a `System`. - /// It is the job of the `System` to send this event to the remote client. - pub fn queue(&mut self, event: NetEvent) { - self.send_buffer.single_write(event); - } - - /// Returns an iterator over the received events. - /// - /// - `reader_id`: the reader id of the registered reader. - /// - /// # Remark - /// - To be able to read events, a reader id is required. This is required for the underlying ringbuffer. - /// The reader id stores information of where in the ringbuffer the reader has read from earlier. - /// Please checkout `register_reader` which will return a `ReaderId` that allows you to read the received events. - pub fn received_events( - &self, - reader_id: &mut ReaderId>, - ) -> EventIterator<'_, NetEvent> { - self.receive_buffer.read(reader_id) - } - - /// Returns a `ReaderId` that can be used to read from the received events. - /// - /// # Remark - /// - To be able to read events, a reader id is required. This is because otherwise the channel - /// wouldn't know where in the ringbuffer the reader has read to earlier. This information is - /// stored in the reader id. - pub fn register_reader(&mut self) -> ReaderId> { - self.receive_buffer.register_reader() - } -} - -impl PartialEq for NetConnection { - fn eq(&self, other: &Self) -> bool { - self.target_addr == other.target_addr - } -} - -impl Eq for NetConnection {} - -impl Component for NetConnection { - type Storage = VecStorage; -} - -///The state of the connection. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum ConnectionState { - /// The connection is established. - Connected, - /// The connection is being established. - Connecting, - /// The connection has been dropped. - Disconnected, -} - -/// A network identity. It can represent either a client or a server. -/// It represents anything that can own an entity or a component. -/// Think of it as an identity card. -/// When used as a resource, it designates the local network uuid. -#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct NetIdentity { - /// The uuid identifying this NetIdentity. - pub uuid: Uuid, -} -impl Debug for NetIdentity { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let uuid_str = self.uuid.to_hyphenated().to_string(); - - f.debug_struct("NetIdentity") - .field("uuid", &uuid_str) - .finish() - } -} -impl Default for NetIdentity { - fn default() -> Self { - NetIdentity { - uuid: Uuid::new_v4(), - } - } -} - -impl Component for NetIdentity { - type Storage = VecStorage; -} - -#[cfg(test)] -mod tests { - use crate::{connection::NetConnection, net_event::NetEvent}; - - #[test] - fn can_read_received_events() { - let mut connection = test_connection(); - let mut reader_id = connection.register_reader(); - connection - .receive_buffer - .single_write(NetEvent::Connected("127.0.0.1:0".parse().unwrap())); - - assert_eq!(connection.received_events(&mut reader_id).len(), 1); - } - - #[test] - fn can_queue_packet_for_send() { - let mut connection = test_connection(); - connection.queue(NetEvent::Connected("127.0.0.1:0".parse().unwrap())); - - assert_eq!(connection.send_buffer_early_read().len(), 1); - } - - #[test] - fn can_queue_vec_for_send() { - let mut connection = test_connection(); - - let mut to_send_data = Vec::new(); - to_send_data.push(NetEvent::Connected("127.0.0.1:0".parse().unwrap())); - to_send_data.push(NetEvent::Connected("127.0.0.1:0".parse().unwrap())); - to_send_data.push(NetEvent::Connected("127.0.0.1:0".parse().unwrap())); - - connection.queue_vec(&mut to_send_data); - - assert_eq!( - connection - .send_buffer - .read(&mut connection.send_reader) - .len(), - 3 - ); - } - - fn test_connection() -> NetConnection { - NetConnection::new("127.0.0.1:0".parse().unwrap()) - } -} diff --git a/amethyst_network/src/error.rs b/amethyst_network/src/error.rs deleted file mode 100644 index 4356cf80e1..0000000000 --- a/amethyst_network/src/error.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Module containing error handling logic. - -use err_derive::Error; -use std::io; - -/// The `amethyst_network` result type. -pub type Result = std::result::Result; - -/// Wrapper for all errors who could occur in `amethyst_network`. -#[derive(Debug, Error)] -pub enum Error { - /// Error that could occur on the UDP-socket - // NB: NetworkError does not implement std::error::Error and cannot be used as a cause. - // But in order to get _some_ useful diagnostics out of it we format it in the message. - #[error(display = "UDP-error occurred: {}", _0)] - UdpError(laminar::ErrorKind), - /// Error that could occur whit IO. - #[error(display = "IO-error occurred")] - IoError(#[cause] io::Error), - /// Error that could occur when serializing whit `bincode` - #[error(display = "Serialization error occurred")] - SerializeError(#[cause] bincode::Error), - /// Error that could occur when sending an `ServerSocketEvent` to some channel. - #[error(display = "Channel send error occurred")] - ChannelSendError(#[cause] crossbeam_channel::SendError), - #[error(display = "Some error has occurred")] - #[doc(hidden)] - __Nonexhaustive, -} - -impl From for Error { - fn from(e: io::Error) -> Error { - Error::IoError(e) - } -} - -impl From> for Error { - fn from(e: crossbeam_channel::SendError) -> Error { - Error::ChannelSendError(e) - } -} - -impl From for Error { - fn from(e: laminar::ErrorKind) -> Error { - Error::UdpError(e) - } -} - -impl From for Error { - fn from(e: bincode::Error) -> Error { - Error::SerializeError(e) - } -} diff --git a/amethyst_network/src/lib.rs b/amethyst_network/src/lib.rs index 07c51479cd..3efcb2e06f 100644 --- a/amethyst_network/src/lib.rs +++ b/amethyst_network/src/lib.rs @@ -1,141 +1,5 @@ -//! Provides a client-server networking architecture to amethyst. +//! Provides a toolbox of networking utilities, resources, components, and systems to amethyst. +//! The library is segmented into the simulation module and, eventually, various client library +//! modules. Soon, we will also provide an HTTP client library. -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - rust_2018_compatibility -)] -#![warn(clippy::all)] -#![allow(clippy::new_without_default)] - -pub use crate::{ - bundle::NetworkBundle, - connection::{ConnectionState, NetConnection, NetIdentity}, - error::Result, - net_event::{NetEvent, NetPacket}, - network_socket::NetSocketSystem, - server::{Host, ServerConfig}, -}; - -use std::net::SocketAddr; - -use bincode::{deserialize, serialize}; -use laminar::Packet; -use serde::{de::DeserializeOwned, Serialize}; - -mod bundle; -mod connection; -mod error; -mod net_event; -mod network_socket; -mod server; -mod test; - -/// Attempts to serialize the given `NetEvent` and returns a laminar packet. -/// Reliable ordered will be used by default. -fn serialize_event(event: NetEvent, addr: SocketAddr) -> Result -where - E: Serialize, -{ - match serialize(&event) { - Ok(packet) => Ok(Packet::reliable_ordered(addr, packet, None)), - Err(e) => Err(e.into()), - } -} - -/// Attempts to serialize the given packet and returns a laminar packet. -fn serialize_packet(packet: NetPacket, addr: SocketAddr) -> Result -where - T: Serialize, -{ - let ser = serialize(&packet.content()); - match ser { - Ok(payload) => Ok(match packet.delivery_guarantee() { - net_event::DeliveryGuarantee::Unreliable => match packet.ordering_guarantee() { - net_event::OrderingGuarantee::None => Packet::unreliable(addr, payload), - net_event::OrderingGuarantee::Sequenced(s) => { - Packet::unreliable_sequenced(addr, payload, s) - } - _ => unreachable!( - "Can not apply the guarantees: {:?}, {:?} to the packet.", - packet.ordering_guarantee(), - packet.delivery_guarantee() - ), - }, - net_event::DeliveryGuarantee::Reliable => match packet.ordering_guarantee() { - net_event::OrderingGuarantee::None => Packet::reliable_unordered(addr, payload), - net_event::OrderingGuarantee::Sequenced(s) => { - Packet::reliable_sequenced(addr, payload, s) - } - net_event::OrderingGuarantee::Ordered(o) => { - Packet::reliable_ordered(addr, payload, o) - } - }, - }), - Err(e) => Err(e.into()), - } -} - -// Attempts to deserialize an event from the raw byte data. -fn deserialize_event(data: &[u8]) -> Result -where - T: DeserializeOwned, -{ - Ok(deserialize::(data)?) -} - -#[cfg(test)] -mod tests { - use crate::{deserialize_event, net_event::NetPacket, serialize_packet}; - use laminar::{DeliveryGuarantee, OrderingGuarantee}; - use std::net::SocketAddr; - - #[test] - fn can_serialize_packets() { - let content = "abc".to_string(); - let packet1 = NetPacket::reliable_unordered(content.clone()); - let packet2 = NetPacket::reliable_ordered(content.clone(), None); - let packet3 = NetPacket::reliable_sequenced(content.clone(), None); - let packet4 = NetPacket::unreliable(content.clone()); - let packet5 = NetPacket::unreliable_sequenced(content.clone(), None); - - let addr: SocketAddr = "127.0.0.1:1234".parse().unwrap(); - - let serialized_packet1 = serialize_packet(packet1, addr).unwrap(); - let serialized_packet2 = serialize_packet(packet2, addr).unwrap(); - let serialized_packet3 = serialize_packet(packet3, addr).unwrap(); - let serialized_packet4 = serialize_packet(packet4, addr).unwrap(); - let serialized_packet5 = serialize_packet(packet5, addr).unwrap(); - - // assure correct guarantees - assert!( - serialized_packet1.delivery_guarantee() == DeliveryGuarantee::Reliable - && serialized_packet1.order_guarantee() == OrderingGuarantee::None - ); - assert!( - serialized_packet2.delivery_guarantee() == DeliveryGuarantee::Reliable - && serialized_packet2.order_guarantee() == OrderingGuarantee::Ordered(None) - ); - assert!( - serialized_packet3.delivery_guarantee() == DeliveryGuarantee::Reliable - && serialized_packet3.order_guarantee() == OrderingGuarantee::Sequenced(None) - ); - assert!( - serialized_packet4.delivery_guarantee() == DeliveryGuarantee::Unreliable - && serialized_packet4.order_guarantee() == OrderingGuarantee::None - ); - assert!( - serialized_packet5.delivery_guarantee() == DeliveryGuarantee::Unreliable - && serialized_packet5.order_guarantee() == OrderingGuarantee::Sequenced(None) - ); - } - - #[test] - fn can_deserialize_event() { - let result = - deserialize_event::>(&[3, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99]).unwrap(); - - assert_eq!(result.content(), &"abc".to_string()); - } -} +pub mod simulation; diff --git a/amethyst_network/src/net_event.rs b/amethyst_network/src/net_event.rs deleted file mode 100644 index 4f6319e627..0000000000 --- a/amethyst_network/src/net_event.rs +++ /dev/null @@ -1,365 +0,0 @@ -//! The network events that are passed from machine to machine, and within the ECS event handling system. -//! NetEvent are passed through the network -//! NetOwnedEvent are passed through the ECS, and contains the event's source (remote connection, usually). - -use crate::Result; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::net::SocketAddr; - -/// Network events which you can send or and receive from an endpoint. -// TODO, Connect, connection refused, disconnect, disconnected -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum NetEvent { - /// Will be fired when a client connected. - /// When this event occurs the `NetConnection` with this address was already automatically added to the world. - Connected(SocketAddr), - /// Will be fired when a client was disconnected. - /// If this happens consider removing the `NetConnection` with this address from the world. - Disconnected(SocketAddr), - /// Send a packet to all connected clients - Packet(NetPacket), - #[doc(hidden)] - __Nonexhaustive, -} - -impl NetEvent -where - T: Serialize + DeserializeOwned, -{ - pub(crate) fn from_packet(packet: laminar::Packet) -> Result { - match crate::deserialize_event::(packet.payload()) { - Ok(event) => { - let net_event: NetEvent = NetEvent::Packet(match packet.delivery_guarantee() { - laminar::DeliveryGuarantee::Unreliable => match packet.order_guarantee() { - laminar::OrderingGuarantee::None => NetPacket::::unreliable(event), - laminar::OrderingGuarantee::Sequenced(s) => { - NetPacket::unreliable_sequenced(event, s) - } - _ => panic!("This is in no way possible"), - }, - laminar::DeliveryGuarantee::Reliable => match packet.order_guarantee() { - laminar::OrderingGuarantee::None => NetPacket::reliable_unordered(event), - laminar::OrderingGuarantee::Sequenced(s) => { - NetPacket::reliable_sequenced(event, s) - } - laminar::OrderingGuarantee::Ordered(o) => { - NetPacket::reliable_ordered(event, o) - } - }, - }); - - Ok(net_event) - } - Err(e) => Err(e), - } - } -} - -/// Enum to specify how a packet should be arranged. -#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] -pub(crate) enum OrderingGuarantee { - /// No arranging will be done. - None, - /// Packets will be arranged in sequence. - Sequenced(Option), - /// Packets will be arranged in order. - Ordered(Option), -} - -/// Enum to specify how a packet should be delivered. -#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] -pub(crate) enum DeliveryGuarantee { - /// Packet may or may not be delivered - Unreliable, - /// Packet will be delivered - Reliable, -} - -impl From for OrderingGuarantee { - fn from(ordering: laminar::OrderingGuarantee) -> Self { - match ordering { - laminar::OrderingGuarantee::None => OrderingGuarantee::None, - laminar::OrderingGuarantee::Sequenced(s) => OrderingGuarantee::Sequenced(s), - laminar::OrderingGuarantee::Ordered(o) => OrderingGuarantee::Ordered(o), - } - } -} - -impl From for laminar::OrderingGuarantee { - fn from(ordering: OrderingGuarantee) -> Self { - match ordering { - OrderingGuarantee::None => laminar::OrderingGuarantee::None, - OrderingGuarantee::Sequenced(s) => laminar::OrderingGuarantee::Sequenced(s), - OrderingGuarantee::Ordered(o) => laminar::OrderingGuarantee::Ordered(o), - } - } -} - -impl From for laminar::DeliveryGuarantee { - fn from(delivery: DeliveryGuarantee) -> Self { - match delivery { - DeliveryGuarantee::Unreliable => laminar::DeliveryGuarantee::Unreliable, - DeliveryGuarantee::Reliable => laminar::DeliveryGuarantee::Reliable, - } - } -} - -impl Default for OrderingGuarantee { - fn default() -> Self { - OrderingGuarantee::None - } -} - -impl Default for DeliveryGuarantee { - fn default() -> Self { - DeliveryGuarantee::Unreliable - } -} - -/// Represents a packet which could have any serializable payload. -/// -/// A packet could have reliability guarantees to specify how it should be delivered and processed. -/// -/// | Reliability Type | Packet Drop | Packet Duplication | Packet Order | Packet Fragmentation |Packet Delivery| -/// | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: -/// | **Unreliable Unordered** | Yes | Yes | No | No | No -/// | **Unreliable Sequenced** | Yes | No | Sequenced | No | No -/// | **Reliable Unordered** | No | No | No | Yes | Yes -/// | **Reliable Ordered** | No | No | Ordered | Yes | Yes -/// | **Reliable Sequenced** | No | No | Sequenced | Yes | Yes -/// -/// You are able to send packets with any the above guarantees. -/// -/// For more information please have a look at: https://amethyst.github.io/laminar/docs/reliability/reliability.html -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct NetPacket { - content: T, - #[serde(skip)] - ordering_guarantee: OrderingGuarantee, - #[serde(skip)] - delivery_guarantee: DeliveryGuarantee, -} - -impl NetPacket { - /// Create a new unreliable packet with the given content. - /// - /// Unreliable: Packets can be dropped, duplicated or arrive without order. - /// - /// **Details** - /// - /// | Packet Drop | Packet Duplication | Packet Order | Packet Fragmentation | Packet Delivery | - /// | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | - /// | Yes | Yes | No | No | No | - /// - /// Basically just bare UDP. The packet may or may not be delivered. - pub fn unreliable(content: T) -> NetPacket { - NetPacket { - ordering_guarantee: OrderingGuarantee::None, - delivery_guarantee: DeliveryGuarantee::Unreliable, - content, - } - } - - /// Create a new unreliable sequenced packet with the given content. - /// - /// Unreliable Sequenced; Packets can be dropped, but could not be duplicated and arrive in sequence. - /// - /// *Details* - /// - /// | Packet Drop | Packet Duplication | Packet Order | Packet Fragmentation | Packet Delivery | - /// | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | - /// | Yes | Yes | Sequenced | No | No | - /// - /// Basically just bare UDP, free to be dropped, but has some sequencing to it so that only the newest packets are kept. - pub fn unreliable_sequenced(content: T, stream_id: Option) -> NetPacket { - NetPacket { - ordering_guarantee: OrderingGuarantee::Sequenced(stream_id), - delivery_guarantee: DeliveryGuarantee::Unreliable, - content, - } - } - - /// Create a new packet with the given content. - /// Reliable; All packets will be sent and received, but without order. - /// - /// *Details* - /// - /// | Packet Drop | Packet Duplication | Packet Order | Packet Fragmentation | Packet Delivery | - /// | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | - /// | No | No | No | Yes | Yes | - /// - /// Basically this is almost TCP without ordering of packets. - pub fn reliable_unordered(content: T) -> NetPacket { - NetPacket { - ordering_guarantee: OrderingGuarantee::None, - delivery_guarantee: DeliveryGuarantee::Reliable, - content, - } - } - - /// Create a new packet with the given content and optional stream on which the ordering will be done. - /// - /// Reliable; All packets will be sent and received, with order. - /// - /// *Details* - /// - /// | Packet Drop | Packet Duplication | Packet Order | Packet Fragmentation | Packet Delivery | - /// | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | - /// | No | No | Ordered | Yes | Yes | - /// - /// Basically this is almost TCP-like with ordering of packets. - /// - /// # Remark - /// - When `stream_id` is specified as `None` the default stream will be used; if you are not sure what this is you can leave it at `None`. - pub fn reliable_ordered(content: T, stream_id: Option) -> NetPacket { - NetPacket { - ordering_guarantee: OrderingGuarantee::Ordered(stream_id), - delivery_guarantee: DeliveryGuarantee::Reliable, - content, - } - } - - /// Create a new packet with the given content and optional stream on which the sequencing will be done. - /// - /// Reliable; All packets will be sent and received, but arranged in sequence. - /// Which means that only the newest packets will be let through, older packets will be received but they won't get to the user. - /// - /// *Details* - /// - /// | Packet Drop | Packet Duplication | Packet Order | Packet Fragmentation | Packet Delivery | - /// | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | - /// | Yes | No | Sequenced | Yes | Yes | - /// - /// Basically this is almost TCP-like but then sequencing instead of ordering. - /// - /// # Remark - /// - When `stream_id` is specified as `None` the default stream will be used; if you are not sure what this is you can leave it at `None`. - pub fn reliable_sequenced(content: T, stream_id: Option) -> NetPacket { - NetPacket { - ordering_guarantee: OrderingGuarantee::Sequenced(stream_id), - delivery_guarantee: DeliveryGuarantee::Reliable, - content, - } - } - - /// Returns if this event is reliable. - /// - /// Each net event type is either reliable or unreliable. - /// Reliable events always reach their destination, unreliable events may be lost. - pub fn is_reliable(&self) -> bool { - self.delivery_guarantee == DeliveryGuarantee::Reliable - } - - /// Returns if this event is unreliable. - /// - /// Each net event type is either reliable or unreliable. - /// Reliable events always reach their destination, unreliable events may be lost. - pub fn is_unreliable(&self) -> bool { - self.delivery_guarantee == DeliveryGuarantee::Unreliable - } - - /// Returns whether this event is an ordered event. - pub fn is_ordered(&self) -> bool { - if let OrderingGuarantee::Ordered(_) = self.ordering_guarantee { - return true; - } - false - } - - /// Returns whether this event is an sequenced event. - pub fn is_sequenced(&self) -> bool { - if let OrderingGuarantee::Sequenced(_) = self.ordering_guarantee { - return true; - } - false - } - - /// Return if this event is neither ordered or sequenced. - pub fn is_unordered(&self) -> bool { - self.ordering_guarantee == OrderingGuarantee::None - } - - /// Returns a immutable reference to the content. - pub fn content(&self) -> &T { - &self.content - } - - /// Returns a immutable reference to the content. - pub fn content_mut(&mut self) -> &mut T { - &mut self.content - } - - /// Returns the ordering guarantee - pub(crate) fn ordering_guarantee(&self) -> OrderingGuarantee { - self.ordering_guarantee - } - - /// Returns the delivery guarantee - pub(crate) fn delivery_guarantee(&self) -> DeliveryGuarantee { - self.delivery_guarantee - } -} - -#[cfg(test)] -mod tests { - use crate::net_event::NetPacket; - - #[test] - fn assure_creation_unreliable_packet() { - let packet = NetPacket::unreliable(test_payload()); - - assert_eq!(packet.content(), &test_payload()); - assert_eq!(packet.is_ordered(), false); - assert_eq!(packet.is_sequenced(), false); - assert_eq!(packet.is_reliable(), false); - assert_eq!(packet.is_unreliable(), true); - } - - #[test] - fn assure_creation_unreliable_sequenced() { - let packet = NetPacket::unreliable_sequenced(test_payload(), Some(1)); - - assert_eq!(packet.content(), &test_payload()); - assert_eq!(packet.is_ordered(), false); - assert_eq!(packet.is_sequenced(), true); - assert_eq!(packet.is_reliable(), false); - assert_eq!(packet.is_unreliable(), true); - } - - #[test] - fn assure_creation_reliable() { - let packet = NetPacket::reliable_unordered(test_payload()); - - assert_eq!(packet.content(), &test_payload()); - assert_eq!(packet.is_ordered(), false); - assert_eq!(packet.is_sequenced(), false); - assert_eq!(packet.is_reliable(), true); - assert_eq!(packet.is_unreliable(), false); - } - - #[test] - fn assure_creation_reliable_ordered() { - let packet = NetPacket::reliable_ordered(test_payload(), Some(1)); - - assert_eq!(packet.content(), &test_payload()); - assert_eq!(packet.is_ordered(), true); - assert_eq!(packet.is_sequenced(), false); - assert_eq!(packet.is_reliable(), true); - assert_eq!(packet.is_unreliable(), false); - } - - #[test] - fn assure_creation_reliable_sequence() { - let packet = NetPacket::reliable_sequenced(test_payload(), Some(1)); - - assert_eq!(packet.content(), &test_payload()); - assert_eq!(packet.is_ordered(), false); - assert_eq!(packet.is_sequenced(), true); - assert_eq!(packet.is_reliable(), true); - assert_eq!(packet.is_unreliable(), false); - } - - fn test_payload() -> Vec { - b"test".to_vec() - } -} diff --git a/amethyst_network/src/network_socket.rs b/amethyst_network/src/network_socket.rs deleted file mode 100644 index 94433367c2..0000000000 --- a/amethyst_network/src/network_socket.rs +++ /dev/null @@ -1,200 +0,0 @@ -//! The network send and receive System - -use std::{clone::Clone, net::SocketAddr, thread}; - -use amethyst_core::ecs::{Entities, Join, System, WriteStorage}; - -use crossbeam_channel::{Receiver, Sender}; -use laminar::{Packet, SocketEvent}; -use log::{error, warn}; -use serde::{de::DeserializeOwned, Serialize}; - -use super::{ - error::Result, - serialize_event, serialize_packet, - server::{Host, ServerConfig}, - ConnectionState, NetConnection, NetEvent, -}; -use std::io::{Error, ErrorKind}; - -enum InternalSocketEvent { - SendEvents { - target: SocketAddr, - events: Vec>, - }, - Stop, -} - -/// The System managing the network state from `NetConnections`. -/// -/// This system has a few responsibilities. -/// -/// - Reading to send packets from `NetConnection` and sending those over to some remote endpoint. -/// - Listening for incoming packets and queue the received packets (`NetEvent::Packet(...)`) on the accompanying `NetConnection`. -/// -/// This system is able to create a `NetConnection` and add those to the world when a new client connects. -/// (This behavior might not be desired and can therefore be deactivated in the configuration). -/// -/// In both cases when a client connects and disconnects a `NetEvent::Connected` or `NetEvent::Disconnected` will be queued on accompanying `NetConnection` -/// -/// - `T` corresponds to the network event type. -#[allow(missing_debug_implementations)] -pub struct NetSocketSystem -where - E: PartialEq, -{ - // sender on which you can queue packets to send to some endpoint. - event_sender: Sender>, - // receiver from which you can read received packets. - event_receiver: Receiver, - // the configuration with which you can configure the network behaviour. - config: ServerConfig, -} - -impl NetSocketSystem -where - E: Serialize + PartialEq + Send + 'static, -{ - /// Creates a `NetSocketSystem` and binds the Socket on the ip and port added in parameters. - pub fn new(config: ServerConfig) -> Result { - if config.udp_socket_addr.port() < 1024 { - // Just warning the user here, just in case they want to use the root port. - warn!("Using a port below 1024, this will require root permission and should not be done."); - } - - let server = Host::run(&config)?; - - let udp_send_handle = server.udp_send_handle(); - let udp_receive_handle = server.udp_receive_handle(); - - let event_sender = NetSocketSystem::::start_sending(udp_send_handle); - - Ok(NetSocketSystem { - event_sender, - event_receiver: udp_receive_handle, - config, - }) - } - - /// Start a thread to send all queued packets. - fn start_sending(sender: Sender) -> Sender> { - let (event_sender, event_receiver) = crossbeam_channel::unbounded(); - - thread::spawn(move || loop { - for control_event in event_receiver.try_iter() { - match control_event { - InternalSocketEvent::SendEvents { target, events } => { - for ev in events { - let serialize_result = match ev { - NetEvent::Packet(packet) => serialize_packet(packet, target), - NetEvent::Connected(addr) => serialize_event(ev, addr), - NetEvent::Disconnected(addr) => serialize_event(ev, addr), - NetEvent::__Nonexhaustive => { - Err(Error::new(ErrorKind::Other, "Net event does not exist.") - .into()) - } - }; - - match serialize_result { - Ok(packet) => match sender.send(packet) { - Ok(_qty) => {} - Err(e) => { - error!("Failed to send data to network socket: {}", e) - } - }, - Err(e) => error!("Cannot serialize packet. Reason: {}", e), - } - } - } - InternalSocketEvent::Stop => { - break; - } - } - } - }); - - event_sender - } -} - -impl<'a, E> System<'a> for NetSocketSystem -where - E: Send + Sync + Serialize + Clone + DeserializeOwned + PartialEq + 'static, -{ - type SystemData = (WriteStorage<'a, NetConnection>, Entities<'a>); - - fn run(&mut self, (mut net_connections, entities): Self::SystemData) { - #[cfg(feature = "profiler")] - profile_scope!("net_socket_system"); - - for connection in (&mut net_connections).join() { - match connection.state { - ConnectionState::Connected | ConnectionState::Connecting => { - self.event_sender - .send(InternalSocketEvent::SendEvents { - target: connection.target_addr, - events: connection.send_buffer_early_read().cloned().collect(), - }) - .expect("Unreachable: Channel will be alive until a stop event is sent"); - } - ConnectionState::Disconnected => { - self.event_sender - .send(InternalSocketEvent::Stop) - .expect("Already sent a stop event to the channel"); - } - } - } - - for (counter, socket_event) in self.event_receiver.try_iter().enumerate() { - match socket_event { - SocketEvent::Packet(packet) => { - let from_addr = packet.addr(); - - match NetEvent::::from_packet(packet) { - Ok(event) => { - for connection in (&mut net_connections).join() { - if connection.target_addr == from_addr { - connection.receive_buffer.single_write(event.clone()); - } - } - } - Err(e) => error!( - "Failed to deserialize an incoming network event: {} From source: {:?}", - e, from_addr - ), - } - } - SocketEvent::Connect(addr) => { - if self.config.create_net_connection_on_connect { - let mut connection: NetConnection = NetConnection::new(addr); - connection - .receive_buffer - .single_write(NetEvent::Connected(addr)); - - entities - .build_entity() - .with(connection, &mut net_connections) - .build(); - } - } - SocketEvent::Timeout(timeout_addr) => { - for connection in (&mut net_connections).join() { - if connection.target_addr == timeout_addr { - // we can't remove the entity from the world here because it could still have events in it's buffer. - connection - .receive_buffer - .single_write(NetEvent::Disconnected(timeout_addr)); - } - } - } - }; - - // this will prevent our system to be stuck in the iterator. - // After 10000 packets we will continue and leave the other packets for the next run. - // eventually some congestion prevention should be done. - if counter >= self.config.max_throughput as usize { - break; - } - } - } -} diff --git a/amethyst_network/src/server/config.rs b/amethyst_network/src/server/config.rs deleted file mode 100644 index 496741c509..0000000000 --- a/amethyst_network/src/server/config.rs +++ /dev/null @@ -1,49 +0,0 @@ -use laminar::Config; -use std::net::SocketAddr; - -#[derive(Clone)] -/// The configuration used for the networking system. -#[allow(missing_debug_implementations)] // TODO: revisit this. Laminar is missing Debug on a lot of things -pub struct ServerConfig { - /// Address at which the UDP server will listen for incoming packets. - pub udp_socket_addr: SocketAddr, - /// Specifies what the maximal packets that could be handled by the server. - /// This value is meant for preventing some loops to read infinitely long when many packets are send and received. - /// This value is by default 5000. - pub max_throughput: u16, - // If enabled a `NetConnection` will be automatically added to the world when a client connects. - /// Make this property 'false' you prevent this behaviour. - /// This property is enabled by default. - pub create_net_connection_on_connect: bool, - /// Allows you to configure laminar its behaviour. - pub laminar_config: Config, -} - -impl ServerConfig { - /// Construct the config with the specified configuration options. - pub fn new( - ip: SocketAddr, - max_throughput: u16, - create_net_connection_on_connect: bool, - laminar_config: Config, - ) -> ServerConfig { - ServerConfig { - udp_socket_addr: ip, - max_throughput, - create_net_connection_on_connect, - laminar_config, - } - } -} - -impl Default for ServerConfig { - fn default() -> Self { - ServerConfig { - // by passing in :0 port the OS will give an available port. - udp_socket_addr: "0.0.0.0:0".parse().unwrap(), - max_throughput: 5000, - create_net_connection_on_connect: true, - laminar_config: Config::default(), - } - } -} diff --git a/amethyst_network/src/server/host.rs b/amethyst_network/src/server/host.rs deleted file mode 100644 index f9d8b30eaa..0000000000 --- a/amethyst_network/src/server/host.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! This module is about the communication with this host to some other endpoints. -//! -//! 1. Sending Data -//! 2. Receiving Data -//! 3. Broadcasting - -use crate::{error::Result, server::ServerConfig}; -use crossbeam_channel::{Receiver, Sender}; -use laminar::{Packet, Socket, SocketEvent}; -use std::thread; - -/// 'Host' abstracts Laminar udp sockets away. -#[allow(missing_debug_implementations)] // TODO: Revisit this, laminar doesn't implement debug anywhere -pub struct Host { - packet_sender: Sender, - packet_receiver: Receiver, -} - -impl Host { - /// This will start and return an instance of the host. - /// - /// The method uses the config provided when creating a `host` instance. - pub fn run(server_config: &ServerConfig) -> Result { - let (mut socket, packet_sender, packet_receiver) = Socket::bind_with_config( - server_config.udp_socket_addr, - server_config.laminar_config.clone(), - )?; - - thread::spawn(move || { - socket.start_polling().unwrap(); - }); - - Ok(Host { - packet_sender, - packet_receiver, - }) - } - - /// Get the handle to the internals of the UDP-receiving threat. - pub fn udp_receive_handle(&self) -> Receiver { - self.packet_receiver.clone() - } - - /// Get the handle to the internals of the UDP-sending thread. - pub fn udp_send_handle(&self) -> Sender { - self.packet_sender.clone() - } - - /// Schedule a UDP-packet for sending. - pub fn send_udp(&mut self, packet: Packet) -> Result<()> { - self.packet_sender.send(packet)?; - Ok(()) - } -} diff --git a/amethyst_network/src/server/mod.rs b/amethyst_network/src/server/mod.rs deleted file mode 100644 index 8de8669092..0000000000 --- a/amethyst_network/src/server/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod config; -mod host; - -pub use self::{config::ServerConfig, host::Host}; diff --git a/amethyst_network/src/simulation.rs b/amethyst_network/src/simulation.rs new file mode 100644 index 0000000000..868bab53eb --- /dev/null +++ b/amethyst_network/src/simulation.rs @@ -0,0 +1,19 @@ +//! Module containing various utilities to run a client/server-based network simulation. Expect +//! more utilities to make their way into this module. e.g. "Component synchronization", +//! "Matchmaking", etc. + +mod client; +mod events; +mod message; +mod requirements; +mod resource; +mod timing; +mod transport; + +pub use events::NetworkSimulationEvent; +pub use message::Message; +pub use requirements::{DeliveryRequirement, UrgencyRequirement}; +pub use resource::NetworkSimulationResource; +pub use timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}; +pub use transport::laminar; +pub use transport::udp; diff --git a/amethyst_network/src/simulation/client.rs b/amethyst_network/src/simulation/client.rs new file mode 100644 index 0000000000..025b1022ab --- /dev/null +++ b/amethyst_network/src/simulation/client.rs @@ -0,0 +1,19 @@ +use std::net::SocketAddr; + +/// Stores information about a known client for the `NetworkSimulationResource`. +#[derive(Clone, Debug)] +pub struct Client { + addr: SocketAddr, +} + +impl Client { + /// Create and return a new Client + pub fn new(addr: SocketAddr) -> Self { + Self { addr } + } + + /// Return the address of the Client + pub fn addr(&self) -> SocketAddr { + self.addr + } +} diff --git a/amethyst_network/src/simulation/events.rs b/amethyst_network/src/simulation/events.rs new file mode 100644 index 0000000000..1242c91c1c --- /dev/null +++ b/amethyst_network/src/simulation/events.rs @@ -0,0 +1,13 @@ +use bytes::Bytes; +use std::net::SocketAddr; + +/// Events which can be received from the network. +#[derive(Debug)] +pub enum NetworkSimulationEvent { + // A message was received from a remote client + Message(SocketAddr, Bytes), + // A new host has connected to us + Connect(SocketAddr), + // A host has disconnected from us + Disconnect(SocketAddr), +} diff --git a/amethyst_network/src/simulation/message.rs b/amethyst_network/src/simulation/message.rs new file mode 100644 index 0000000000..5848409d78 --- /dev/null +++ b/amethyst_network/src/simulation/message.rs @@ -0,0 +1,29 @@ +use super::requirements::{DeliveryRequirement, UrgencyRequirement}; +use bytes::Bytes; + +/// Structure used to hold message payloads before they are consumed and sent by an underlying +/// NetworkSystem. +#[derive(Debug, PartialEq, Eq)] +pub struct Message { + /// The serialized payload itself. + pub(crate) payload: Bytes, + /// The requirement around whether or not this message should be resent if lost. + pub(crate) delivery: DeliveryRequirement, + /// The requirement around when this message should be sent. + pub(crate) urgency: UrgencyRequirement, +} + +impl Message { + /// Creates and returns a new Message. + pub(crate) fn new( + payload: &[u8], + delivery: DeliveryRequirement, + urgency: UrgencyRequirement, + ) -> Self { + Self { + payload: Bytes::from(payload), + delivery, + urgency, + } + } +} diff --git a/amethyst_network/src/simulation/requirements.rs b/amethyst_network/src/simulation/requirements.rs new file mode 100644 index 0000000000..9045ec483b --- /dev/null +++ b/amethyst_network/src/simulation/requirements.rs @@ -0,0 +1,36 @@ +/// Specification of the desired delivery guarantee on a message. All examples will use the +/// following: 1, 2, 3, 4, 5, 6 sent from the server. 5, 1, 4, 2, 3 received by the client. Packet +/// 6 was lost on initial send. +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] +pub enum DeliveryRequirement { + /// Messages may not be delivered. + /// Client receives 5, 1, 4, 2, 3 + Unreliable, + /// Messages may not be delivered and the client is guaranteed to only receive the newest + /// messages. + /// Client receives 5 + UnreliableSequenced(Option), + /// Messages must all be delivered. + /// Client receives 5, 1, 4, 2, 3, 6 + Reliable, + /// Messages must all be delivered but only the newest messages are returned to the client. + /// Client receives 5, 6 + ReliableSequenced(Option), + /// Messages must all be delivered and returned to the client in the order they were sent. + /// This takes an optional "stream_id" which can be used if the underlying transport supports + /// multiplexed streams. By specifying "None" for the stream_id, the transport can decide + /// where it wants to put the message. + /// Client receives 1, 2, 3, 4, 5, 6 + ReliableOrdered(Option), +} + +/// Specification of urgency of the sending of a message. Typically we'll want to send messages +/// on simulation tick but the option to send messages immediately is available. +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] +pub enum UrgencyRequirement { + /// Message will be sent based on the current configuration of the simulation frame rate and + /// the message send rate. + OnTick, + /// Message will be sent as soon as possible. + Immediate, +} diff --git a/amethyst_network/src/simulation/resource.rs b/amethyst_network/src/simulation/resource.rs new file mode 100644 index 0000000000..730964fcf8 --- /dev/null +++ b/amethyst_network/src/simulation/resource.rs @@ -0,0 +1,210 @@ +use super::{ + client::Client, + message::Message, + requirements::{DeliveryRequirement, UrgencyRequirement}, + transport::socket::Socket, +}; +use std::{collections::VecDeque, net::SocketAddr}; + +/// Resource serving as the owner of the underlying socket and the queue of messages to be sent. +pub struct NetworkSimulationResource { + socket: Option, + is_server: bool, + server_addr: Option, + clients: Vec, + messages: VecDeque, +} + +impl NetworkSimulationResource { + /// Create a new `NetworkSimulationResource` as a client + pub fn new_client(server_addr: SocketAddr) -> Self { + Self { + socket: None, + server_addr: Some(server_addr), + clients: Vec::new(), + messages: VecDeque::new(), + is_server: false, + } + } + + /// Create a new `NetworkSimulationResource` as a server + pub fn new_server() -> Self { + Self { + socket: None, + server_addr: None, + clients: Vec::new(), + messages: VecDeque::new(), + is_server: true, + } + } + + /// Add a number of trusted clients to the `NetworkSimulationResource` for use as a server + pub fn with_trusted_clients(mut self, clients: &[SocketAddr]) -> Self { + self.clients = clients.iter().map(|addr| Client::new(*addr)).collect(); + self + } + + /// Returns a slice of the tracked clients + pub fn clients(&self) -> &[Client] { + &self.clients + } + + /// Set the server address + pub fn set_server_addr(&mut self, server_addr: Option) { + self.server_addr = server_addr; + } + + /// Return a mutable reference to the socket if there is one configured. + pub fn get_socket_mut(&mut self) -> Option<&mut S> { + self.socket.as_mut() + } + + /// Set the bound socket to the `NetworkSimulationResource` + pub fn set_socket(&mut self, socket: S) { + self.socket = Some(socket); + } + + /// Drops the socket from the `NetworkSimulationResource` + pub fn drop_socket(&mut self) { + self.socket = None; + } + + /// Returns whether or not the `NetworkSimulationResource` has a bound socket + pub fn has_socket(&self) -> bool { + self.socket.is_some() + } + + /// Returns the server address if one was set + pub fn server_addr(&self) -> Option { + self.server_addr + } + + /// Returns whether or not this `NetworkSimulationResource` was created as a 'server' + pub fn is_server(&self) -> bool { + self.is_server + } + + /// Returns whether or not this `NetworkSimulationResource` was created as a 'client' + pub fn is_client(&self) -> bool { + !self.is_server + } + + /// Create a `Message` with the default guarantees provided by the `Socket` implementation and + /// pushes it onto the messages queue to be sent on next sim tick. + pub fn send(&mut self, payload: &[u8]) { + self.send_with_requirements( + payload, + S::default_requirement(), + UrgencyRequirement::OnTick, + ); + } + + /// Create a `Message` with the default guarantees provided by the `Socket` implementation and + /// pushes it onto the messages queue to be sent immediately. + pub fn send_immediate(&mut self, payload: &[u8]) { + self.send_with_requirements( + payload, + S::default_requirement(), + UrgencyRequirement::Immediate, + ); + } + + /// Create and queue a `Message` with the specified guarantee + pub fn send_with_requirements( + &mut self, + payload: &[u8], + delivery: DeliveryRequirement, + timing: UrgencyRequirement, + ) { + let message = Message::new(payload, delivery, timing); + self.messages.push_back(message); + } + + /// Returns true if there are messages enqueued to be sent. + pub fn has_messages(&self) -> bool { + !self.messages.is_empty() + } + + /// Drains the messages queue and returns the drained messages + pub fn drain_messages(&mut self, mut filter: impl FnMut(&mut Message) -> bool) -> Vec { + let mut drained = Vec::with_capacity(self.messages.len()); + let mut i = 0; + while i != self.messages.len() { + if filter(&mut self.messages[i]) { + if let Some(m) = self.messages.remove(i) { + drained.push(m); + } + } else { + i += 1; + } + } + drained + } +} + +impl Default for NetworkSimulationResource { + fn default() -> Self { + panic!( + "The `NetworkSimulationResource` resource MUST be created and added to the `Application` \ + before use." + ); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::simulation::transport::laminar::LaminarSocket; + + #[test] + fn test_send() { + let mut net = create_test_resource(); + net.send(test_payload()); + assert_eq!(net.messages.len(), 1); + let packet = &net.messages[0]; + // Default guarantee specified by the Laminar impl + assert_eq!(packet.delivery, DeliveryRequirement::ReliableOrdered(None)); + assert_eq!(packet.urgency, UrgencyRequirement::OnTick); + } + + #[test] + fn test_send_with_requirements() { + use DeliveryRequirement::*; + let mut net = create_test_resource(); + + let requirements = [ + Unreliable, + UnreliableSequenced(None), + Reliable, + ReliableSequenced(None), + ReliableOrdered(None), + ]; + + for req in requirements.iter().cloned() { + net.send_with_requirements(test_payload(), req, UrgencyRequirement::OnTick); + } + + assert_eq!(net.messages.len(), requirements.len()); + + for (i, req) in requirements.iter().enumerate() { + assert_eq!(net.messages[i].delivery, *req); + } + } + + #[test] + fn test_has_socket_and_with_socket() { + let mut net = create_test_resource(); + assert!(!net.has_socket()); + net.set_socket(LaminarSocket::bind_any().unwrap()); + assert!(net.has_socket()); + } + + fn test_payload() -> &'static [u8] { + b"test" + } + + fn create_test_resource() -> NetworkSimulationResource { + let addr = "127.0.0.1:3000".parse().unwrap(); + >::new_client(addr) + } +} diff --git a/amethyst_network/src/simulation/timing.rs b/amethyst_network/src/simulation/timing.rs new file mode 100644 index 0000000000..f067d3882e --- /dev/null +++ b/amethyst_network/src/simulation/timing.rs @@ -0,0 +1,148 @@ +//! Systems and resources to have a consistent, separate simulation frame rate from the ECS +//! frame rate. + +use amethyst_core::{ + ecs::{Read, System, Write}, + timing::Time, +}; +use std::{ops::RangeInclusive, time::Duration}; + +/// Default number of network simulation frames per second. +const DEFAULT_SIM_FRAME_RATE: u32 = 30; + +/// This system is used exclusively to update the state of the `NetworkSimulationTime` resource. +pub struct NetworkSimulationTimeSystem; + +impl<'s> System<'s> for NetworkSimulationTimeSystem { + type SystemData = (Write<'s, NetworkSimulationTime>, Read<'s, Time>); + + fn run(&mut self, (mut sim_time, game_time): Self::SystemData) { + sim_time.update_elapsed(game_time.delta_time()); + sim_time.reset_frame_lag(); + while sim_time.elapsed_duration() > sim_time.per_frame_duration() { + sim_time.increment_frame_number(); + } + } +} + +/// Resource to track the state of the network simulation separately from the ECS frame timings +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct NetworkSimulationTime { + /// The current simulation frame + frame_number: u32, + /// Accumulated duration since last simulation frame + elapsed_duration: Duration, + /// Duration per frame + per_frame_duration: Duration, + /// Determines how often we send messages. i.e. "Every N frames" where N is message_send_rate + message_send_rate: u8, + /// Number of frames behind the simulation is. This will usually be 0 or 1 if the ECS system + /// is keeping up + frame_lag: u32, +} + +impl NetworkSimulationTime { + /// Returns the simulation frame numbers needed to be run this game frame. + pub fn sim_frames_to_run(&self) -> RangeInclusive { + (self.frame_number + 1 - self.frame_lag)..=self.frame_number + } + + /// Determines whether or not to send a message in the current frame based on the + /// `message_send_rate` + pub fn should_send_message_this_frame(&self) -> bool { + self.should_send_message(self.frame_number) + } + + /// Determines whether or not to send a message based on the `message_send_rate` + pub fn should_send_message(&self, frame: u32) -> bool { + frame % u32::from(self.message_send_rate) == 0 + } + + /// Bumps the frame number + pub fn increment_frame_number(&mut self) { + self.frame_number += 1; + self.elapsed_duration -= self.per_frame_duration; + self.frame_lag += 1; + } + + /// Resets the frame lag + pub fn reset_frame_lag(&mut self) { + self.frame_lag = 0; + } + + /// Increase the `elapsed_duration` by the given duration + pub fn update_elapsed(&mut self, duration: Duration) { + self.elapsed_duration += duration; + } + + /// Returns the current simulation frame number + pub fn frame_number(&self) -> u32 { + self.frame_number + } + + /// Returns the total duration since the last simulation frame + pub fn elapsed_duration(&self) -> Duration { + self.elapsed_duration + } + + /// The duration between each simulation frame. This number is calculated when a frame rate + /// is set + pub fn per_frame_duration(&self) -> Duration { + self.per_frame_duration + } + + /// The rate at which messages should be sent over the network. + /// i.e. 'Every N frames' where N is `message_send_rate`. + pub fn message_send_rate(&self) -> u8 { + self.message_send_rate + } + + /// The number of frames behind the simulation is. This will usually be 0 or 1 if the ECS system + /// is keeping up + pub fn frame_lag(&self) -> u32 { + self.frame_lag + } + + /// Set the rate at which the network simulation progresses. Specified in hertz (frames/second). + pub fn with_sim_frame_rate(mut self, new_rate: u32) -> Self { + self.per_frame_duration = Duration::from_secs(1) / new_rate; + self + } + + /// Set the rate which messages are sent. Specified as "every N frames" where N is new_rate. + pub fn with_message_send_rate(mut self, new_rate: u8) -> Self { + self.message_send_rate = new_rate; + self + } +} + +impl Default for NetworkSimulationTime { + fn default() -> Self { + Self { + frame_number: 0, + elapsed_duration: Duration::from_secs(0), + // Default to 30 frames / second + per_frame_duration: Duration::from_secs(1) / DEFAULT_SIM_FRAME_RATE, + // Default to sending a message with every simulation frame + message_send_rate: 1, + // Default the lag to run so systems have a chance to run on the frame 0 + frame_lag: 1, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::Duration; + + #[test] + fn test_calculated_properties_and_getters() { + let time = NetworkSimulationTime::default().with_sim_frame_rate(20); + assert_eq!(time.frame_number(), 0); + assert_eq!(time.frame_lag(), 1); + assert_eq!(time.message_send_rate(), 1); + assert_eq!(time.per_frame_duration(), Duration::from_millis(50)); + assert_eq!(time.elapsed_duration(), Duration::from_millis(0)); + } +} diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs new file mode 100644 index 0000000000..f6d9804d73 --- /dev/null +++ b/amethyst_network/src/simulation/transport.rs @@ -0,0 +1,176 @@ +//! This module holds the underlying system implementations for each of the various transport +//! protocols. One important thing to note if you're implementing your own, the underlying sockets +//! MUST be non-blocking in order to play nicely with the ECS scheduler. + +pub mod laminar; +pub mod socket; +pub mod udp; + +const NETWORK_SIM_TIME_SYSTEM_NAME: &str = "simulation_time"; +const NETWORK_SEND_SYSTEM_NAME: &str = "network_send"; +const NETWORK_RECV_SYSTEM_NAME: &str = "network_recv"; +const NETWORK_POLL_SYSTEM_NAME: &str = "network_poll"; + +use crate::simulation::{ + transport::socket::Socket, Message, NetworkSimulationResource, NetworkSimulationTime, + UrgencyRequirement, +}; +use log::warn; +use std::net::SocketAddr; + +/// Shared set up code for implementations of `NetworkSendSystem`s +pub fn run_network_send_system( + net: &mut NetworkSimulationResource, + sim_time: &NetworkSimulationTime, + mut handle_send: impl FnMut(&mut T, SocketAddr, &Message) -> (), +) { + // If no socket configured, this system should be a no-op. + if !net.has_socket() { + if net.has_messages() { + warn!("Messages waiting to be sent but no socket configured."); + } + return; + } + + let messages = net.drain_messages(|message| { + message.urgency == UrgencyRequirement::Immediate + || sim_time.should_send_message_this_frame() + }); + + // If we have no messages to send, we're done here. + if messages.is_empty() { + return; + } + + // If we're a server, we need to broadcast messages to all connected clients. If we're + // just a client, we're just going to send to our designated server. + if net.is_server() { + let clients = net.clients().to_vec(); + let socket = net.get_socket_mut().expect("A socket should be configured"); + clients.iter().map(|client| client.addr()).for_each(|addr| { + for message in messages.iter() { + handle_send(socket, addr, message); + } + }); + } else { + let server_addr = net.server_addr(); + let socket = net.get_socket_mut().expect("A socket should be configured"); + server_addr.map(|addr| { + for message in messages.iter() { + handle_send(socket, addr, message); + } + }); + }; +} + +/// Shared set up code for implementations of `NetworkRecvSystem`s +pub fn run_network_recv_system( + net: &mut NetworkSimulationResource, + mut handle_recv: impl FnMut(&mut T) -> (), +) { + net.get_socket_mut().map(|socket| handle_recv(socket)); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::simulation::{ + DeliveryRequirement, NetworkSimulationResource, NetworkSimulationTime, + }; + use std::time::Duration; + + // If there are no messages to send, the handle_send function shouldn't be invoked + #[test] + fn run_network_send_system_no_op() { + let (mut net, sim_time) = setup_test(); + let mut call_count = 0; + run_network_send_system(&mut net, &sim_time, |_, _, _| { + call_count += 1; + }); + assert_eq!(call_count, 0); + } + + // Since we're on the first frame, the callback will be invoked immediately + #[test] + fn run_network_send_system_should_call_with_send() { + let (mut net, sim_time) = setup_test(); + + net.send(b"test"); + + let mut call_count = 0; + run_network_send_system(&mut net, &sim_time, |_, _, _| { + call_count += 1; + }); + assert_eq!(call_count, 1); + } + + // On the second frame, with message send rate of 2, the callback will not be invoked. + #[test] + fn run_network_send_system_should_not_call_on_next_frame() { + let (mut net, mut sim_time) = setup_test(); + + net.send(b"test"); + + sim_time.update_elapsed(Duration::from_secs(1)); + sim_time.increment_frame_number(); + + let mut call_count = 0; + run_network_send_system(&mut net, &sim_time, |_, _, _| { + call_count += 1; + }); + assert_eq!(call_count, 0); + } + + // On the second frame, with message send rate of 2, the callback will be invoked on messages + // with immediate urgency. + #[test] + fn run_network_send_system_should_call_on_next_frame_with_immediate() { + let (mut net, mut sim_time) = setup_test(); + + net.send_immediate(b"test"); + + sim_time.update_elapsed(Duration::from_secs(1)); + sim_time.increment_frame_number(); + + let mut call_count = 0; + run_network_send_system(&mut net, &sim_time, |_, _, _| { + call_count += 1; + }); + assert_eq!(call_count, 1); + } + + #[test] + fn run_network_send_system_should_broadcast_messages_in_server_mode() { + let (mut net, sim_time) = setup_test(); + net = net.with_trusted_clients(&[ + "127.0.0.1:9001".parse().unwrap(), + "127.0.0.1:9002".parse().unwrap(), + "127.0.0.1:9003".parse().unwrap(), + ]); + net.send(b"test"); + let mut call_count = 0; + run_network_send_system(&mut net, &sim_time, |_, _, _| { + call_count += 1; + }); + assert_eq!(call_count, 3); + } + + fn setup_test() -> (NetworkSimulationResource, NetworkSimulationTime) { + let socket = TestSocket {}; + let mut net = NetworkSimulationResource::new_server() + .with_trusted_clients(&["127.0.0.1:9001".parse().unwrap()]); + net.set_socket(socket); + + // Set up the sim_time to send every other frame + let sim_time = NetworkSimulationTime::default().with_message_send_rate(2); + (net, sim_time) + } + + struct TestSocket; + + impl Socket for TestSocket { + fn default_requirement() -> DeliveryRequirement { + DeliveryRequirement::Unreliable + } + } +} diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs new file mode 100644 index 0000000000..c504870572 --- /dev/null +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -0,0 +1,136 @@ +//! Network systems implementation backed by the laminar network protocol. +use crate::simulation::{ + events::NetworkSimulationEvent, + requirements::DeliveryRequirement, + resource::NetworkSimulationResource, + timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, + transport::{ + run_network_recv_system, run_network_send_system, socket::Socket, NETWORK_POLL_SYSTEM_NAME, + NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, + }, +}; +use amethyst_core::{ + bundle::SystemBundle, + ecs::{DispatcherBuilder, Read, System, World, Write}, + shrev::EventChannel, +}; +use amethyst_error::Error; +pub use laminar::{Config as LaminarConfig, Socket as LaminarSocket}; +use laminar::{Packet, SocketEvent}; + +use bytes::Bytes; +use log::error; +use std::{ + ops::{Deref, DerefMut}, + time::Instant, +}; + +/// Use this network bundle to add the various underlying laminar network systems to your game. +pub struct LaminarNetworkBundle; + +impl<'a, 'b> SystemBundle<'a, 'b> for LaminarNetworkBundle { + fn build( + self, + _world: &mut World, + builder: &mut DispatcherBuilder<'_, '_>, + ) -> Result<(), Error> { + builder.add( + NetworkSimulationTimeSystem, + NETWORK_SIM_TIME_SYSTEM_NAME, + &[], + ); + builder.add(LaminarNetworkSendSystem, NETWORK_SEND_SYSTEM_NAME, &[]); + builder.add( + LaminarNetworkPollSystem, + NETWORK_POLL_SYSTEM_NAME, + &[NETWORK_SEND_SYSTEM_NAME], + ); + builder.add( + LaminarNetworkRecvSystem, + NETWORK_RECV_SYSTEM_NAME, + &[NETWORK_POLL_SYSTEM_NAME], + ); + Ok(()) + } +} + +struct LaminarNetworkSendSystem; + +impl<'s> System<'s> for LaminarNetworkSendSystem { + type SystemData = ( + Write<'s, NetworkSimulationResource>, + Read<'s, NetworkSimulationTime>, + ); + + fn run(&mut self, (mut net, sim_time): Self::SystemData) { + run_network_send_system( + net.deref_mut(), + sim_time.deref(), + |socket, addr, message| { + let packet = match message.delivery { + DeliveryRequirement::Unreliable => { + Packet::unreliable(addr, message.payload.to_vec()) + } + DeliveryRequirement::UnreliableSequenced(stream_id) => { + Packet::unreliable_sequenced(addr, message.payload.to_vec(), stream_id) + } + DeliveryRequirement::Reliable => { + Packet::reliable_unordered(addr, message.payload.to_vec()) + } + DeliveryRequirement::ReliableSequenced(stream_id) => { + Packet::reliable_sequenced(addr, message.payload.to_vec(), stream_id) + } + DeliveryRequirement::ReliableOrdered(stream_id) => { + Packet::reliable_ordered(addr, message.payload.to_vec(), stream_id) + } + }; + + if let Err(e) = socket.send(packet) { + error!("There was an error when attempting to send packet: {:?}", e); + } + }, + ); + } +} + +struct LaminarNetworkPollSystem; + +impl<'s> System<'s> for LaminarNetworkPollSystem { + type SystemData = Write<'s, NetworkSimulationResource>; + + fn run(&mut self, mut net: Self::SystemData) { + net.get_socket_mut() + .map(|socket| socket.manual_poll(Instant::now())); + } +} + +struct LaminarNetworkRecvSystem; + +impl<'s> System<'s> for LaminarNetworkRecvSystem { + type SystemData = ( + Write<'s, NetworkSimulationResource>, + Write<'s, EventChannel>, + ); + + fn run(&mut self, (mut net, mut event_channel): Self::SystemData) { + run_network_recv_system(net.deref_mut(), |socket| { + while let Some(event) = socket.recv() { + let event = match event { + SocketEvent::Packet(packet) => NetworkSimulationEvent::Message( + packet.addr(), + Bytes::from(packet.payload()), + ), + SocketEvent::Connect(addr) => NetworkSimulationEvent::Connect(addr), + SocketEvent::Timeout(addr) => NetworkSimulationEvent::Disconnect(addr), + }; + event_channel.single_write(event); + } + }); + } +} + +impl Socket for LaminarSocket { + fn default_requirement() -> DeliveryRequirement { + DeliveryRequirement::ReliableOrdered(None) + } +} diff --git a/amethyst_network/src/simulation/transport/socket.rs b/amethyst_network/src/simulation/transport/socket.rs new file mode 100644 index 0000000000..caaad39a19 --- /dev/null +++ b/amethyst_network/src/simulation/transport/socket.rs @@ -0,0 +1,6 @@ +use crate::simulation::requirements::DeliveryRequirement; + +/// Trait defining the interface for the generic type that `NetworkSimulationResource` requires. +pub trait Socket { + fn default_requirement() -> DeliveryRequirement; +} diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs new file mode 100644 index 0000000000..51bbbb873d --- /dev/null +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -0,0 +1,133 @@ +//! Network systems implementation backed by the UDP network protocol. +use crate::simulation::{ + events::NetworkSimulationEvent, + requirements::DeliveryRequirement, + resource::NetworkSimulationResource, + timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, + transport::{ + run_network_recv_system, run_network_send_system, socket::Socket, NETWORK_RECV_SYSTEM_NAME, + NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, + }, +}; +use amethyst_core::{ + bundle::SystemBundle, + ecs::{DispatcherBuilder, Read, System, World, Write}, + shrev::EventChannel, +}; +use amethyst_error::Error; +use bytes::Bytes; +use log::error; +use std::{ + io, + net::UdpSocket, + ops::{Deref, DerefMut}, +}; + +/// Use this network bundle to add the various underlying UDP network systems to your game. +pub struct UdpNetworkBundle { + recv_buffer_size_bytes: usize, +} + +impl UdpNetworkBundle { + pub fn new(recv_buffer_size_bytes: usize) -> Self { + Self { + recv_buffer_size_bytes, + } + } +} + +impl<'a, 'b> SystemBundle<'a, 'b> for UdpNetworkBundle { + fn build( + self, + _world: &mut World, + builder: &mut DispatcherBuilder<'_, '_>, + ) -> Result<(), Error> { + builder.add( + NetworkSimulationTimeSystem, + NETWORK_SIM_TIME_SYSTEM_NAME, + &[], + ); + builder.add(UdpNetworkSendSystem, NETWORK_SEND_SYSTEM_NAME, &[]); + builder.add( + UdpNetworkRecvSystem::with_buffer_capacity(self.recv_buffer_size_bytes), + NETWORK_RECV_SYSTEM_NAME, + &[], + ); + Ok(()) + } +} + +pub struct UdpNetworkSendSystem; + +impl<'s> System<'s> for UdpNetworkSendSystem { + type SystemData = ( + Write<'s, NetworkSimulationResource>, + Read<'s, NetworkSimulationTime>, + ); + + fn run(&mut self, (mut net, sim_time): Self::SystemData) { + run_network_send_system( + net.deref_mut(), + sim_time.deref(), + |socket, addr, message| match message.delivery { + DeliveryRequirement::Unreliable => { + if let Err(e) = socket.send_to(&message.payload, addr) { + error!("There was an error when attempting to send packet: {:?}", e); + } + } + delivery => panic!( + "{:?} is unsupported. UDP only supports Unreliable by design.", + delivery + ), + }, + ); + } +} + +pub struct UdpNetworkRecvSystem { + recv_buffer: Vec, +} + +impl UdpNetworkRecvSystem { + pub fn with_buffer_capacity(size: usize) -> Self { + Self { + recv_buffer: vec![0; size], + } + } +} + +impl<'s> System<'s> for UdpNetworkRecvSystem { + type SystemData = ( + Write<'s, NetworkSimulationResource>, + Write<'s, EventChannel>, + ); + + fn run(&mut self, (mut net, mut recv_channel): Self::SystemData) { + run_network_recv_system(net.deref_mut(), |socket| { + loop { + match socket.recv_from(&mut self.recv_buffer) { + Ok((recv_len, address)) => { + let event = NetworkSimulationEvent::Message( + address, + Bytes::from(&self.recv_buffer[..recv_len]), + ); + // TODO: Handle other types of events. + recv_channel.single_write(event); + } + Err(e) => { + if e.kind() != io::ErrorKind::WouldBlock { + error!("Encountered an error receiving data: {:?}", e); + } + break; + } + } + } + }); + } +} + +impl Socket for UdpSocket { + fn default_requirement() -> DeliveryRequirement { + DeliveryRequirement::Unreliable + } +} diff --git a/amethyst_network/src/test.rs b/amethyst_network/src/test.rs deleted file mode 100644 index 942951a09e..0000000000 --- a/amethyst_network/src/test.rs +++ /dev/null @@ -1,133 +0,0 @@ -#![cfg(test)] - -use std::{net::SocketAddr, thread::sleep, time::Duration}; - -use amethyst_core::{ - ecs::{Builder, Join, World, WorldExt, WriteStorage}, - shred::{Dispatcher, DispatcherBuilder, SystemData}, -}; - -use crate::{ - net_event::{NetEvent, NetPacket}, - server::ServerConfig, - NetConnection, NetSocketSystem, -}; -use laminar::Config; - -#[test] -fn single_packet_early() { - let server_addr: SocketAddr = "127.0.0.1:21200".parse().unwrap(); - let client_addr: SocketAddr = "127.0.0.1:21202".parse().unwrap(); - - let (mut world_cl, mut cl_dispatch, mut world_sv, mut sv_dispatch) = - build(client_addr, server_addr); - - let mut conn_to_server = NetConnection::::new(server_addr); - let mut conn_to_client = NetConnection::::new(client_addr); - - let packet = NetEvent::Packet(NetPacket::reliable_unordered( - "Test Message From Client1".to_string(), - )); - - conn_to_server.queue(packet.clone()); - world_cl.create_entity().with(conn_to_server).build(); - - let mut rcv = conn_to_client.receive_buffer.register_reader(); - let conn_to_client_entity = world_sv.create_entity().with(conn_to_client).build(); - - cl_dispatch.dispatch(&world_cl); - sleep(Duration::from_millis(500)); - sv_dispatch.dispatch(&world_sv); - - let storage = world_sv.read_storage::>(); - let comp = storage.get(conn_to_client_entity).unwrap(); - - assert_eq!(comp.receive_buffer.read(&mut rcv).next(), Some(&packet)); - // We should have consumed the only event in the iterator by calling next(). - assert!(comp.receive_buffer.read(&mut rcv).count() == 0); -} - -#[test] -#[ignore] -fn send_receive_100_packets() { - let server_addr: SocketAddr = "127.0.0.1:21204".parse().unwrap(); - let client_addr: SocketAddr = "127.0.0.1:21206".parse().unwrap(); - - // setup world for client and server - let (mut world_cl, mut cl_dispatch, mut world_sv, mut sv_dispatch) = - build(client_addr, server_addr); - - // setup connections from client -> server and server -> client - let conn_to_server = NetConnection::::new(server_addr); - let mut conn_to_client = NetConnection::::new(client_addr); - - let packet = NetEvent::Packet(NetPacket::reliable_unordered( - "Test Message From Client1".to_string(), - )); - - world_cl.create_entity().with(conn_to_server).build(); - - let mut rcv = conn_to_client.receive_buffer.register_reader(); - let conn_to_client_entity = world_sv.create_entity().with(conn_to_client).build(); - - sleep(Duration::from_millis(50)); - { - let mut sto = WriteStorage::>::fetch(&world_cl); - - for cmp in (&mut sto).join() { - for _i in 0..100 { - cmp.queue(packet.clone()); - } - } - } - cl_dispatch.dispatch(&world_cl); - sleep(Duration::from_millis(100)); - sv_dispatch.dispatch(&world_sv); - - let storage = world_sv.read_storage::>(); - let comp = storage.get(conn_to_client_entity).unwrap(); - assert_eq!(comp.receive_buffer.read(&mut rcv).count(), 100); -} - -fn build<'a, 'b>( - client_addr: SocketAddr, - server_addr: SocketAddr, -) -> (World, Dispatcher<'a, 'b>, World, Dispatcher<'a, 'b>) { - let mut world_cl = World::new(); - let mut world_sv = World::new(); - - // client config - let client_config = ServerConfig { - udp_socket_addr: client_addr, - max_throughput: 10000, - create_net_connection_on_connect: false, - laminar_config: Config::default(), - }; - - // server config - let server_config = ServerConfig { - udp_socket_addr: server_addr, - max_throughput: 10000, - create_net_connection_on_connect: false, - laminar_config: Config::default(), - }; - - let mut cl_dispatch = DispatcherBuilder::new() - .with( - NetSocketSystem::::new(client_config).unwrap(), - "s", - &[], - ) - .build(); - cl_dispatch.setup(&mut world_cl); - let mut sv_dispatch = DispatcherBuilder::new() - .with( - NetSocketSystem::::new(server_config).unwrap(), - "s", - &[], - ) - .build(); - sv_dispatch.setup(&mut world_sv); - - (world_cl, cl_dispatch, world_sv, sv_dispatch) -} diff --git a/examples/headless_server/main.rs b/examples/headless_server/main.rs deleted file mode 100644 index 6993671c36..0000000000 --- a/examples/headless_server/main.rs +++ /dev/null @@ -1,75 +0,0 @@ -use amethyst::{ - core::{frame_limiter::FrameRateLimitStrategy, SystemDesc}, - derive::SystemDesc, - ecs::{Join, System, SystemData, World, WriteStorage}, - network::*, - prelude::*, - shrev::ReaderId, - utils::application_root_dir, - Result, -}; - -use log::info; - -use std::time::Duration; - -fn main() -> Result<()> { - amethyst::start_logger(Default::default()); - - let assets_dir = application_root_dir()?.join("./"); - - let game_data = GameDataBuilder::default() - .with_bundle(NetworkBundle::<()>::new("127.0.0.1:23455".parse().unwrap()))? - .with(SpamReceiveSystem::new(), "rcv", &[]); - let mut game = Application::build(assets_dir, State1)? - .with_frame_limit( - FrameRateLimitStrategy::SleepAndYield(Duration::from_millis(2)), - 1, - ) - .build(game_data)?; - game.run(); - Ok(()) -} - -/// Default empty state -pub struct State1; -impl SimpleState for State1 { - fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) { - data.world - .create_entity() - .with(NetConnection::<()>::new("127.0.0.1:3457".parse().unwrap())) - .build(); - } -} - -/// A simple system that receives a ton of network events. -#[derive(SystemDesc)] -struct SpamReceiveSystem { - pub reader: Option>>, -} - -impl SpamReceiveSystem { - pub fn new() -> Self { - SpamReceiveSystem { reader: None } - } -} - -impl<'a> System<'a> for SpamReceiveSystem { - type SystemData = (WriteStorage<'a, NetConnection<()>>,); - fn run(&mut self, (mut connections,): Self::SystemData) { - let mut count = 0; - for (conn,) in (&mut connections,).join() { - if self.reader.is_none() { - self.reader = Some(conn.register_reader()); - } - - for ev in conn.received_events(self.reader.as_mut().unwrap()) { - count += 1; - match ev { - _ => {} - } - } - } - info!("Received {} messages this frame", count); - } -} diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index d617cab254..9a9cd784b2 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -1,8 +1,10 @@ use amethyst::{ - core::{frame_limiter::FrameRateLimitStrategy, SystemDesc, Time}, - derive::SystemDesc, - ecs::{Join, Read, System, SystemData, World, WriteStorage}, - network::*, + core::{frame_limiter::FrameRateLimitStrategy, Time}, + ecs::{Read, System, Write}, + network::simulation::{ + laminar::{LaminarNetworkBundle, LaminarSocket}, + NetworkSimulationResource, NetworkSimulationTime, + }, prelude::*, utils::application_root_dir, Result, @@ -10,41 +12,40 @@ use amethyst::{ use log::info; use std::time::Duration; +// You'll likely want to use a type alias for any place you specify the NetworkResource so +// that, if changed, it will only need to be changed in one place. +type NetworkResourceImpl = NetworkSimulationResource; + fn main() -> Result<()> { amethyst::start_logger(Default::default()); + let socket = LaminarSocket::bind("0.0.0.0:3455").expect("Should bind"); + + let server_addr = "127.0.0.1:3457".parse()?; let assets_dir = application_root_dir()?.join("./"); + let mut net = NetworkSimulationResource::new_client(server_addr); + net.set_socket(socket); + let game_data = GameDataBuilder::default() - .with_bundle(NetworkBundle::::new( - "127.0.0.1:3457".parse().unwrap(), - ))? + .with_bundle(LaminarNetworkBundle)? .with(SpamSystem::new(), "spam", &[]); - let mut game = Application::build(assets_dir, State1)? + let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( FrameRateLimitStrategy::SleepAndYield(Duration::from_millis(2)), 144, ) + .with_resource(net) .build(game_data)?; game.run(); Ok(()) } /// Default empty state -pub struct State1; -impl SimpleState for State1 { - fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) { - data.world - .create_entity() - .with(NetConnection::::new( - "127.0.0.1:3455".parse().unwrap(), - )) - .build(); - } -} +pub struct GameState; +impl SimpleState for GameState {} /// A simple system that sends a ton of messages to all connections. /// In this case, only the server is connected. -#[derive(SystemDesc)] struct SpamSystem; impl SpamSystem { @@ -54,20 +55,24 @@ impl SpamSystem { } impl<'a> System<'a> for SpamSystem { - type SystemData = (WriteStorage<'a, NetConnection>, Read<'a, Time>); - fn run(&mut self, (mut connections, time): Self::SystemData) { - for conn in (&mut connections).join() { - info!("Sending 10k messages."); - for i in 0..500 { - let packet = NetEvent::Packet(NetPacket::unreliable(format!( - "CL: frame:{},abs_time:{},c:{}", - time.frame_number(), - time.absolute_time_seconds(), - i - ))); - - conn.queue(packet); - } + type SystemData = ( + Read<'a, NetworkSimulationTime>, + Read<'a, Time>, + Write<'a, NetworkResourceImpl>, + ); + fn run(&mut self, (sim_time, time, mut net): Self::SystemData) { + // Use method `sim_time.sim_frames_to_run()` to determine if the system should send a + // message this frame. If, for example, the ECS frame rate is slower than the simulation + // frame rate, this code block will run until it catches up with the expected simulation + // frame number. + for frame in sim_time.sim_frames_to_run() { + info!("Sending message for sim frame {}.", frame); + let payload = format!( + "CL: sim_frame:{},abs_time:{}", + frame, + time.absolute_time_seconds() + ); + net.send(payload.as_bytes()); } } } diff --git a/examples/net_server/main.rs b/examples/net_server/main.rs index 64f87007b8..4c651ba6b9 100644 --- a/examples/net_server/main.rs +++ b/examples/net_server/main.rs @@ -1,103 +1,128 @@ +use std::time::Duration; + use amethyst::{ - core::{frame_limiter::FrameRateLimitStrategy, SystemDesc}, - derive::SystemDesc, - ecs::{Component, Entities, Join, System, SystemData, VecStorage, World, WriteStorage}, - network::*, + core::{bundle::SystemBundle, frame_limiter::FrameRateLimitStrategy, SystemDesc}, + ecs::{DispatcherBuilder, Read, System, SystemData, World, Write}, + network::simulation::{ + laminar::{LaminarNetworkBundle, LaminarSocket}, + DeliveryRequirement, NetworkSimulationEvent, NetworkSimulationResource, UrgencyRequirement, + }, prelude::*, - shrev::ReaderId, + shrev::{EventChannel, ReaderId}, utils::application_root_dir, Result, }; - use log::info; -use std::time::Duration; +// You'll likely want to use a type alias for any place you specify the `NetworkSimulationResource` so +// that, if changed, it will only need to be changed in one place. +type NetworkSimResourceImpl = NetworkSimulationResource; fn main() -> Result<()> { amethyst::start_logger(Default::default()); + let socket = LaminarSocket::bind("0.0.0.0:3457").expect("Should bind"); + + // At some point, when we have a matchmaking solution, we probably want to rethink the trusted + // clients functionality. + let clients = ["127.0.0.1:3455".parse()?]; let assets_dir = application_root_dir()?.join("./"); + let mut net = NetworkSimulationResource::new_server().with_trusted_clients(&clients); + net.set_socket(socket); + + // XXX: This is gross. We really need a handshake in laminar. Reliable delivery will not work + // unless you send an unreliable message first and begin the client BEFORE the 5 second disconnect + // timer. + net.send_with_requirements( + b"", + DeliveryRequirement::Unreliable, + UrgencyRequirement::OnTick, + ); + let game_data = GameDataBuilder::default() - .with_bundle(NetworkBundle::::new( - "127.0.0.1:3455".parse().unwrap(), - ))? - .with(SpamReceiveSystem::new(), "rcv", &[]); - let mut game = Application::build(assets_dir, State1)? + .with_bundle(LaminarNetworkBundle)? + .with_bundle(SpamReceiveBundle)?; + let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( FrameRateLimitStrategy::SleepAndYield(Duration::from_millis(2)), - 1, + 60, ) + .with_resource(net) .build(game_data)?; game.run(); Ok(()) } /// Default empty state -pub struct State1; -impl SimpleState for State1 { - fn on_start(&mut self, _data: StateData<'_, GameData<'_, '_>>) {} -} +pub struct GameState; + +impl SimpleState for GameState {} + +#[derive(Debug)] +struct SpamReceiveBundle; -/// Component to store client's event subscription -struct SpamReader(ReaderId>); +impl<'a, 'b> SystemBundle<'a, 'b> for SpamReceiveBundle { + fn build(self, world: &mut World, builder: &mut DispatcherBuilder<'a, 'b>) -> Result<()> { + builder.add( + SpamReceiveSystemDesc::default().build(world), + "receiving_system", + &[], + ); + Ok(()) + } +} -impl Component for SpamReader { - type Storage = VecStorage; +#[derive(Default, Debug)] +pub struct SpamReceiveSystemDesc; + +impl<'a, 'b> SystemDesc<'a, 'b, SpamReceiveSystem> for SpamReceiveSystemDesc { + fn build(self, world: &mut World) -> SpamReceiveSystem { + // Creates the EventChannel managed by the ECS. + >::SystemData::setup(world); + // Fetch the change we just created and call `register_reader` to get a + // ReaderId. This reader id is used to fetch new events from the network event + // channel. + let reader = world + .fetch_mut::>() + .register_reader(); + SpamReceiveSystem::new(reader) + } } /// A simple system that receives a ton of network events. -#[derive(SystemDesc)] -struct SpamReceiveSystem {} +struct SpamReceiveSystem { + reader: ReaderId, +} impl SpamReceiveSystem { - pub fn new() -> Self { - SpamReceiveSystem {} + pub fn new(reader: ReaderId) -> Self { + Self { reader } } } impl<'a> System<'a> for SpamReceiveSystem { type SystemData = ( - WriteStorage<'a, NetConnection>, - WriteStorage<'a, SpamReader>, - Entities<'a>, + Write<'a, NetworkSimResourceImpl>, + Read<'a, EventChannel>, ); - fn run(&mut self, (mut connections, mut readers, entities): Self::SystemData) { - let mut count = 0; - let mut connection_count = 0; - - for (e, connection) in (&entities, &mut connections).join() { - let reader = readers - .entry(e) - .expect("Cannot get reader") - .or_insert_with(|| SpamReader(connection.register_reader())); - - let mut client_disconnected = false; - - for ev in connection.received_events(&mut reader.0) { - count += 1; - match ev { - NetEvent::Packet(packet) => info!("{}", packet.content()), - NetEvent::Connected(addr) => info!("New Client Connection: {}", addr), - NetEvent::Disconnected(_addr) => { - client_disconnected = true; - } - _ => {} - } - } - if client_disconnected { - println!("Client Disconnects"); - entities - .delete(e) - .expect("Cannot delete connection from world!"); + fn run(&mut self, (mut net, channel): Self::SystemData) { + for event in channel.read(&mut self.reader) { + match event { + NetworkSimulationEvent::Message(addr, payload) => { + info!("{}: {:?}", addr, payload); + // In a typical client/server simulation, both the client and the server will + // be exchanging messages at a constant rate. Laminar makes use of this by + // packaging message acks with the next sent message. Therefore, in order for + // reliability to work properly, we'll send a generic "ok" response. + net.send(b"ok"); + } + NetworkSimulationEvent::Connect(addr) => info!("New client connection: {}", addr), + NetworkSimulationEvent::Disconnect(addr) => { + info!("Client Disconnected: {}", addr); + } } - - connection_count += 1; } - println!( - "Received {} messages this frame connections: {}", - count, connection_count - ); } } From 2790651fd8bfde163aedaac73547660c62a2a92a Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Mon, 23 Sep 2019 02:20:45 -0400 Subject: [PATCH 04/22] Simplify the responsibilities of `NetworkSimulationResource` by making it transport layer only. --- Cargo.toml | 2 +- amethyst_network/src/simulation.rs | 6 +- amethyst_network/src/simulation/client.rs | 19 ---- amethyst_network/src/simulation/message.rs | 5 + .../src/simulation/requirements.rs | 1 + amethyst_network/src/simulation/transport.rs | 80 +++++----------- .../src/simulation/transport/laminar.rs | 69 +++++++------- .../simulation/{ => transport}/resource.rs | 93 +++++-------------- .../src/simulation/transport/socket.rs | 2 +- .../src/simulation/transport/udp.rs | 15 +-- examples/net_client/main.rs | 13 ++- examples/net_server/main.rs | 17 ++-- 12 files changed, 117 insertions(+), 205 deletions(-) delete mode 100644 amethyst_network/src/simulation/client.rs rename amethyst_network/src/simulation/{ => transport}/resource.rs (61%) diff --git a/Cargo.toml b/Cargo.toml index 8c7d9e6fb0..b56d750b44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ license = "MIT/Apache-2.0" travis-ci = { repository = "amethyst/amethyst", branch = "master" } [features] -default = ["animation", "audio", "locale", "network", "renderer", "sentry"] +default = ["animation", "audio", "locale", "network", "renderer"] vulkan = ["amethyst_rendy/vulkan"] metal = ["amethyst_rendy/metal"] diff --git a/amethyst_network/src/simulation.rs b/amethyst_network/src/simulation.rs index 868bab53eb..ae32618cc9 100644 --- a/amethyst_network/src/simulation.rs +++ b/amethyst_network/src/simulation.rs @@ -2,18 +2,14 @@ //! more utilities to make their way into this module. e.g. "Component synchronization", //! "Matchmaking", etc. -mod client; mod events; mod message; mod requirements; -mod resource; mod timing; mod transport; pub use events::NetworkSimulationEvent; pub use message::Message; pub use requirements::{DeliveryRequirement, UrgencyRequirement}; -pub use resource::NetworkSimulationResource; pub use timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}; -pub use transport::laminar; -pub use transport::udp; +pub use transport::{laminar, udp, SimulationTransportResource}; diff --git a/amethyst_network/src/simulation/client.rs b/amethyst_network/src/simulation/client.rs deleted file mode 100644 index 025b1022ab..0000000000 --- a/amethyst_network/src/simulation/client.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::net::SocketAddr; - -/// Stores information about a known client for the `NetworkSimulationResource`. -#[derive(Clone, Debug)] -pub struct Client { - addr: SocketAddr, -} - -impl Client { - /// Create and return a new Client - pub fn new(addr: SocketAddr) -> Self { - Self { addr } - } - - /// Return the address of the Client - pub fn addr(&self) -> SocketAddr { - self.addr - } -} diff --git a/amethyst_network/src/simulation/message.rs b/amethyst_network/src/simulation/message.rs index 5848409d78..03529d1c69 100644 --- a/amethyst_network/src/simulation/message.rs +++ b/amethyst_network/src/simulation/message.rs @@ -1,10 +1,13 @@ use super::requirements::{DeliveryRequirement, UrgencyRequirement}; use bytes::Bytes; +use std::net::SocketAddr; /// Structure used to hold message payloads before they are consumed and sent by an underlying /// NetworkSystem. #[derive(Debug, PartialEq, Eq)] pub struct Message { + /// The destination to send the message. + pub(crate) destination: SocketAddr, /// The serialized payload itself. pub(crate) payload: Bytes, /// The requirement around whether or not this message should be resent if lost. @@ -16,11 +19,13 @@ pub struct Message { impl Message { /// Creates and returns a new Message. pub(crate) fn new( + destination: SocketAddr, payload: &[u8], delivery: DeliveryRequirement, urgency: UrgencyRequirement, ) -> Self { Self { + destination, payload: Bytes::from(payload), delivery, urgency, diff --git a/amethyst_network/src/simulation/requirements.rs b/amethyst_network/src/simulation/requirements.rs index 9045ec483b..f8660bd298 100644 --- a/amethyst_network/src/simulation/requirements.rs +++ b/amethyst_network/src/simulation/requirements.rs @@ -1,6 +1,7 @@ /// Specification of the desired delivery guarantee on a message. All examples will use the /// following: 1, 2, 3, 4, 5, 6 sent from the server. 5, 1, 4, 2, 3 received by the client. Packet /// 6 was lost on initial send. + #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] pub enum DeliveryRequirement { /// Messages may not be delivered. diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index f6d9804d73..e479805fdb 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -2,27 +2,29 @@ //! protocols. One important thing to note if you're implementing your own, the underlying sockets //! MUST be non-blocking in order to play nicely with the ECS scheduler. +mod resource; + pub mod laminar; pub mod socket; pub mod udp; +pub use crate::simulation::transport::resource::SimulationTransportResource; + const NETWORK_SIM_TIME_SYSTEM_NAME: &str = "simulation_time"; const NETWORK_SEND_SYSTEM_NAME: &str = "network_send"; const NETWORK_RECV_SYSTEM_NAME: &str = "network_recv"; const NETWORK_POLL_SYSTEM_NAME: &str = "network_poll"; use crate::simulation::{ - transport::socket::Socket, Message, NetworkSimulationResource, NetworkSimulationTime, - UrgencyRequirement, + transport::socket::Socket, Message, NetworkSimulationTime, UrgencyRequirement, }; use log::warn; -use std::net::SocketAddr; /// Shared set up code for implementations of `NetworkSendSystem`s pub fn run_network_send_system( - net: &mut NetworkSimulationResource, + net: &mut SimulationTransportResource, sim_time: &NetworkSimulationTime, - mut handle_send: impl FnMut(&mut T, SocketAddr, &Message) -> (), + mut handle_send: impl FnMut(&mut T, &Message) -> (), ) { // If no socket configured, this system should be a no-op. if !net.has_socket() { @@ -37,35 +39,15 @@ pub fn run_network_send_system( || sim_time.should_send_message_this_frame() }); - // If we have no messages to send, we're done here. - if messages.is_empty() { - return; + let socket = net.get_socket_mut().expect("A socket should be configured"); + for message in messages.iter() { + handle_send(socket, message); } - - // If we're a server, we need to broadcast messages to all connected clients. If we're - // just a client, we're just going to send to our designated server. - if net.is_server() { - let clients = net.clients().to_vec(); - let socket = net.get_socket_mut().expect("A socket should be configured"); - clients.iter().map(|client| client.addr()).for_each(|addr| { - for message in messages.iter() { - handle_send(socket, addr, message); - } - }); - } else { - let server_addr = net.server_addr(); - let socket = net.get_socket_mut().expect("A socket should be configured"); - server_addr.map(|addr| { - for message in messages.iter() { - handle_send(socket, addr, message); - } - }); - }; } /// Shared set up code for implementations of `NetworkRecvSystem`s pub fn run_network_recv_system( - net: &mut NetworkSimulationResource, + net: &mut SimulationTransportResource, mut handle_recv: impl FnMut(&mut T) -> (), ) { net.get_socket_mut().map(|socket| handle_recv(socket)); @@ -75,7 +57,7 @@ pub fn run_network_recv_system( mod tests { use super::*; use crate::simulation::{ - DeliveryRequirement, NetworkSimulationResource, NetworkSimulationTime, + DeliveryRequirement, NetworkSimulationTime, SimulationTransportResource, }; use std::time::Duration; @@ -84,7 +66,7 @@ mod tests { fn run_network_send_system_no_op() { let (mut net, sim_time) = setup_test(); let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _, _| { + run_network_send_system(&mut net, &sim_time, |_, _| { call_count += 1; }); assert_eq!(call_count, 0); @@ -95,10 +77,10 @@ mod tests { fn run_network_send_system_should_call_with_send() { let (mut net, sim_time) = setup_test(); - net.send(b"test"); + net.send("127.0.0.1:9001".parse().unwrap(), b"test"); let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _, _| { + run_network_send_system(&mut net, &sim_time, |_, _| { call_count += 1; }); assert_eq!(call_count, 1); @@ -109,13 +91,13 @@ mod tests { fn run_network_send_system_should_not_call_on_next_frame() { let (mut net, mut sim_time) = setup_test(); - net.send(b"test"); + net.send("127.0.0.1:9001".parse().unwrap(), b"test"); sim_time.update_elapsed(Duration::from_secs(1)); sim_time.increment_frame_number(); let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _, _| { + run_network_send_system(&mut net, &sim_time, |_, _| { call_count += 1; }); assert_eq!(call_count, 0); @@ -127,38 +109,24 @@ mod tests { fn run_network_send_system_should_call_on_next_frame_with_immediate() { let (mut net, mut sim_time) = setup_test(); - net.send_immediate(b"test"); + net.send_immediate("127.0.0.1:9001".parse().unwrap(), b"test"); sim_time.update_elapsed(Duration::from_secs(1)); sim_time.increment_frame_number(); let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _, _| { + run_network_send_system(&mut net, &sim_time, |_, _| { call_count += 1; }); assert_eq!(call_count, 1); } - #[test] - fn run_network_send_system_should_broadcast_messages_in_server_mode() { - let (mut net, sim_time) = setup_test(); - net = net.with_trusted_clients(&[ - "127.0.0.1:9001".parse().unwrap(), - "127.0.0.1:9002".parse().unwrap(), - "127.0.0.1:9003".parse().unwrap(), - ]); - net.send(b"test"); - let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _, _| { - call_count += 1; - }); - assert_eq!(call_count, 3); - } - - fn setup_test() -> (NetworkSimulationResource, NetworkSimulationTime) { + fn setup_test() -> ( + SimulationTransportResource, + NetworkSimulationTime, + ) { let socket = TestSocket {}; - let mut net = NetworkSimulationResource::new_server() - .with_trusted_clients(&["127.0.0.1:9001".parse().unwrap()]); + let mut net = SimulationTransportResource::new(); net.set_socket(socket); // Set up the sim_time to send every other frame diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs index c504870572..dd64d76217 100644 --- a/amethyst_network/src/simulation/transport/laminar.rs +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -1,12 +1,13 @@ //! Network systems implementation backed by the laminar network protocol. + use crate::simulation::{ events::NetworkSimulationEvent, requirements::DeliveryRequirement, - resource::NetworkSimulationResource, timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, transport::{ - run_network_recv_system, run_network_send_system, socket::Socket, NETWORK_POLL_SYSTEM_NAME, - NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, + run_network_recv_system, run_network_send_system, socket::Socket, + SimulationTransportResource, NETWORK_POLL_SYSTEM_NAME, NETWORK_RECV_SYSTEM_NAME, + NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, }, }; use amethyst_core::{ @@ -58,45 +59,49 @@ struct LaminarNetworkSendSystem; impl<'s> System<'s> for LaminarNetworkSendSystem { type SystemData = ( - Write<'s, NetworkSimulationResource>, + Write<'s, SimulationTransportResource>, Read<'s, NetworkSimulationTime>, ); fn run(&mut self, (mut net, sim_time): Self::SystemData) { - run_network_send_system( - net.deref_mut(), - sim_time.deref(), - |socket, addr, message| { - let packet = match message.delivery { - DeliveryRequirement::Unreliable => { - Packet::unreliable(addr, message.payload.to_vec()) - } - DeliveryRequirement::UnreliableSequenced(stream_id) => { - Packet::unreliable_sequenced(addr, message.payload.to_vec(), stream_id) - } - DeliveryRequirement::Reliable => { - Packet::reliable_unordered(addr, message.payload.to_vec()) - } - DeliveryRequirement::ReliableSequenced(stream_id) => { - Packet::reliable_sequenced(addr, message.payload.to_vec(), stream_id) - } - DeliveryRequirement::ReliableOrdered(stream_id) => { - Packet::reliable_ordered(addr, message.payload.to_vec(), stream_id) - } - }; - - if let Err(e) = socket.send(packet) { - error!("There was an error when attempting to send packet: {:?}", e); + run_network_send_system(net.deref_mut(), sim_time.deref(), |socket, message| { + let packet = match message.delivery { + DeliveryRequirement::Unreliable => { + Packet::unreliable(message.destination, message.payload.to_vec()) } - }, - ); + DeliveryRequirement::UnreliableSequenced(stream_id) => { + Packet::unreliable_sequenced( + message.destination, + message.payload.to_vec(), + stream_id, + ) + } + DeliveryRequirement::Reliable => { + Packet::reliable_unordered(message.destination, message.payload.to_vec()) + } + DeliveryRequirement::ReliableSequenced(stream_id) => Packet::reliable_sequenced( + message.destination, + message.payload.to_vec(), + stream_id, + ), + DeliveryRequirement::ReliableOrdered(stream_id) => Packet::reliable_ordered( + message.destination, + message.payload.to_vec(), + stream_id, + ), + }; + + if let Err(e) = socket.send(packet) { + error!("There was an error when attempting to send packet: {:?}", e); + } + }); } } struct LaminarNetworkPollSystem; impl<'s> System<'s> for LaminarNetworkPollSystem { - type SystemData = Write<'s, NetworkSimulationResource>; + type SystemData = Write<'s, SimulationTransportResource>; fn run(&mut self, mut net: Self::SystemData) { net.get_socket_mut() @@ -108,7 +113,7 @@ struct LaminarNetworkRecvSystem; impl<'s> System<'s> for LaminarNetworkRecvSystem { type SystemData = ( - Write<'s, NetworkSimulationResource>, + Write<'s, SimulationTransportResource>, Write<'s, EventChannel>, ); diff --git a/amethyst_network/src/simulation/resource.rs b/amethyst_network/src/simulation/transport/resource.rs similarity index 61% rename from amethyst_network/src/simulation/resource.rs rename to amethyst_network/src/simulation/transport/resource.rs index 730964fcf8..6783384fe6 100644 --- a/amethyst_network/src/simulation/resource.rs +++ b/amethyst_network/src/simulation/transport/resource.rs @@ -1,5 +1,4 @@ -use super::{ - client::Client, +use crate::simulation::{ message::Message, requirements::{DeliveryRequirement, UrgencyRequirement}, transport::socket::Socket, @@ -7,92 +6,45 @@ use super::{ use std::{collections::VecDeque, net::SocketAddr}; /// Resource serving as the owner of the underlying socket and the queue of messages to be sent. -pub struct NetworkSimulationResource { +pub struct SimulationTransportResource { socket: Option, - is_server: bool, - server_addr: Option, - clients: Vec, messages: VecDeque, } -impl NetworkSimulationResource { - /// Create a new `NetworkSimulationResource` as a client - pub fn new_client(server_addr: SocketAddr) -> Self { +impl SimulationTransportResource { + /// Create a new `SimulationTransportResource` + pub fn new() -> Self { Self { socket: None, - server_addr: Some(server_addr), - clients: Vec::new(), messages: VecDeque::new(), - is_server: false, } } - /// Create a new `NetworkSimulationResource` as a server - pub fn new_server() -> Self { - Self { - socket: None, - server_addr: None, - clients: Vec::new(), - messages: VecDeque::new(), - is_server: true, - } - } - - /// Add a number of trusted clients to the `NetworkSimulationResource` for use as a server - pub fn with_trusted_clients(mut self, clients: &[SocketAddr]) -> Self { - self.clients = clients.iter().map(|addr| Client::new(*addr)).collect(); - self - } - - /// Returns a slice of the tracked clients - pub fn clients(&self) -> &[Client] { - &self.clients - } - - /// Set the server address - pub fn set_server_addr(&mut self, server_addr: Option) { - self.server_addr = server_addr; - } - /// Return a mutable reference to the socket if there is one configured. pub fn get_socket_mut(&mut self) -> Option<&mut S> { self.socket.as_mut() } - /// Set the bound socket to the `NetworkSimulationResource` + /// Set the bound socket to the `SimulationTransportResource` pub fn set_socket(&mut self, socket: S) { self.socket = Some(socket); } - /// Drops the socket from the `NetworkSimulationResource` + /// Drops the socket from the `SimulationTransportResource` pub fn drop_socket(&mut self) { self.socket = None; } - /// Returns whether or not the `NetworkSimulationResource` has a bound socket + /// Returns whether or not the `SimulationTransportResource` has a bound socket pub fn has_socket(&self) -> bool { self.socket.is_some() } - /// Returns the server address if one was set - pub fn server_addr(&self) -> Option { - self.server_addr - } - - /// Returns whether or not this `NetworkSimulationResource` was created as a 'server' - pub fn is_server(&self) -> bool { - self.is_server - } - - /// Returns whether or not this `NetworkSimulationResource` was created as a 'client' - pub fn is_client(&self) -> bool { - !self.is_server - } - /// Create a `Message` with the default guarantees provided by the `Socket` implementation and /// pushes it onto the messages queue to be sent on next sim tick. - pub fn send(&mut self, payload: &[u8]) { + pub fn send(&mut self, destination: SocketAddr, payload: &[u8]) { self.send_with_requirements( + destination, payload, S::default_requirement(), UrgencyRequirement::OnTick, @@ -101,8 +53,9 @@ impl NetworkSimulationResource { /// Create a `Message` with the default guarantees provided by the `Socket` implementation and /// pushes it onto the messages queue to be sent immediately. - pub fn send_immediate(&mut self, payload: &[u8]) { + pub fn send_immediate(&mut self, destination: SocketAddr, payload: &[u8]) { self.send_with_requirements( + destination, payload, S::default_requirement(), UrgencyRequirement::Immediate, @@ -112,11 +65,12 @@ impl NetworkSimulationResource { /// Create and queue a `Message` with the specified guarantee pub fn send_with_requirements( &mut self, + destination: SocketAddr, payload: &[u8], delivery: DeliveryRequirement, timing: UrgencyRequirement, ) { - let message = Message::new(payload, delivery, timing); + let message = Message::new(destination, payload, delivery, timing); self.messages.push_back(message); } @@ -125,7 +79,9 @@ impl NetworkSimulationResource { !self.messages.is_empty() } - /// Drains the messages queue and returns the drained messages + /// Drains the messages queue and returns the drained messages. The filter allows you to drain + /// only messages that adhere to your filter. This might be useful in a scenario like draining + /// messages with a particular urgency requirement. pub fn drain_messages(&mut self, mut filter: impl FnMut(&mut Message) -> bool) -> Vec { let mut drained = Vec::with_capacity(self.messages.len()); let mut i = 0; @@ -142,10 +98,10 @@ impl NetworkSimulationResource { } } -impl Default for NetworkSimulationResource { +impl Default for SimulationTransportResource { fn default() -> Self { panic!( - "The `NetworkSimulationResource` resource MUST be created and added to the `Application` \ + "The `SimulationTransportResource` resource MUST be created and added to the `Application` \ before use." ); } @@ -159,7 +115,8 @@ mod tests { #[test] fn test_send() { let mut net = create_test_resource(); - net.send(test_payload()); + let addr = "127.0.0.1:3000".parse().unwrap(); + net.send(addr, test_payload()); assert_eq!(net.messages.len(), 1); let packet = &net.messages[0]; // Default guarantee specified by the Laminar impl @@ -171,6 +128,7 @@ mod tests { fn test_send_with_requirements() { use DeliveryRequirement::*; let mut net = create_test_resource(); + let addr = "127.0.0.1:3000".parse().unwrap(); let requirements = [ Unreliable, @@ -181,7 +139,7 @@ mod tests { ]; for req in requirements.iter().cloned() { - net.send_with_requirements(test_payload(), req, UrgencyRequirement::OnTick); + net.send_with_requirements(addr, test_payload(), req, UrgencyRequirement::OnTick); } assert_eq!(net.messages.len(), requirements.len()); @@ -203,8 +161,7 @@ mod tests { b"test" } - fn create_test_resource() -> NetworkSimulationResource { - let addr = "127.0.0.1:3000".parse().unwrap(); - >::new_client(addr) + fn create_test_resource() -> SimulationTransportResource { + >::new() } } diff --git a/amethyst_network/src/simulation/transport/socket.rs b/amethyst_network/src/simulation/transport/socket.rs index caaad39a19..f348b6553a 100644 --- a/amethyst_network/src/simulation/transport/socket.rs +++ b/amethyst_network/src/simulation/transport/socket.rs @@ -1,6 +1,6 @@ use crate::simulation::requirements::DeliveryRequirement; -/// Trait defining the interface for the generic type that `NetworkSimulationResource` requires. +/// Trait defining the interface for the generic type that `SimulationTransportResource` requires. pub trait Socket { fn default_requirement() -> DeliveryRequirement; } diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs index 51bbbb873d..72c18179f7 100644 --- a/amethyst_network/src/simulation/transport/udp.rs +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -1,12 +1,13 @@ //! Network systems implementation backed by the UDP network protocol. + use crate::simulation::{ events::NetworkSimulationEvent, requirements::DeliveryRequirement, - resource::NetworkSimulationResource, timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, transport::{ - run_network_recv_system, run_network_send_system, socket::Socket, NETWORK_RECV_SYSTEM_NAME, - NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, + run_network_recv_system, run_network_send_system, socket::Socket, + SimulationTransportResource, NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, + NETWORK_SIM_TIME_SYSTEM_NAME, }, }; use amethyst_core::{ @@ -61,7 +62,7 @@ pub struct UdpNetworkSendSystem; impl<'s> System<'s> for UdpNetworkSendSystem { type SystemData = ( - Write<'s, NetworkSimulationResource>, + Write<'s, SimulationTransportResource>, Read<'s, NetworkSimulationTime>, ); @@ -69,9 +70,9 @@ impl<'s> System<'s> for UdpNetworkSendSystem { run_network_send_system( net.deref_mut(), sim_time.deref(), - |socket, addr, message| match message.delivery { + |socket, message| match message.delivery { DeliveryRequirement::Unreliable => { - if let Err(e) = socket.send_to(&message.payload, addr) { + if let Err(e) = socket.send_to(&message.payload, message.destination) { error!("There was an error when attempting to send packet: {:?}", e); } } @@ -98,7 +99,7 @@ impl UdpNetworkRecvSystem { impl<'s> System<'s> for UdpNetworkRecvSystem { type SystemData = ( - Write<'s, NetworkSimulationResource>, + Write<'s, SimulationTransportResource>, Write<'s, EventChannel>, ); diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index 9a9cd784b2..7da25fbdca 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -3,7 +3,7 @@ use amethyst::{ ecs::{Read, System, Write}, network::simulation::{ laminar::{LaminarNetworkBundle, LaminarSocket}, - NetworkSimulationResource, NetworkSimulationTime, + NetworkSimulationTime, SimulationTransportResource, }, prelude::*, utils::application_root_dir, @@ -14,17 +14,15 @@ use std::time::Duration; // You'll likely want to use a type alias for any place you specify the NetworkResource so // that, if changed, it will only need to be changed in one place. -type NetworkResourceImpl = NetworkSimulationResource; +type SimulationTransportResourceImpl = SimulationTransportResource; fn main() -> Result<()> { amethyst::start_logger(Default::default()); let socket = LaminarSocket::bind("0.0.0.0:3455").expect("Should bind"); - - let server_addr = "127.0.0.1:3457".parse()?; let assets_dir = application_root_dir()?.join("./"); - let mut net = NetworkSimulationResource::new_client(server_addr); + let mut net = SimulationTransportResource::new(); net.set_socket(socket); let game_data = GameDataBuilder::default() @@ -58,13 +56,14 @@ impl<'a> System<'a> for SpamSystem { type SystemData = ( Read<'a, NetworkSimulationTime>, Read<'a, Time>, - Write<'a, NetworkResourceImpl>, + Write<'a, SimulationTransportResourceImpl>, ); fn run(&mut self, (sim_time, time, mut net): Self::SystemData) { // Use method `sim_time.sim_frames_to_run()` to determine if the system should send a // message this frame. If, for example, the ECS frame rate is slower than the simulation // frame rate, this code block will run until it catches up with the expected simulation // frame number. + let server_addr = "127.0.0.1:3457".parse().unwrap(); for frame in sim_time.sim_frames_to_run() { info!("Sending message for sim frame {}.", frame); let payload = format!( @@ -72,7 +71,7 @@ impl<'a> System<'a> for SpamSystem { frame, time.absolute_time_seconds() ); - net.send(payload.as_bytes()); + net.send(server_addr, payload.as_bytes()); } } } diff --git a/examples/net_server/main.rs b/examples/net_server/main.rs index 4c651ba6b9..d0c899818c 100644 --- a/examples/net_server/main.rs +++ b/examples/net_server/main.rs @@ -5,7 +5,8 @@ use amethyst::{ ecs::{DispatcherBuilder, Read, System, SystemData, World, Write}, network::simulation::{ laminar::{LaminarNetworkBundle, LaminarSocket}, - DeliveryRequirement, NetworkSimulationEvent, NetworkSimulationResource, UrgencyRequirement, + DeliveryRequirement, NetworkSimulationEvent, SimulationTransportResource, + UrgencyRequirement, }, prelude::*, shrev::{EventChannel, ReaderId}, @@ -14,27 +15,25 @@ use amethyst::{ }; use log::info; -// You'll likely want to use a type alias for any place you specify the `NetworkSimulationResource` so +// You'll likely want to use a type alias for any place you specify the `SimulationTransportResource` so // that, if changed, it will only need to be changed in one place. -type NetworkSimResourceImpl = NetworkSimulationResource; +type SimulationTransportResourceImpl = SimulationTransportResource; fn main() -> Result<()> { amethyst::start_logger(Default::default()); let socket = LaminarSocket::bind("0.0.0.0:3457").expect("Should bind"); - // At some point, when we have a matchmaking solution, we probably want to rethink the trusted - // clients functionality. - let clients = ["127.0.0.1:3455".parse()?]; let assets_dir = application_root_dir()?.join("./"); - let mut net = NetworkSimulationResource::new_server().with_trusted_clients(&clients); + let mut net = SimulationTransportResource::new(); net.set_socket(socket); // XXX: This is gross. We really need a handshake in laminar. Reliable delivery will not work // unless you send an unreliable message first and begin the client BEFORE the 5 second disconnect // timer. net.send_with_requirements( + "127.0.0.1:3455".parse().unwrap(), b"", DeliveryRequirement::Unreliable, UrgencyRequirement::OnTick, @@ -103,7 +102,7 @@ impl SpamReceiveSystem { impl<'a> System<'a> for SpamReceiveSystem { type SystemData = ( - Write<'a, NetworkSimResourceImpl>, + Write<'a, SimulationTransportResourceImpl>, Read<'a, EventChannel>, ); @@ -116,7 +115,7 @@ impl<'a> System<'a> for SpamReceiveSystem { // be exchanging messages at a constant rate. Laminar makes use of this by // packaging message acks with the next sent message. Therefore, in order for // reliability to work properly, we'll send a generic "ok" response. - net.send(b"ok"); + net.send("127.0.0.1:3455".parse().unwrap(), b"ok"); } NetworkSimulationEvent::Connect(addr) => info!("New client connection: {}", addr), NetworkSimulationEvent::Disconnect(addr) => { From 342c53210f8379e40ddd067acf46f0c3b96a9bb5 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Wed, 25 Sep 2019 00:01:30 -0400 Subject: [PATCH 05/22] Update amethyst_network/src/simulation/transport.rs Co-Authored-By: Timon --- amethyst_network/src/simulation/transport.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index e479805fdb..8c39f49ea1 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -61,7 +61,7 @@ mod tests { }; use std::time::Duration; - // If there are no messages to send, the handle_send function shouldn't be invoked + // If there are no messages to send, the `handle_send` function shouldn't be invoked #[test] fn run_network_send_system_no_op() { let (mut net, sim_time) = setup_test(); From 7c65bb727ade1513992f443ba3d9eb9e33e43164 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Sun, 29 Sep 2019 14:33:08 -0400 Subject: [PATCH 06/22] separate the socket and the message queue into separate resources. This will allow each individual network bundle to define and own their socket resource. --- amethyst_network/src/simulation.rs | 2 +- .../src/simulation/requirements.rs | 3 + amethyst_network/src/simulation/timing.rs | 2 +- amethyst_network/src/simulation/transport.rs | 132 +--------------- .../src/simulation/transport/laminar.rs | 145 ++++++++++++------ .../src/simulation/transport/resource.rs | 71 ++++----- .../src/simulation/transport/socket.rs | 6 - .../src/simulation/transport/tcp.rs | 85 ++++++++++ .../src/simulation/transport/udp.rs | 96 ++++++++---- examples/net_client/main.rs | 17 +- examples/net_server/main.rs | 29 ++-- 11 files changed, 297 insertions(+), 291 deletions(-) delete mode 100644 amethyst_network/src/simulation/transport/socket.rs create mode 100644 amethyst_network/src/simulation/transport/tcp.rs diff --git a/amethyst_network/src/simulation.rs b/amethyst_network/src/simulation.rs index ae32618cc9..1d34117427 100644 --- a/amethyst_network/src/simulation.rs +++ b/amethyst_network/src/simulation.rs @@ -12,4 +12,4 @@ pub use events::NetworkSimulationEvent; pub use message::Message; pub use requirements::{DeliveryRequirement, UrgencyRequirement}; pub use timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}; -pub use transport::{laminar, udp, SimulationTransportResource}; +pub use transport::{laminar, udp, TransportResource}; diff --git a/amethyst_network/src/simulation/requirements.rs b/amethyst_network/src/simulation/requirements.rs index f8660bd298..d7fd61cd32 100644 --- a/amethyst_network/src/simulation/requirements.rs +++ b/amethyst_network/src/simulation/requirements.rs @@ -23,6 +23,9 @@ pub enum DeliveryRequirement { /// where it wants to put the message. /// Client receives 1, 2, 3, 4, 5, 6 ReliableOrdered(Option), + /// Defer to the underlying implementation to decide what "Default" means. + /// e.g. Udp will have a default delivery of "Unreliable" + Default, } /// Specification of urgency of the sending of a message. Typically we'll want to send messages diff --git a/amethyst_network/src/simulation/timing.rs b/amethyst_network/src/simulation/timing.rs index f067d3882e..907d2ec98a 100644 --- a/amethyst_network/src/simulation/timing.rs +++ b/amethyst_network/src/simulation/timing.rs @@ -49,7 +49,7 @@ impl NetworkSimulationTime { /// Determines whether or not to send a message in the current frame based on the /// `message_send_rate` - pub fn should_send_message_this_frame(&self) -> bool { + pub fn should_send_messages(&self) -> bool { self.should_send_message(self.frame_number) } diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index 8c39f49ea1..e029cc5322 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -5,140 +5,12 @@ mod resource; pub mod laminar; -pub mod socket; +pub mod tcp; pub mod udp; -pub use crate::simulation::transport::resource::SimulationTransportResource; +pub use resource::TransportResource; const NETWORK_SIM_TIME_SYSTEM_NAME: &str = "simulation_time"; const NETWORK_SEND_SYSTEM_NAME: &str = "network_send"; const NETWORK_RECV_SYSTEM_NAME: &str = "network_recv"; const NETWORK_POLL_SYSTEM_NAME: &str = "network_poll"; - -use crate::simulation::{ - transport::socket::Socket, Message, NetworkSimulationTime, UrgencyRequirement, -}; -use log::warn; - -/// Shared set up code for implementations of `NetworkSendSystem`s -pub fn run_network_send_system( - net: &mut SimulationTransportResource, - sim_time: &NetworkSimulationTime, - mut handle_send: impl FnMut(&mut T, &Message) -> (), -) { - // If no socket configured, this system should be a no-op. - if !net.has_socket() { - if net.has_messages() { - warn!("Messages waiting to be sent but no socket configured."); - } - return; - } - - let messages = net.drain_messages(|message| { - message.urgency == UrgencyRequirement::Immediate - || sim_time.should_send_message_this_frame() - }); - - let socket = net.get_socket_mut().expect("A socket should be configured"); - for message in messages.iter() { - handle_send(socket, message); - } -} - -/// Shared set up code for implementations of `NetworkRecvSystem`s -pub fn run_network_recv_system( - net: &mut SimulationTransportResource, - mut handle_recv: impl FnMut(&mut T) -> (), -) { - net.get_socket_mut().map(|socket| handle_recv(socket)); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::simulation::{ - DeliveryRequirement, NetworkSimulationTime, SimulationTransportResource, - }; - use std::time::Duration; - - // If there are no messages to send, the `handle_send` function shouldn't be invoked - #[test] - fn run_network_send_system_no_op() { - let (mut net, sim_time) = setup_test(); - let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _| { - call_count += 1; - }); - assert_eq!(call_count, 0); - } - - // Since we're on the first frame, the callback will be invoked immediately - #[test] - fn run_network_send_system_should_call_with_send() { - let (mut net, sim_time) = setup_test(); - - net.send("127.0.0.1:9001".parse().unwrap(), b"test"); - - let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _| { - call_count += 1; - }); - assert_eq!(call_count, 1); - } - - // On the second frame, with message send rate of 2, the callback will not be invoked. - #[test] - fn run_network_send_system_should_not_call_on_next_frame() { - let (mut net, mut sim_time) = setup_test(); - - net.send("127.0.0.1:9001".parse().unwrap(), b"test"); - - sim_time.update_elapsed(Duration::from_secs(1)); - sim_time.increment_frame_number(); - - let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _| { - call_count += 1; - }); - assert_eq!(call_count, 0); - } - - // On the second frame, with message send rate of 2, the callback will be invoked on messages - // with immediate urgency. - #[test] - fn run_network_send_system_should_call_on_next_frame_with_immediate() { - let (mut net, mut sim_time) = setup_test(); - - net.send_immediate("127.0.0.1:9001".parse().unwrap(), b"test"); - - sim_time.update_elapsed(Duration::from_secs(1)); - sim_time.increment_frame_number(); - - let mut call_count = 0; - run_network_send_system(&mut net, &sim_time, |_, _| { - call_count += 1; - }); - assert_eq!(call_count, 1); - } - - fn setup_test() -> ( - SimulationTransportResource, - NetworkSimulationTime, - ) { - let socket = TestSocket {}; - let mut net = SimulationTransportResource::new(); - net.set_socket(socket); - - // Set up the sim_time to send every other frame - let sim_time = NetworkSimulationTime::default().with_message_send_rate(2); - (net, sim_time) - } - - struct TestSocket; - - impl Socket for TestSocket { - fn default_requirement() -> DeliveryRequirement { - DeliveryRequirement::Unreliable - } - } -} diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs index dd64d76217..fb4c0903c6 100644 --- a/amethyst_network/src/simulation/transport/laminar.rs +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -1,12 +1,11 @@ -//! Network systems implementation backed by the laminar network protocol. +//! Network systems implementation backed by the Laminar network protocol. use crate::simulation::{ events::NetworkSimulationEvent, requirements::DeliveryRequirement, timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, transport::{ - run_network_recv_system, run_network_send_system, socket::Socket, - SimulationTransportResource, NETWORK_POLL_SYSTEM_NAME, NETWORK_RECV_SYSTEM_NAME, + TransportResource, NETWORK_POLL_SYSTEM_NAME, NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, }, }; @@ -21,18 +20,23 @@ use laminar::{Packet, SocketEvent}; use bytes::Bytes; use log::error; -use std::{ - ops::{Deref, DerefMut}, - time::Instant, -}; +use std::time::Instant; /// Use this network bundle to add the various underlying laminar network systems to your game. -pub struct LaminarNetworkBundle; +pub struct LaminarNetworkBundle { + socket: Option, +} + +impl LaminarNetworkBundle { + pub fn new(socket: Option) -> Self { + Self { socket } + } +} impl<'a, 'b> SystemBundle<'a, 'b> for LaminarNetworkBundle { fn build( self, - _world: &mut World, + world: &mut World, builder: &mut DispatcherBuilder<'_, '_>, ) -> Result<(), Error> { builder.add( @@ -40,17 +44,22 @@ impl<'a, 'b> SystemBundle<'a, 'b> for LaminarNetworkBundle { NETWORK_SIM_TIME_SYSTEM_NAME, &[], ); - builder.add(LaminarNetworkSendSystem, NETWORK_SEND_SYSTEM_NAME, &[]); + builder.add( + LaminarNetworkSendSystem, + NETWORK_SEND_SYSTEM_NAME, + &[NETWORK_SIM_TIME_SYSTEM_NAME], + ); builder.add( LaminarNetworkPollSystem, NETWORK_POLL_SYSTEM_NAME, - &[NETWORK_SEND_SYSTEM_NAME], + &[NETWORK_SIM_TIME_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME], ); builder.add( LaminarNetworkRecvSystem, NETWORK_RECV_SYSTEM_NAME, - &[NETWORK_POLL_SYSTEM_NAME], + &[NETWORK_SIM_TIME_SYSTEM_NAME, NETWORK_POLL_SYSTEM_NAME], ); + world.insert(LaminarSocketResource::new(self.socket)); Ok(()) } } @@ -59,40 +68,52 @@ struct LaminarNetworkSendSystem; impl<'s> System<'s> for LaminarNetworkSendSystem { type SystemData = ( - Write<'s, SimulationTransportResource>, + Write<'s, TransportResource>, + Write<'s, LaminarSocketResource>, Read<'s, NetworkSimulationTime>, ); - fn run(&mut self, (mut net, sim_time): Self::SystemData) { - run_network_send_system(net.deref_mut(), sim_time.deref(), |socket, message| { - let packet = match message.delivery { - DeliveryRequirement::Unreliable => { - Packet::unreliable(message.destination, message.payload.to_vec()) - } - DeliveryRequirement::UnreliableSequenced(stream_id) => { - Packet::unreliable_sequenced( + fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { + socket.get_mut().map(|socket| { + let messages = transport.messages_to_send(|_| sim_time.should_send_messages()); + + for message in messages.iter() { + let packet = match message.delivery { + DeliveryRequirement::Unreliable => { + Packet::unreliable(message.destination, message.payload.to_vec()) + } + DeliveryRequirement::UnreliableSequenced(stream_id) => { + Packet::unreliable_sequenced( + message.destination, + message.payload.to_vec(), + stream_id, + ) + } + DeliveryRequirement::Reliable => { + Packet::reliable_unordered(message.destination, message.payload.to_vec()) + } + DeliveryRequirement::ReliableSequenced(stream_id) => { + Packet::reliable_sequenced( + message.destination, + message.payload.to_vec(), + stream_id, + ) + } + DeliveryRequirement::ReliableOrdered(stream_id) => Packet::reliable_ordered( message.destination, message.payload.to_vec(), stream_id, - ) - } - DeliveryRequirement::Reliable => { - Packet::reliable_unordered(message.destination, message.payload.to_vec()) + ), + DeliveryRequirement::Default => Packet::reliable_ordered( + message.destination, + message.payload.to_vec(), + None, + ), + }; + + if let Err(e) = socket.send(packet) { + error!("There was an error when attempting to send packet: {:?}", e); } - DeliveryRequirement::ReliableSequenced(stream_id) => Packet::reliable_sequenced( - message.destination, - message.payload.to_vec(), - stream_id, - ), - DeliveryRequirement::ReliableOrdered(stream_id) => Packet::reliable_ordered( - message.destination, - message.payload.to_vec(), - stream_id, - ), - }; - - if let Err(e) = socket.send(packet) { - error!("There was an error when attempting to send packet: {:?}", e); } }); } @@ -101,10 +122,11 @@ impl<'s> System<'s> for LaminarNetworkSendSystem { struct LaminarNetworkPollSystem; impl<'s> System<'s> for LaminarNetworkPollSystem { - type SystemData = Write<'s, SimulationTransportResource>; + type SystemData = Write<'s, LaminarSocketResource>; - fn run(&mut self, mut net: Self::SystemData) { - net.get_socket_mut() + fn run(&mut self, mut socket: Self::SystemData) { + socket + .get_mut() .map(|socket| socket.manual_poll(Instant::now())); } } @@ -113,12 +135,12 @@ struct LaminarNetworkRecvSystem; impl<'s> System<'s> for LaminarNetworkRecvSystem { type SystemData = ( - Write<'s, SimulationTransportResource>, + Write<'s, LaminarSocketResource>, Write<'s, EventChannel>, ); - fn run(&mut self, (mut net, mut event_channel): Self::SystemData) { - run_network_recv_system(net.deref_mut(), |socket| { + fn run(&mut self, (mut socket, mut event_channel): Self::SystemData) { + socket.get_mut().map(|socket| { while let Some(event) = socket.recv() { let event = match event { SocketEvent::Packet(packet) => NetworkSimulationEvent::Message( @@ -134,8 +156,35 @@ impl<'s> System<'s> for LaminarNetworkRecvSystem { } } -impl Socket for LaminarSocket { - fn default_requirement() -> DeliveryRequirement { - DeliveryRequirement::ReliableOrdered(None) +/// Resource to own the Laminar socket. +pub struct LaminarSocketResource { + socket: Option, +} + +impl Default for LaminarSocketResource { + fn default() -> Self { + Self { socket: None } + } +} + +impl LaminarSocketResource { + /// Create a new instance of the `UdpSocketResource` + pub fn new(socket: Option) -> Self { + Self { socket } + } + + /// Return a mutable reference to the socket if there is one configured. + pub fn get_mut(&mut self) -> Option<&mut LaminarSocket> { + self.socket.as_mut() + } + + /// Set the bound socket to the `LaminarSocketResource` + pub fn set_socket(&mut self, socket: LaminarSocket) { + self.socket = Some(socket); + } + + /// Drops the socket from the `LaminarSocketResource` + pub fn drop_socket(&mut self) { + self.socket = None; } } diff --git a/amethyst_network/src/simulation/transport/resource.rs b/amethyst_network/src/simulation/transport/resource.rs index 6783384fe6..58488578c0 100644 --- a/amethyst_network/src/simulation/transport/resource.rs +++ b/amethyst_network/src/simulation/transport/resource.rs @@ -1,52 +1,30 @@ use crate::simulation::{ message::Message, requirements::{DeliveryRequirement, UrgencyRequirement}, - transport::socket::Socket, }; use std::{collections::VecDeque, net::SocketAddr}; -/// Resource serving as the owner of the underlying socket and the queue of messages to be sent. -pub struct SimulationTransportResource { - socket: Option, +/// Resource serving as the owner of the queue of messages to be sent. This resource also serves +/// as the interface for other systems to send messages +pub struct TransportResource { messages: VecDeque, } -impl SimulationTransportResource { - /// Create a new `SimulationTransportResource` +impl TransportResource { + /// Create a new `TransportResource` pub fn new() -> Self { Self { - socket: None, messages: VecDeque::new(), } } - /// Return a mutable reference to the socket if there is one configured. - pub fn get_socket_mut(&mut self) -> Option<&mut S> { - self.socket.as_mut() - } - - /// Set the bound socket to the `SimulationTransportResource` - pub fn set_socket(&mut self, socket: S) { - self.socket = Some(socket); - } - - /// Drops the socket from the `SimulationTransportResource` - pub fn drop_socket(&mut self) { - self.socket = None; - } - - /// Returns whether or not the `SimulationTransportResource` has a bound socket - pub fn has_socket(&self) -> bool { - self.socket.is_some() - } - /// Create a `Message` with the default guarantees provided by the `Socket` implementation and /// pushes it onto the messages queue to be sent on next sim tick. pub fn send(&mut self, destination: SocketAddr, payload: &[u8]) { self.send_with_requirements( destination, payload, - S::default_requirement(), + DeliveryRequirement::Default, UrgencyRequirement::OnTick, ); } @@ -57,7 +35,7 @@ impl SimulationTransportResource { self.send_with_requirements( destination, payload, - S::default_requirement(), + DeliveryRequirement::Default, UrgencyRequirement::Immediate, ); } @@ -79,6 +57,17 @@ impl SimulationTransportResource { !self.messages.is_empty() } + /// Returns the messages to send by returning the immediate messages or anything adhering to + /// the given filter. + pub fn messages_to_send( + &mut self, + mut filter: impl FnMut(&mut Message) -> bool, + ) -> Vec { + self.drain_messages(|message| { + message.urgency == UrgencyRequirement::Immediate || filter(message) + }) + } + /// Drains the messages queue and returns the drained messages. The filter allows you to drain /// only messages that adhere to your filter. This might be useful in a scenario like draining /// messages with a particular urgency requirement. @@ -98,19 +87,18 @@ impl SimulationTransportResource { } } -impl Default for SimulationTransportResource { +impl Default for TransportResource { fn default() -> Self { - panic!( - "The `SimulationTransportResource` resource MUST be created and added to the `Application` \ - before use." - ); + Self { + messages: VecDeque::new(), + } } } +// TODO: Fix these. These are all broken now #[cfg(test)] mod tests { use super::*; - use crate::simulation::transport::laminar::LaminarSocket; #[test] fn test_send() { @@ -119,8 +107,7 @@ mod tests { net.send(addr, test_payload()); assert_eq!(net.messages.len(), 1); let packet = &net.messages[0]; - // Default guarantee specified by the Laminar impl - assert_eq!(packet.delivery, DeliveryRequirement::ReliableOrdered(None)); + assert_eq!(packet.delivery, DeliveryRequirement::Default); assert_eq!(packet.urgency, UrgencyRequirement::OnTick); } @@ -152,16 +139,16 @@ mod tests { #[test] fn test_has_socket_and_with_socket() { let mut net = create_test_resource(); - assert!(!net.has_socket()); - net.set_socket(LaminarSocket::bind_any().unwrap()); - assert!(net.has_socket()); + // assert!(!net.has_socket()); + // net.set_socket(LaminarSocket::bind_any().unwrap()); + // assert!(net.has_socket()); } fn test_payload() -> &'static [u8] { b"test" } - fn create_test_resource() -> SimulationTransportResource { - >::new() + fn create_test_resource() -> TransportResource { + TransportResource::new() } } diff --git a/amethyst_network/src/simulation/transport/socket.rs b/amethyst_network/src/simulation/transport/socket.rs deleted file mode 100644 index f348b6553a..0000000000 --- a/amethyst_network/src/simulation/transport/socket.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::simulation::requirements::DeliveryRequirement; - -/// Trait defining the interface for the generic type that `SimulationTransportResource` requires. -pub trait Socket { - fn default_requirement() -> DeliveryRequirement; -} diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs new file mode 100644 index 0000000000..8f7d1cb53a --- /dev/null +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -0,0 +1,85 @@ +//! Network systems implementation backed by the TCP network protocol. + +use crate::simulation::{ + timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, + transport::{NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME}, +}; +use amethyst_core::{ + bundle::SystemBundle, + ecs::{DispatcherBuilder, Read, System, World, Write}, + shrev::EventChannel, +}; +use amethyst_error::Error; + +const CONNECTION_LISTENER_SYSTEM_NAME: &str = "connection_listener"; + +/// Use this network bundle to add the various underlying tcp network systems to your game. +pub struct TcpNetworkBundle; + +impl<'a, 'b> SystemBundle<'a, 'b> for TcpNetworkBundle { + fn build( + self, + _world: &mut World, + builder: &mut DispatcherBuilder<'_, '_>, + ) -> Result<(), Error> { + builder.add( + NetworkSimulationTimeSystem, + NETWORK_SIM_TIME_SYSTEM_NAME, + &[], + ); + builder.add( + TcpConnectionListenerSystem, + CONNECTION_LISTENER_SYSTEM_NAME, + &[NETWORK_SIM_TIME_SYSTEM_NAME], + ); + builder.add( + TcpNetworkSendSystem, + NETWORK_SEND_SYSTEM_NAME, + &[CONNECTION_LISTENER_SYSTEM_NAME], + ); + builder.add( + TcpNetworkRecvSystem, + NETWORK_RECV_SYSTEM_NAME, + &[CONNECTION_LISTENER_SYSTEM_NAME], + ); + Ok(()) + } +} + +/// System to listen for incoming connections and cache them to the resource. +pub struct TcpConnectionListenerSystem; + +impl<'s> System<'s> for TcpConnectionListenerSystem { + type SystemData = ( + Write<'s, TcpNetworkResource>, + Read<'s, NetworkSimulationTime>, + ); + + fn run(&mut self, data: Self::SystemData) {} +} + +/// System to send messages to a particular open `TcpStream`. +pub struct TcpNetworkSendSystem; + +impl<'s> System<'s> for TcpNetworkSendSystem { + type SystemData = (); + + fn run(&mut self, data: Self::SystemData) {} +} + +/// System to receive messages from all open `TcpStream`s. +pub struct TcpNetworkRecvSystem; + +impl<'s> System<'s> for TcpNetworkRecvSystem { + type SystemData = (); + + fn run(&mut self, data: Self::SystemData) {} +} + +pub struct TcpNetworkResource; + +impl Default for TcpNetworkResource { + fn default() -> Self { + Self {} + } +} diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs index 72c18179f7..1e49c117be 100644 --- a/amethyst_network/src/simulation/transport/udp.rs +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -5,8 +5,7 @@ use crate::simulation::{ requirements::DeliveryRequirement, timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, transport::{ - run_network_recv_system, run_network_send_system, socket::Socket, - SimulationTransportResource, NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, + TransportResource, NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, }, }; @@ -18,20 +17,18 @@ use amethyst_core::{ use amethyst_error::Error; use bytes::Bytes; use log::error; -use std::{ - io, - net::UdpSocket, - ops::{Deref, DerefMut}, -}; +use std::{io, net::UdpSocket}; /// Use this network bundle to add the various underlying UDP network systems to your game. pub struct UdpNetworkBundle { + socket: Option, recv_buffer_size_bytes: usize, } impl UdpNetworkBundle { - pub fn new(recv_buffer_size_bytes: usize) -> Self { + pub fn new(socket: Option, recv_buffer_size_bytes: usize) -> Self { Self { + socket, recv_buffer_size_bytes, } } @@ -40,7 +37,7 @@ impl UdpNetworkBundle { impl<'a, 'b> SystemBundle<'a, 'b> for UdpNetworkBundle { fn build( self, - _world: &mut World, + world: &mut World, builder: &mut DispatcherBuilder<'_, '_>, ) -> Result<(), Error> { builder.add( @@ -48,12 +45,17 @@ impl<'a, 'b> SystemBundle<'a, 'b> for UdpNetworkBundle { NETWORK_SIM_TIME_SYSTEM_NAME, &[], ); - builder.add(UdpNetworkSendSystem, NETWORK_SEND_SYSTEM_NAME, &[]); + builder.add( + UdpNetworkSendSystem, + NETWORK_SEND_SYSTEM_NAME, + &[NETWORK_SIM_TIME_SYSTEM_NAME], + ); builder.add( UdpNetworkRecvSystem::with_buffer_capacity(self.recv_buffer_size_bytes), NETWORK_RECV_SYSTEM_NAME, - &[], + &[NETWORK_SIM_TIME_SYSTEM_NAME], ); + world.insert(UdpSocketResource::new(self.socket)); Ok(()) } } @@ -62,30 +64,33 @@ pub struct UdpNetworkSendSystem; impl<'s> System<'s> for UdpNetworkSendSystem { type SystemData = ( - Write<'s, SimulationTransportResource>, + Write<'s, TransportResource>, + Write<'s, UdpSocketResource>, Read<'s, NetworkSimulationTime>, ); - fn run(&mut self, (mut net, sim_time): Self::SystemData) { - run_network_send_system( - net.deref_mut(), - sim_time.deref(), - |socket, message| match message.delivery { - DeliveryRequirement::Unreliable => { - if let Err(e) = socket.send_to(&message.payload, message.destination) { - error!("There was an error when attempting to send packet: {:?}", e); + fn run(&mut self, (mut net, mut socket, sim_time): Self::SystemData) { + socket.get_mut().map(|socket| { + let messages = net.messages_to_send(|_| sim_time.should_send_messages()); + for message in messages.iter() { + match message.delivery { + DeliveryRequirement::Unreliable | DeliveryRequirement::Default => { + if let Err(e) = socket.send_to(&message.payload, message.destination) { + error!("There was an error when attempting to send packet: {:?}", e); + } } + delivery => panic!( + "{:?} is unsupported. UDP only supports Unreliable by design.", + delivery + ), } - delivery => panic!( - "{:?} is unsupported. UDP only supports Unreliable by design.", - delivery - ), - }, - ); + } + }); } } pub struct UdpNetworkRecvSystem { + // TODO: Probably should move this to the UdpSocketResource recv_buffer: Vec, } @@ -99,12 +104,12 @@ impl UdpNetworkRecvSystem { impl<'s> System<'s> for UdpNetworkRecvSystem { type SystemData = ( - Write<'s, SimulationTransportResource>, + Write<'s, UdpSocketResource>, Write<'s, EventChannel>, ); - fn run(&mut self, (mut net, mut recv_channel): Self::SystemData) { - run_network_recv_system(net.deref_mut(), |socket| { + fn run(&mut self, (mut socket, mut recv_channel): Self::SystemData) { + socket.get_mut().map(|socket| { loop { match socket.recv_from(&mut self.recv_buffer) { Ok((recv_len, address)) => { @@ -127,8 +132,35 @@ impl<'s> System<'s> for UdpNetworkRecvSystem { } } -impl Socket for UdpSocket { - fn default_requirement() -> DeliveryRequirement { - DeliveryRequirement::Unreliable +/// Resource to own the UDP socket. +pub struct UdpSocketResource { + socket: Option, +} + +impl Default for UdpSocketResource { + fn default() -> Self { + Self { socket: None } + } +} + +impl UdpSocketResource { + /// Create a new instance of the `UdpSocketResource` + pub fn new(socket: Option) -> Self { + Self { socket } + } + + /// Return a mutable reference to the socket if there is one configured. + pub fn get_mut(&mut self) -> Option<&mut UdpSocket> { + self.socket.as_mut() + } + + /// Set the bound socket to the `UdpSocketResource` + pub fn set_socket(&mut self, socket: UdpSocket) { + self.socket = Some(socket); + } + + /// Drops the socket from the `UdpSocketResource` + pub fn drop_socket(&mut self) { + self.socket = None; } } diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index 7da25fbdca..d9a3d04e04 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -3,7 +3,7 @@ use amethyst::{ ecs::{Read, System, Write}, network::simulation::{ laminar::{LaminarNetworkBundle, LaminarSocket}, - NetworkSimulationTime, SimulationTransportResource, + NetworkSimulationTime, TransportResource, }, prelude::*, utils::application_root_dir, @@ -12,28 +12,21 @@ use amethyst::{ use log::info; use std::time::Duration; -// You'll likely want to use a type alias for any place you specify the NetworkResource so -// that, if changed, it will only need to be changed in one place. -type SimulationTransportResourceImpl = SimulationTransportResource; - fn main() -> Result<()> { amethyst::start_logger(Default::default()); - let socket = LaminarSocket::bind("0.0.0.0:3455").expect("Should bind"); - let assets_dir = application_root_dir()?.join("./"); + let socket = LaminarSocket::bind("0.0.0.0:3455")?; - let mut net = SimulationTransportResource::new(); - net.set_socket(socket); + let assets_dir = application_root_dir()?.join("./"); let game_data = GameDataBuilder::default() - .with_bundle(LaminarNetworkBundle)? + .with_bundle(LaminarNetworkBundle::new(Some(socket)))? .with(SpamSystem::new(), "spam", &[]); let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( FrameRateLimitStrategy::SleepAndYield(Duration::from_millis(2)), 144, ) - .with_resource(net) .build(game_data)?; game.run(); Ok(()) @@ -56,7 +49,7 @@ impl<'a> System<'a> for SpamSystem { type SystemData = ( Read<'a, NetworkSimulationTime>, Read<'a, Time>, - Write<'a, SimulationTransportResourceImpl>, + Write<'a, TransportResource>, ); fn run(&mut self, (sim_time, time, mut net): Self::SystemData) { // Use method `sim_time.sim_frames_to_run()` to determine if the system should send a diff --git a/examples/net_server/main.rs b/examples/net_server/main.rs index d0c899818c..a9c1af8ef1 100644 --- a/examples/net_server/main.rs +++ b/examples/net_server/main.rs @@ -5,8 +5,7 @@ use amethyst::{ ecs::{DispatcherBuilder, Read, System, SystemData, World, Write}, network::simulation::{ laminar::{LaminarNetworkBundle, LaminarSocket}, - DeliveryRequirement, NetworkSimulationEvent, SimulationTransportResource, - UrgencyRequirement, + DeliveryRequirement, NetworkSimulationEvent, TransportResource, UrgencyRequirement, }, prelude::*, shrev::{EventChannel, ReaderId}, @@ -15,39 +14,31 @@ use amethyst::{ }; use log::info; -// You'll likely want to use a type alias for any place you specify the `SimulationTransportResource` so -// that, if changed, it will only need to be changed in one place. -type SimulationTransportResourceImpl = SimulationTransportResource; - fn main() -> Result<()> { amethyst::start_logger(Default::default()); - let socket = LaminarSocket::bind("0.0.0.0:3457").expect("Should bind"); + let socket = LaminarSocket::bind("0.0.0.0:3457")?; let assets_dir = application_root_dir()?.join("./"); - let mut net = SimulationTransportResource::new(); - net.set_socket(socket); - // XXX: This is gross. We really need a handshake in laminar. Reliable delivery will not work // unless you send an unreliable message first and begin the client BEFORE the 5 second disconnect // timer. - net.send_with_requirements( - "127.0.0.1:3455".parse().unwrap(), - b"", - DeliveryRequirement::Unreliable, - UrgencyRequirement::OnTick, - ); + // net.send_with_requirements( + // "127.0.0.1:3455".parse().unwrap(), + // b"", + // DeliveryRequirement::Unreliable, + // UrgencyRequirement::Immediate, + // ); let game_data = GameDataBuilder::default() - .with_bundle(LaminarNetworkBundle)? + .with_bundle(LaminarNetworkBundle::new(Some(socket)))? .with_bundle(SpamReceiveBundle)?; let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( FrameRateLimitStrategy::SleepAndYield(Duration::from_millis(2)), 60, ) - .with_resource(net) .build(game_data)?; game.run(); Ok(()) @@ -102,7 +93,7 @@ impl SpamReceiveSystem { impl<'a> System<'a> for SpamReceiveSystem { type SystemData = ( - Write<'a, SimulationTransportResourceImpl>, + Write<'a, TransportResource>, Read<'a, EventChannel>, ); From e9aac38dc34986e379c310053eb6764dd275352c Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Mon, 30 Sep 2019 02:25:45 -0400 Subject: [PATCH 07/22] add a mostly functional example of tcp. This means that the current API should be flexible enough for any backend implementation. --- amethyst_network/src/simulation.rs | 2 +- .../src/simulation/transport/tcp.rs | 169 ++++++++++++++++-- .../src/simulation/transport/udp.rs | 8 +- examples/net_client/main.rs | 9 +- examples/net_server/main.rs | 11 +- 5 files changed, 177 insertions(+), 22 deletions(-) diff --git a/amethyst_network/src/simulation.rs b/amethyst_network/src/simulation.rs index 1d34117427..bf9b968a25 100644 --- a/amethyst_network/src/simulation.rs +++ b/amethyst_network/src/simulation.rs @@ -12,4 +12,4 @@ pub use events::NetworkSimulationEvent; pub use message::Message; pub use requirements::{DeliveryRequirement, UrgencyRequirement}; pub use timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}; -pub use transport::{laminar, udp, TransportResource}; +pub use transport::{laminar, tcp, udp, TransportResource}; diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs index 8f7d1cb53a..2d13971873 100644 --- a/amethyst_network/src/simulation/transport/tcp.rs +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -1,8 +1,13 @@ //! Network systems implementation backed by the TCP network protocol. use crate::simulation::{ + events::NetworkSimulationEvent, + requirements::DeliveryRequirement, timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, - transport::{NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME}, + transport::{ + TransportResource, NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, + NETWORK_SIM_TIME_SYSTEM_NAME + }, }; use amethyst_core::{ bundle::SystemBundle, @@ -10,16 +15,33 @@ use amethyst_core::{ shrev::EventChannel, }; use amethyst_error::Error; +use bytes::Bytes; +use log::error; +use std::{ + collections::HashMap, + io::{self, Read as IORead, Write as IOWrite}, + ops::{Deref, DerefMut}, + net::{SocketAddr, TcpListener, TcpStream} +}; const CONNECTION_LISTENER_SYSTEM_NAME: &str = "connection_listener"; /// Use this network bundle to add the various underlying tcp network systems to your game. -pub struct TcpNetworkBundle; +pub struct TcpNetworkBundle { + listener: Option, + recv_buffer_size_bytes: usize, +} + +impl TcpNetworkBundle { + pub fn new(listener: Option, recv_buffer_size_bytes: usize) -> Self { + Self { listener, recv_buffer_size_bytes } + } +} impl<'a, 'b> SystemBundle<'a, 'b> for TcpNetworkBundle { fn build( self, - _world: &mut World, + world: &mut World, builder: &mut DispatcherBuilder<'_, '_>, ) -> Result<(), Error> { builder.add( @@ -42,6 +64,7 @@ impl<'a, 'b> SystemBundle<'a, 'b> for TcpNetworkBundle { NETWORK_RECV_SYSTEM_NAME, &[CONNECTION_LISTENER_SYSTEM_NAME], ); + world.insert(TcpNetworkResource::new(self.listener, self.recv_buffer_size_bytes)); Ok(()) } } @@ -53,33 +76,159 @@ impl<'s> System<'s> for TcpConnectionListenerSystem { type SystemData = ( Write<'s, TcpNetworkResource>, Read<'s, NetworkSimulationTime>, + Write<'s, EventChannel> ); - fn run(&mut self, data: Self::SystemData) {} + fn run(&mut self, (mut net, sim_time, mut event_channel): Self::SystemData) { + let resource = net.deref_mut(); + if let Some(ref listener) = resource.listener { + loop { + match listener.accept() { + Ok((stream, addr)) => { + stream.set_nonblocking(true).expect("Setting non-blocking mode"); + stream.set_nodelay(true).expect("Setting nodelay"); + resource.streams.insert(addr, stream); + event_channel.single_write(NetworkSimulationEvent::Connect(addr)); + }, + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + break; + } + Err(e) => { + error!("Error listening for connections: {}", e); + break; + } + }; + } + } + } } /// System to send messages to a particular open `TcpStream`. pub struct TcpNetworkSendSystem; impl<'s> System<'s> for TcpNetworkSendSystem { - type SystemData = (); + type SystemData = ( + Write<'s, TransportResource>, + Write<'s, TcpNetworkResource>, + Read<'s, NetworkSimulationTime>, + ); - fn run(&mut self, data: Self::SystemData) {} + fn run(&mut self, (mut transport, mut net, sim_time): Self::SystemData) { + let messages = transport.messages_to_send(|_| sim_time.should_send_messages()); + for message in messages.iter() { + match message.delivery { + DeliveryRequirement::ReliableOrdered(_) | DeliveryRequirement::Default => { + let stream = net.get_or_create_stream(message.destination); + if let Err(e) = stream.write(&message.payload) { + error!("There was an error when attempting to send message: {:?}", e); + } + } + delivery => panic!( + "{:?} is unsupported. TCP only supports ReliableOrdered by design.", + delivery + ), + } + } + } } /// System to receive messages from all open `TcpStream`s. pub struct TcpNetworkRecvSystem; impl<'s> System<'s> for TcpNetworkRecvSystem { - type SystemData = (); + type SystemData = ( + Write<'s, TcpNetworkResource>, + Write<'s, EventChannel>, + ); - fn run(&mut self, data: Self::SystemData) {} + fn run(&mut self, (mut net, mut event_channel): Self::SystemData) { + let resource = net.deref_mut(); + for (_, stream) in resource.streams.iter_mut() { + let peer_addr = stream.peer_addr().unwrap(); + loop { + match stream.read(&mut resource.recv_buffer) { + Ok(recv_len) => { + if recv_len > 0 { + let event = NetworkSimulationEvent::Message( + peer_addr, + Bytes::from(&resource.recv_buffer[..recv_len]), + ); + event_channel.single_write(event); + } else { + event_channel.single_write(NetworkSimulationEvent::Disconnect(peer_addr)); +// resource.drop_stream(peer_addr); + } + } + Err(e) => { + match e.kind() { + io::ErrorKind::ConnectionReset => { + event_channel.single_write(NetworkSimulationEvent::Disconnect(peer_addr)); +// resource.drop_stream(peer_addr); + } + io::ErrorKind::WouldBlock => {} + _ => error!("Encountered an error receiving data: {:?}", e) + } + break; + } + } + } + } + } } -pub struct TcpNetworkResource; +pub struct TcpNetworkResource { + listener: Option, + streams: HashMap, + recv_buffer: Vec, +} + +impl TcpNetworkResource { + pub fn new(listener: Option, recv_buffer_size_bytes: usize) -> Self { + Self { + listener, + streams: HashMap::new(), + recv_buffer: vec![0; recv_buffer_size_bytes] + } + } + + /// Return a mutable reference to the listener if there is one configured. + pub fn get_mut(&mut self) -> Option<&mut TcpListener> { + self.listener.as_mut() + } + + /// Set the bound listener to the `TcpNetworkResource` + pub fn set_listener(&mut self, listener: TcpListener) { + self.listener = Some(listener); + } + + /// Drops the listener from the `TcpNetworkResource` + pub fn drop_listener(&mut self) { + self.listener = None; + } + + /// Returns the stream associated with the `SocketAddr` or creates one if one doesn't exist. + pub fn get_or_create_stream(&mut self, addr: SocketAddr) -> &mut TcpStream { + self.streams.entry(addr).or_insert_with(|| { + let s = TcpStream::connect(addr).unwrap(); + s.set_nonblocking(true).expect("Setting non-blocking mode"); + s.set_nodelay(true).expect("Setting nodelay"); + s + }) + } + + /// Drop the stream with the given `SocketAddr`. This will be called when a peer seems to have + /// been disconnected. + pub fn drop_stream(&mut self, addr: SocketAddr) -> Option { + self.streams.remove(&addr) + } +} impl Default for TcpNetworkResource { fn default() -> Self { - Self {} + Self { + listener: None, + streams: HashMap::new(), + recv_buffer: Vec::new() + } } } diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs index 1e49c117be..daea774309 100644 --- a/amethyst_network/src/simulation/transport/udp.rs +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -69,9 +69,9 @@ impl<'s> System<'s> for UdpNetworkSendSystem { Read<'s, NetworkSimulationTime>, ); - fn run(&mut self, (mut net, mut socket, sim_time): Self::SystemData) { + fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { socket.get_mut().map(|socket| { - let messages = net.messages_to_send(|_| sim_time.should_send_messages()); + let messages = transport.messages_to_send(|_| sim_time.should_send_messages()); for message in messages.iter() { match message.delivery { DeliveryRequirement::Unreliable | DeliveryRequirement::Default => { @@ -108,7 +108,7 @@ impl<'s> System<'s> for UdpNetworkRecvSystem { Write<'s, EventChannel>, ); - fn run(&mut self, (mut socket, mut recv_channel): Self::SystemData) { + fn run(&mut self, (mut socket, mut event_channel): Self::SystemData) { socket.get_mut().map(|socket| { loop { match socket.recv_from(&mut self.recv_buffer) { @@ -118,7 +118,7 @@ impl<'s> System<'s> for UdpNetworkRecvSystem { Bytes::from(&self.recv_buffer[..recv_len]), ); // TODO: Handle other types of events. - recv_channel.single_write(event); + event_channel.single_write(event); } Err(e) => { if e.kind() != io::ErrorKind::WouldBlock { diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index d9a3d04e04..e4e0fc3845 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -2,7 +2,8 @@ use amethyst::{ core::{frame_limiter::FrameRateLimitStrategy, Time}, ecs::{Read, System, Write}, network::simulation::{ - laminar::{LaminarNetworkBundle, LaminarSocket}, +// laminar::{LaminarNetworkBundle, LaminarSocket}, + tcp::TcpNetworkBundle, NetworkSimulationTime, TransportResource, }, prelude::*, @@ -11,16 +12,18 @@ use amethyst::{ }; use log::info; use std::time::Duration; +use std::net::TcpListener; fn main() -> Result<()> { amethyst::start_logger(Default::default()); - let socket = LaminarSocket::bind("0.0.0.0:3455")?; +// let listener = TcpListener::bind("0.0.0.0:3455")?; +// listener.set_nonblocking(true)?; let assets_dir = application_root_dir()?.join("./"); let game_data = GameDataBuilder::default() - .with_bundle(LaminarNetworkBundle::new(Some(socket)))? + .with_bundle(TcpNetworkBundle::new(None, 1500))? .with(SpamSystem::new(), "spam", &[]); let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( diff --git a/examples/net_server/main.rs b/examples/net_server/main.rs index a9c1af8ef1..dde07cd07f 100644 --- a/examples/net_server/main.rs +++ b/examples/net_server/main.rs @@ -4,7 +4,8 @@ use amethyst::{ core::{bundle::SystemBundle, frame_limiter::FrameRateLimitStrategy, SystemDesc}, ecs::{DispatcherBuilder, Read, System, SystemData, World, Write}, network::simulation::{ - laminar::{LaminarNetworkBundle, LaminarSocket}, +// laminar::{LaminarNetworkBundle, LaminarSocket}, + tcp::TcpNetworkBundle, DeliveryRequirement, NetworkSimulationEvent, TransportResource, UrgencyRequirement, }, prelude::*, @@ -13,11 +14,13 @@ use amethyst::{ Result, }; use log::info; +use std::net::TcpListener; fn main() -> Result<()> { amethyst::start_logger(Default::default()); - let socket = LaminarSocket::bind("0.0.0.0:3457")?; + let listener = TcpListener::bind("0.0.0.0:3457")?; + listener.set_nonblocking(true); let assets_dir = application_root_dir()?.join("./"); @@ -32,7 +35,7 @@ fn main() -> Result<()> { // ); let game_data = GameDataBuilder::default() - .with_bundle(LaminarNetworkBundle::new(Some(socket)))? + .with_bundle(TcpNetworkBundle::new(Some(listener), 1500))? .with_bundle(SpamReceiveBundle)?; let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( @@ -106,7 +109,7 @@ impl<'a> System<'a> for SpamReceiveSystem { // be exchanging messages at a constant rate. Laminar makes use of this by // packaging message acks with the next sent message. Therefore, in order for // reliability to work properly, we'll send a generic "ok" response. - net.send("127.0.0.1:3455".parse().unwrap(), b"ok"); + net.send(*addr, b"ok"); } NetworkSimulationEvent::Connect(addr) => info!("New client connection: {}", addr), NetworkSimulationEvent::Disconnect(addr) => { From 5113d6cf9de5adf0f3bd85c43072e3caf962f34b Mon Sep 17 00:00:00 2001 From: Timon Date: Mon, 30 Sep 2019 23:45:02 +0200 Subject: [PATCH 08/22] [WIP] Tests (#1) * added timing.rs tests * a --- amethyst_network/src/simulation/timing.rs | 24 ++++ .../src/simulation/transport/resource.rs | 118 +++++++++++++++--- .../src/simulation/transport/tcp.rs | 43 ++++--- examples/net_client/main.rs | 11 +- examples/net_server/main.rs | 7 +- 5 files changed, 162 insertions(+), 41 deletions(-) diff --git a/amethyst_network/src/simulation/timing.rs b/amethyst_network/src/simulation/timing.rs index 907d2ec98a..74b2fa20a7 100644 --- a/amethyst_network/src/simulation/timing.rs +++ b/amethyst_network/src/simulation/timing.rs @@ -145,4 +145,28 @@ mod tests { assert_eq!(time.per_frame_duration(), Duration::from_millis(50)); assert_eq!(time.elapsed_duration(), Duration::from_millis(0)); } + + #[test] + fn test_message_send_rate_should_send_every_2_frames() { + let time = NetworkSimulationTime::default().with_message_send_rate(2); + + for i in 1..100 { + // every second frame (even) should return true + if i % 2 == 0 { + assert_eq!(time.should_send_message(i), true); + } else { + assert_eq!(time.should_send_message(i), false); + } + } + } + + #[test] + fn test_elapsed_duration_gets_updated() { + let mut time = NetworkSimulationTime::default(); + + let elapsed_time = Duration::from_millis(500); + time.update_elapsed(elapsed_time); + + assert_eq!(time.elapsed_duration(), elapsed_time) + } } diff --git a/amethyst_network/src/simulation/transport/resource.rs b/amethyst_network/src/simulation/transport/resource.rs index 58488578c0..09c15b9bee 100644 --- a/amethyst_network/src/simulation/transport/resource.rs +++ b/amethyst_network/src/simulation/transport/resource.rs @@ -95,26 +95,114 @@ impl Default for TransportResource { } } -// TODO: Fix these. These are all broken now #[cfg(test)] mod tests { use super::*; #[test] - fn test_send() { - let mut net = create_test_resource(); - let addr = "127.0.0.1:3000".parse().unwrap(); - net.send(addr, test_payload()); - assert_eq!(net.messages.len(), 1); - let packet = &net.messages[0]; + fn test_send_with_default_requirements() { + let mut resource = create_test_resource(); + + resource.send("127.0.0.1:3000".parse().unwrap(), test_payload()); + + let packet = &resource.messages[0]; + + assert_eq!(resource.messages.len(), 1); assert_eq!(packet.delivery, DeliveryRequirement::Default); assert_eq!(packet.urgency, UrgencyRequirement::OnTick); } + #[test] + fn test_send_immediate_message() { + let mut resource = create_test_resource(); + + resource.send_immediate("127.0.0.1:3000".parse().unwrap(), test_payload()); + + let packet = &resource.messages[0]; + + assert_eq!(resource.messages.len(), 1); + assert_eq!(packet.delivery, DeliveryRequirement::Default); + assert_eq!(packet.urgency, UrgencyRequirement::Immediate); + } + + #[test] + fn test_has_messages() { + let mut resource = create_test_resource(); + assert_eq!(resource.has_messages(), false); + resource.send_immediate("127.0.0.1:3000".parse().unwrap(), test_payload()); + assert_eq!(resource.has_messages(), true); + } + + #[test] + fn test_drain_only_immediate_messages() { + let mut resource = create_test_resource(); + + let addr = "127.0.0.1:3000".parse().unwrap(); + resource.send_immediate(addr, test_payload()); + resource.send_immediate(addr, test_payload()); + resource.send(addr, test_payload()); + resource.send(addr, test_payload()); + resource.send_immediate(addr, test_payload()); + + assert_eq!(resource.messages_to_send(|_| false).len(), 3); + assert_eq!(resource.messages_to_send(|_| false).len(), 0); + } + + #[test] + fn test_drain_only_messages_with_specific_requirements() { + let mut resource = create_test_resource(); + + let addr = "127.0.0.1:3000".parse().unwrap(); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::Unreliable, + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::Reliable, + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::ReliableOrdered(None), + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::ReliableSequenced(None), + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::Unreliable, + UrgencyRequirement::OnTick, + ); + + assert_eq!( + resource + .drain_messages(|message| message.delivery == DeliveryRequirement::Unreliable) + .len(), + 2 + ); + // validate removal + assert_eq!( + resource + .drain_messages(|message| message.delivery == DeliveryRequirement::Unreliable) + .len(), + 0 + ); + } + #[test] fn test_send_with_requirements() { use DeliveryRequirement::*; - let mut net = create_test_resource(); + let mut resource = create_test_resource(); let addr = "127.0.0.1:3000".parse().unwrap(); let requirements = [ @@ -126,24 +214,16 @@ mod tests { ]; for req in requirements.iter().cloned() { - net.send_with_requirements(addr, test_payload(), req, UrgencyRequirement::OnTick); + resource.send_with_requirements(addr, test_payload(), req, UrgencyRequirement::OnTick); } - assert_eq!(net.messages.len(), requirements.len()); + assert_eq!(resource.messages.len(), requirements.len()); for (i, req) in requirements.iter().enumerate() { - assert_eq!(net.messages[i].delivery, *req); + assert_eq!(resource.messages[i].delivery, *req); } } - #[test] - fn test_has_socket_and_with_socket() { - let mut net = create_test_resource(); - // assert!(!net.has_socket()); - // net.set_socket(LaminarSocket::bind_any().unwrap()); - // assert!(net.has_socket()); - } - fn test_payload() -> &'static [u8] { b"test" } diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs index 2d13971873..a37070b593 100644 --- a/amethyst_network/src/simulation/transport/tcp.rs +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -6,7 +6,7 @@ use crate::simulation::{ timing::{NetworkSimulationTime, NetworkSimulationTimeSystem}, transport::{ TransportResource, NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, - NETWORK_SIM_TIME_SYSTEM_NAME + NETWORK_SIM_TIME_SYSTEM_NAME, }, }; use amethyst_core::{ @@ -20,8 +20,8 @@ use log::error; use std::{ collections::HashMap, io::{self, Read as IORead, Write as IOWrite}, + net::{SocketAddr, TcpListener, TcpStream}, ops::{Deref, DerefMut}, - net::{SocketAddr, TcpListener, TcpStream} }; const CONNECTION_LISTENER_SYSTEM_NAME: &str = "connection_listener"; @@ -34,7 +34,10 @@ pub struct TcpNetworkBundle { impl TcpNetworkBundle { pub fn new(listener: Option, recv_buffer_size_bytes: usize) -> Self { - Self { listener, recv_buffer_size_bytes } + Self { + listener, + recv_buffer_size_bytes, + } } } @@ -64,7 +67,10 @@ impl<'a, 'b> SystemBundle<'a, 'b> for TcpNetworkBundle { NETWORK_RECV_SYSTEM_NAME, &[CONNECTION_LISTENER_SYSTEM_NAME], ); - world.insert(TcpNetworkResource::new(self.listener, self.recv_buffer_size_bytes)); + world.insert(TcpNetworkResource::new( + self.listener, + self.recv_buffer_size_bytes, + )); Ok(()) } } @@ -76,7 +82,7 @@ impl<'s> System<'s> for TcpConnectionListenerSystem { type SystemData = ( Write<'s, TcpNetworkResource>, Read<'s, NetworkSimulationTime>, - Write<'s, EventChannel> + Write<'s, EventChannel>, ); fn run(&mut self, (mut net, sim_time, mut event_channel): Self::SystemData) { @@ -85,11 +91,13 @@ impl<'s> System<'s> for TcpConnectionListenerSystem { loop { match listener.accept() { Ok((stream, addr)) => { - stream.set_nonblocking(true).expect("Setting non-blocking mode"); + stream + .set_nonblocking(true) + .expect("Setting non-blocking mode"); stream.set_nodelay(true).expect("Setting nodelay"); resource.streams.insert(addr, stream); event_channel.single_write(NetworkSimulationEvent::Connect(addr)); - }, + } Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { break; } @@ -120,7 +128,10 @@ impl<'s> System<'s> for TcpNetworkSendSystem { DeliveryRequirement::ReliableOrdered(_) | DeliveryRequirement::Default => { let stream = net.get_or_create_stream(message.destination); if let Err(e) = stream.write(&message.payload) { - error!("There was an error when attempting to send message: {:?}", e); + error!( + "There was an error when attempting to send message: {:?}", + e + ); } } delivery => panic!( @@ -155,18 +166,20 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { ); event_channel.single_write(event); } else { - event_channel.single_write(NetworkSimulationEvent::Disconnect(peer_addr)); -// resource.drop_stream(peer_addr); + event_channel + .single_write(NetworkSimulationEvent::Disconnect(peer_addr)); + // resource.drop_stream(peer_addr); } } Err(e) => { match e.kind() { io::ErrorKind::ConnectionReset => { - event_channel.single_write(NetworkSimulationEvent::Disconnect(peer_addr)); -// resource.drop_stream(peer_addr); + event_channel + .single_write(NetworkSimulationEvent::Disconnect(peer_addr)); + // resource.drop_stream(peer_addr); } io::ErrorKind::WouldBlock => {} - _ => error!("Encountered an error receiving data: {:?}", e) + _ => error!("Encountered an error receiving data: {:?}", e), } break; } @@ -187,7 +200,7 @@ impl TcpNetworkResource { Self { listener, streams: HashMap::new(), - recv_buffer: vec![0; recv_buffer_size_bytes] + recv_buffer: vec![0; recv_buffer_size_bytes], } } @@ -228,7 +241,7 @@ impl Default for TcpNetworkResource { Self { listener: None, streams: HashMap::new(), - recv_buffer: Vec::new() + recv_buffer: Vec::new(), } } } diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index e4e0fc3845..591666b7fa 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -2,23 +2,24 @@ use amethyst::{ core::{frame_limiter::FrameRateLimitStrategy, Time}, ecs::{Read, System, Write}, network::simulation::{ -// laminar::{LaminarNetworkBundle, LaminarSocket}, + // laminar::{LaminarNetworkBundle, LaminarSocket}, tcp::TcpNetworkBundle, - NetworkSimulationTime, TransportResource, + NetworkSimulationTime, + TransportResource, }, prelude::*, utils::application_root_dir, Result, }; use log::info; -use std::time::Duration; use std::net::TcpListener; +use std::time::Duration; fn main() -> Result<()> { amethyst::start_logger(Default::default()); -// let listener = TcpListener::bind("0.0.0.0:3455")?; -// listener.set_nonblocking(true)?; + // let listener = TcpListener::bind("0.0.0.0:3455")?; + // listener.set_nonblocking(true)?; let assets_dir = application_root_dir()?.join("./"); diff --git a/examples/net_server/main.rs b/examples/net_server/main.rs index dde07cd07f..32d1313c4e 100644 --- a/examples/net_server/main.rs +++ b/examples/net_server/main.rs @@ -4,9 +4,12 @@ use amethyst::{ core::{bundle::SystemBundle, frame_limiter::FrameRateLimitStrategy, SystemDesc}, ecs::{DispatcherBuilder, Read, System, SystemData, World, Write}, network::simulation::{ -// laminar::{LaminarNetworkBundle, LaminarSocket}, + // laminar::{LaminarNetworkBundle, LaminarSocket}, tcp::TcpNetworkBundle, - DeliveryRequirement, NetworkSimulationEvent, TransportResource, UrgencyRequirement, + DeliveryRequirement, + NetworkSimulationEvent, + TransportResource, + UrgencyRequirement, }, prelude::*, shrev::{EventChannel, ReaderId}, From dab41ba85f32847ac20427c347b3a14f81782a27 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Mon, 30 Sep 2019 22:58:32 -0400 Subject: [PATCH 09/22] fix up a couple of unhandled errors. Still a couple more to go. --- .../src/simulation/transport/tcp.rs | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs index a37070b593..3031b38f29 100644 --- a/amethyst_network/src/simulation/transport/tcp.rs +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -8,6 +8,7 @@ use crate::simulation::{ TransportResource, NETWORK_RECV_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME, NETWORK_SIM_TIME_SYSTEM_NAME, }, + Message, }; use amethyst_core::{ bundle::SystemBundle, @@ -16,7 +17,7 @@ use amethyst_core::{ }; use amethyst_error::Error; use bytes::Bytes; -use log::error; +use log::{error, warn}; use std::{ collections::HashMap, io::{self, Read as IORead, Write as IOWrite}, @@ -63,7 +64,7 @@ impl<'a, 'b> SystemBundle<'a, 'b> for TcpNetworkBundle { &[CONNECTION_LISTENER_SYSTEM_NAME], ); builder.add( - TcpNetworkRecvSystem, + TcpNetworkRecvSystem::new(), NETWORK_RECV_SYSTEM_NAME, &[CONNECTION_LISTENER_SYSTEM_NAME], ); @@ -93,7 +94,7 @@ impl<'s> System<'s> for TcpConnectionListenerSystem { Ok((stream, addr)) => { stream .set_nonblocking(true) - .expect("Setting non-blocking mode"); + .expect("Setting nonblocking mode"); stream.set_nodelay(true).expect("Setting nodelay"); resource.streams.insert(addr, stream); event_channel.single_write(NetworkSimulationEvent::Connect(addr)); @@ -125,14 +126,12 @@ impl<'s> System<'s> for TcpNetworkSendSystem { let messages = transport.messages_to_send(|_| sim_time.should_send_messages()); for message in messages.iter() { match message.delivery { + DeliveryRequirement::ReliableOrdered(Some(_)) => { + warn!("Streams are not supported by TCP and will be ignored."); + write_message(message, &mut net); + } DeliveryRequirement::ReliableOrdered(_) | DeliveryRequirement::Default => { - let stream = net.get_or_create_stream(message.destination); - if let Err(e) = stream.write(&message.payload) { - error!( - "There was an error when attempting to send message: {:?}", - e - ); - } + write_message(message, &mut net); } delivery => panic!( "{:?} is unsupported. TCP only supports ReliableOrdered by design.", @@ -143,8 +142,28 @@ impl<'s> System<'s> for TcpNetworkSendSystem { } } +fn write_message(message: &Message, net: &mut TcpNetworkResource) { + let stream = net.get_or_create_stream(message.destination); + if let Err(e) = stream.write(&message.payload) { + error!( + "There was an error when attempting to send message: {:?}", + e + ); + } +} + /// System to receive messages from all open `TcpStream`s. -pub struct TcpNetworkRecvSystem; +pub struct TcpNetworkRecvSystem { + streams_to_drop: Vec +} + +impl TcpNetworkRecvSystem { + pub fn new() -> Self { + Self { + streams_to_drop: Vec::new() + } + } +} impl<'s> System<'s> for TcpNetworkRecvSystem { type SystemData = ( @@ -155,7 +174,13 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { fn run(&mut self, (mut net, mut event_channel): Self::SystemData) { let resource = net.deref_mut(); for (_, stream) in resource.streams.iter_mut() { - let peer_addr = stream.peer_addr().unwrap(); + let peer_addr = match stream.peer_addr() { + Ok(addr) => addr, + Err(e) => { + error!("Encountered an error getting peer_addr: {:?}", e); + continue; + } + }; loop { match stream.read(&mut resource.recv_buffer) { Ok(recv_len) => { @@ -168,7 +193,8 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { } else { event_channel .single_write(NetworkSimulationEvent::Disconnect(peer_addr)); - // resource.drop_stream(peer_addr); + self.streams_to_drop.push(peer_addr); + break; } } Err(e) => { @@ -176,7 +202,7 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { io::ErrorKind::ConnectionReset => { event_channel .single_write(NetworkSimulationEvent::Disconnect(peer_addr)); - // resource.drop_stream(peer_addr); + self.streams_to_drop.push(peer_addr); } io::ErrorKind::WouldBlock => {} _ => error!("Encountered an error receiving data: {:?}", e), @@ -186,6 +212,9 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { } } } + self.streams_to_drop.drain(..).for_each(|peer_addr| { + resource.drop_stream(peer_addr); + }) } } From f3874f280e464d9c0ef93482fc69558c7d66a73c Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Wed, 2 Oct 2019 02:06:27 -0400 Subject: [PATCH 10/22] harden the implementation of tcp. In fixing this, I also noticed that I had the dependencies specified backwards for each of the network bundles. Whoops :P. --- .../src/simulation/transport/laminar.rs | 22 ++- .../src/simulation/transport/resource.rs | 11 +- .../src/simulation/transport/tcp.rs | 137 ++++++++++-------- .../src/simulation/transport/udp.rs | 18 +-- examples/net_client/main.rs | 10 +- 5 files changed, 108 insertions(+), 90 deletions(-) diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs index fb4c0903c6..1aefdc07b7 100644 --- a/amethyst_network/src/simulation/transport/laminar.rs +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -39,25 +39,21 @@ impl<'a, 'b> SystemBundle<'a, 'b> for LaminarNetworkBundle { world: &mut World, builder: &mut DispatcherBuilder<'_, '_>, ) -> Result<(), Error> { - builder.add( - NetworkSimulationTimeSystem, - NETWORK_SIM_TIME_SYSTEM_NAME, - &[], - ); - builder.add( - LaminarNetworkSendSystem, - NETWORK_SEND_SYSTEM_NAME, - &[NETWORK_SIM_TIME_SYSTEM_NAME], - ); + builder.add(LaminarNetworkSendSystem, NETWORK_SEND_SYSTEM_NAME, &[]); builder.add( LaminarNetworkPollSystem, NETWORK_POLL_SYSTEM_NAME, - &[NETWORK_SIM_TIME_SYSTEM_NAME, NETWORK_SEND_SYSTEM_NAME], + &[NETWORK_SEND_SYSTEM_NAME], ); builder.add( LaminarNetworkRecvSystem, NETWORK_RECV_SYSTEM_NAME, - &[NETWORK_SIM_TIME_SYSTEM_NAME, NETWORK_POLL_SYSTEM_NAME], + &[NETWORK_POLL_SYSTEM_NAME], + ); + builder.add( + NetworkSimulationTimeSystem, + NETWORK_SIM_TIME_SYSTEM_NAME, + &[NETWORK_RECV_SYSTEM_NAME], ); world.insert(LaminarSocketResource::new(self.socket)); Ok(()) @@ -75,7 +71,7 @@ impl<'s> System<'s> for LaminarNetworkSendSystem { fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { socket.get_mut().map(|socket| { - let messages = transport.messages_to_send(|_| sim_time.should_send_messages()); + let messages = transport.drain_messages_to_send(|_| sim_time.should_send_messages()); for message in messages.iter() { let packet = match message.delivery { diff --git a/amethyst_network/src/simulation/transport/resource.rs b/amethyst_network/src/simulation/transport/resource.rs index 09c15b9bee..e3142b0128 100644 --- a/amethyst_network/src/simulation/transport/resource.rs +++ b/amethyst_network/src/simulation/transport/resource.rs @@ -57,9 +57,14 @@ impl TransportResource { !self.messages.is_empty() } + /// Returns a reference to the owned messages. + pub fn get_messages(&self) -> &VecDeque { + &self.messages + } + /// Returns the messages to send by returning the immediate messages or anything adhering to /// the given filter. - pub fn messages_to_send( + pub fn drain_messages_to_send( &mut self, mut filter: impl FnMut(&mut Message) -> bool, ) -> Vec { @@ -144,8 +149,8 @@ mod tests { resource.send(addr, test_payload()); resource.send_immediate(addr, test_payload()); - assert_eq!(resource.messages_to_send(|_| false).len(), 3); - assert_eq!(resource.messages_to_send(|_| false).len(), 0); + assert_eq!(resource.drain_messages_to_send(|_| false).len(), 3); + assert_eq!(resource.drain_messages_to_send(|_| false).len(), 0); } #[test] diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs index 3031b38f29..6ab4944bc3 100644 --- a/amethyst_network/src/simulation/transport/tcp.rs +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -22,10 +22,11 @@ use std::{ collections::HashMap, io::{self, Read as IORead, Write as IOWrite}, net::{SocketAddr, TcpListener, TcpStream}, - ops::{Deref, DerefMut}, + ops::DerefMut, }; const CONNECTION_LISTENER_SYSTEM_NAME: &str = "connection_listener"; +const STREAM_MANAGEMENT_SYSTEM_NAME: &str = "stream_management"; /// Use this network bundle to add the various underlying tcp network systems to your game. pub struct TcpNetworkBundle { @@ -48,25 +49,25 @@ impl<'a, 'b> SystemBundle<'a, 'b> for TcpNetworkBundle { world: &mut World, builder: &mut DispatcherBuilder<'_, '_>, ) -> Result<(), Error> { + builder.add(TcpNetworkSendSystem, NETWORK_SEND_SYSTEM_NAME, &[]); + builder.add(TcpNetworkRecvSystem, NETWORK_RECV_SYSTEM_NAME, &[]); builder.add( - NetworkSimulationTimeSystem, - NETWORK_SIM_TIME_SYSTEM_NAME, - &[], + TcpStreamManagementSystem, + STREAM_MANAGEMENT_SYSTEM_NAME, + &[NETWORK_SEND_SYSTEM_NAME, NETWORK_RECV_SYSTEM_NAME], ); builder.add( TcpConnectionListenerSystem, CONNECTION_LISTENER_SYSTEM_NAME, - &[NETWORK_SIM_TIME_SYSTEM_NAME], + &[NETWORK_SEND_SYSTEM_NAME, NETWORK_RECV_SYSTEM_NAME], ); builder.add( - TcpNetworkSendSystem, - NETWORK_SEND_SYSTEM_NAME, - &[CONNECTION_LISTENER_SYSTEM_NAME], - ); - builder.add( - TcpNetworkRecvSystem::new(), - NETWORK_RECV_SYSTEM_NAME, - &[CONNECTION_LISTENER_SYSTEM_NAME], + NetworkSimulationTimeSystem, + NETWORK_SIM_TIME_SYSTEM_NAME, + &[ + STREAM_MANAGEMENT_SYSTEM_NAME, + CONNECTION_LISTENER_SYSTEM_NAME, + ], ); world.insert(TcpNetworkResource::new( self.listener, @@ -76,17 +77,56 @@ impl<'a, 'b> SystemBundle<'a, 'b> for TcpNetworkBundle { } } +/// System to manage the current active TCPStreams. +pub struct TcpStreamManagementSystem; + +impl<'s> System<'s> for TcpStreamManagementSystem { + type SystemData = ( + Write<'s, TcpNetworkResource>, + Read<'s, TransportResource>, + Write<'s, EventChannel>, + ); + + fn run(&mut self, (mut net, transport, mut event_channel): Self::SystemData) { + // Make connections for each message in the channel if one hasn't yet been established + transport.get_messages().iter().for_each(|message| { + if !net.streams.contains_key(&message.destination) { + let s = match TcpStream::connect(message.destination) { + Ok(s) => s, + Err(e) => { + warn!( + "Error attempting to connection to {}: {:?}", + message.destination, e + ); + return; + } + }; + s.set_nonblocking(true).expect("Setting non-blocking mode"); + s.set_nodelay(true).expect("Setting nodelay"); + net.streams.insert(message.destination, (true, s)); + } + }); + + // Remove inactive connections + net.streams.retain(|addr, (active, _)| { + if !*active { + event_channel.single_write(NetworkSimulationEvent::Disconnect(*addr)); + } + *active + }); + } +} + /// System to listen for incoming connections and cache them to the resource. pub struct TcpConnectionListenerSystem; impl<'s> System<'s> for TcpConnectionListenerSystem { type SystemData = ( Write<'s, TcpNetworkResource>, - Read<'s, NetworkSimulationTime>, Write<'s, EventChannel>, ); - fn run(&mut self, (mut net, sim_time, mut event_channel): Self::SystemData) { + fn run(&mut self, (mut net, mut event_channel): Self::SystemData) { let resource = net.deref_mut(); if let Some(ref listener) = resource.listener { loop { @@ -96,7 +136,7 @@ impl<'s> System<'s> for TcpConnectionListenerSystem { .set_nonblocking(true) .expect("Setting nonblocking mode"); stream.set_nodelay(true).expect("Setting nodelay"); - resource.streams.insert(addr, stream); + resource.streams.insert(addr, (true, stream)); event_channel.single_write(NetworkSimulationEvent::Connect(addr)); } Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { @@ -123,7 +163,7 @@ impl<'s> System<'s> for TcpNetworkSendSystem { ); fn run(&mut self, (mut transport, mut net, sim_time): Self::SystemData) { - let messages = transport.messages_to_send(|_| sim_time.should_send_messages()); + let messages = transport.drain_messages_to_send(|_| sim_time.should_send_messages()); for message in messages.iter() { match message.delivery { DeliveryRequirement::ReliableOrdered(Some(_)) => { @@ -143,27 +183,18 @@ impl<'s> System<'s> for TcpNetworkSendSystem { } fn write_message(message: &Message, net: &mut TcpNetworkResource) { - let stream = net.get_or_create_stream(message.destination); - if let Err(e) = stream.write(&message.payload) { - error!( - "There was an error when attempting to send message: {:?}", - e - ); - } + net.get_stream(message.destination).map(|(_, stream)| { + if let Err(e) = stream.write(&message.payload) { + error!( + "There was an error when attempting to send message: {:?}", + e + ); + } + }); } /// System to receive messages from all open `TcpStream`s. -pub struct TcpNetworkRecvSystem { - streams_to_drop: Vec -} - -impl TcpNetworkRecvSystem { - pub fn new() -> Self { - Self { - streams_to_drop: Vec::new() - } - } -} +pub struct TcpNetworkRecvSystem; impl<'s> System<'s> for TcpNetworkRecvSystem { type SystemData = ( @@ -173,14 +204,18 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { fn run(&mut self, (mut net, mut event_channel): Self::SystemData) { let resource = net.deref_mut(); - for (_, stream) in resource.streams.iter_mut() { + for (_, (active, stream)) in resource.streams.iter_mut() { + // If we can't get a peer_addr, there is likely something pretty wrong with the + // connection so we'll mark it inactive. let peer_addr = match stream.peer_addr() { Ok(addr) => addr, Err(e) => { - error!("Encountered an error getting peer_addr: {:?}", e); + warn!("Encountered an error getting peer_addr: {:?}", e); + *active = false; continue; } }; + loop { match stream.read(&mut resource.recv_buffer) { Ok(recv_len) => { @@ -191,18 +226,14 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { ); event_channel.single_write(event); } else { - event_channel - .single_write(NetworkSimulationEvent::Disconnect(peer_addr)); - self.streams_to_drop.push(peer_addr); + *active = false; break; } } Err(e) => { match e.kind() { io::ErrorKind::ConnectionReset => { - event_channel - .single_write(NetworkSimulationEvent::Disconnect(peer_addr)); - self.streams_to_drop.push(peer_addr); + *active = false; } io::ErrorKind::WouldBlock => {} _ => error!("Encountered an error receiving data: {:?}", e), @@ -212,15 +243,12 @@ impl<'s> System<'s> for TcpNetworkRecvSystem { } } } - self.streams_to_drop.drain(..).for_each(|peer_addr| { - resource.drop_stream(peer_addr); - }) } } pub struct TcpNetworkResource { listener: Option, - streams: HashMap, + streams: HashMap, recv_buffer: Vec, } @@ -248,19 +276,14 @@ impl TcpNetworkResource { self.listener = None; } - /// Returns the stream associated with the `SocketAddr` or creates one if one doesn't exist. - pub fn get_or_create_stream(&mut self, addr: SocketAddr) -> &mut TcpStream { - self.streams.entry(addr).or_insert_with(|| { - let s = TcpStream::connect(addr).unwrap(); - s.set_nonblocking(true).expect("Setting non-blocking mode"); - s.set_nodelay(true).expect("Setting nodelay"); - s - }) + /// Returns a tuple of an active TcpStream and whether ot not that stream is active + pub fn get_stream(&mut self, addr: SocketAddr) -> Option<&mut (bool, TcpStream)> { + self.streams.get_mut(&addr) } /// Drop the stream with the given `SocketAddr`. This will be called when a peer seems to have - /// been disconnected. - pub fn drop_stream(&mut self, addr: SocketAddr) -> Option { + /// been disconnected + pub fn drop_stream(&mut self, addr: SocketAddr) -> Option<(bool, TcpStream)> { self.streams.remove(&addr) } } diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs index daea774309..590a423733 100644 --- a/amethyst_network/src/simulation/transport/udp.rs +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -40,20 +40,16 @@ impl<'a, 'b> SystemBundle<'a, 'b> for UdpNetworkBundle { world: &mut World, builder: &mut DispatcherBuilder<'_, '_>, ) -> Result<(), Error> { + builder.add(UdpNetworkSendSystem, NETWORK_SEND_SYSTEM_NAME, &[]); builder.add( - NetworkSimulationTimeSystem, - NETWORK_SIM_TIME_SYSTEM_NAME, + UdpNetworkRecvSystem::with_buffer_capacity(self.recv_buffer_size_bytes), + NETWORK_RECV_SYSTEM_NAME, &[], ); builder.add( - UdpNetworkSendSystem, - NETWORK_SEND_SYSTEM_NAME, - &[NETWORK_SIM_TIME_SYSTEM_NAME], - ); - builder.add( - UdpNetworkRecvSystem::with_buffer_capacity(self.recv_buffer_size_bytes), - NETWORK_RECV_SYSTEM_NAME, - &[NETWORK_SIM_TIME_SYSTEM_NAME], + NetworkSimulationTimeSystem, + NETWORK_SIM_TIME_SYSTEM_NAME, + &[NETWORK_SEND_SYSTEM_NAME, NETWORK_RECV_SYSTEM_NAME], ); world.insert(UdpSocketResource::new(self.socket)); Ok(()) @@ -71,7 +67,7 @@ impl<'s> System<'s> for UdpNetworkSendSystem { fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { socket.get_mut().map(|socket| { - let messages = transport.messages_to_send(|_| sim_time.should_send_messages()); + let messages = transport.drain_messages_to_send(|_| sim_time.should_send_messages()); for message in messages.iter() { match message.delivery { DeliveryRequirement::Unreliable | DeliveryRequirement::Default => { diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index 591666b7fa..7b4aa362dc 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -2,10 +2,9 @@ use amethyst::{ core::{frame_limiter::FrameRateLimitStrategy, Time}, ecs::{Read, System, Write}, network::simulation::{ - // laminar::{LaminarNetworkBundle, LaminarSocket}, + laminar::{LaminarNetworkBundle, LaminarSocket}, tcp::TcpNetworkBundle, - NetworkSimulationTime, - TransportResource, + NetworkSimulationTime, TransportResource, }, prelude::*, utils::application_root_dir, @@ -18,11 +17,10 @@ use std::time::Duration; fn main() -> Result<()> { amethyst::start_logger(Default::default()); - // let listener = TcpListener::bind("0.0.0.0:3455")?; - // listener.set_nonblocking(true)?; - let assets_dir = application_root_dir()?.join("./"); + // let socket = LaminarSocket::bind("0.0.0.0:3457").unwrap(); + let game_data = GameDataBuilder::default() .with_bundle(TcpNetworkBundle::new(None, 1500))? .with(SpamSystem::new(), "spam", &[]); From 24ac7289f57f3ac284d8ff46017fe750eedcf0e1 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Wed, 2 Oct 2019 02:16:52 -0400 Subject: [PATCH 11/22] rename to `should_send_message_now` and added a new method to set the frame number --- amethyst_network/src/simulation/timing.rs | 8 +++++++- amethyst_network/src/simulation/transport/laminar.rs | 2 +- amethyst_network/src/simulation/transport/tcp.rs | 2 +- amethyst_network/src/simulation/transport/udp.rs | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/amethyst_network/src/simulation/timing.rs b/amethyst_network/src/simulation/timing.rs index 74b2fa20a7..0d7724bd50 100644 --- a/amethyst_network/src/simulation/timing.rs +++ b/amethyst_network/src/simulation/timing.rs @@ -49,7 +49,7 @@ impl NetworkSimulationTime { /// Determines whether or not to send a message in the current frame based on the /// `message_send_rate` - pub fn should_send_messages(&self) -> bool { + pub fn should_send_message_now(&self) -> bool { self.should_send_message(self.frame_number) } @@ -80,6 +80,12 @@ impl NetworkSimulationTime { self.frame_number } + /// Sets the frame number to the given frame number. This is useful when synchronizing frames + /// with a server for example. + pub fn set_frame_number(&mut self, new_frame: u32) { + self.frame_number = new_frame; + } + /// Returns the total duration since the last simulation frame pub fn elapsed_duration(&self) -> Duration { self.elapsed_duration diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs index 1aefdc07b7..67d3c2a981 100644 --- a/amethyst_network/src/simulation/transport/laminar.rs +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -71,7 +71,7 @@ impl<'s> System<'s> for LaminarNetworkSendSystem { fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { socket.get_mut().map(|socket| { - let messages = transport.drain_messages_to_send(|_| sim_time.should_send_messages()); + let messages = transport.drain_messages_to_send(|_| sim_time.should_send_message_now()); for message in messages.iter() { let packet = match message.delivery { diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs index 6ab4944bc3..7463612cda 100644 --- a/amethyst_network/src/simulation/transport/tcp.rs +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -163,7 +163,7 @@ impl<'s> System<'s> for TcpNetworkSendSystem { ); fn run(&mut self, (mut transport, mut net, sim_time): Self::SystemData) { - let messages = transport.drain_messages_to_send(|_| sim_time.should_send_messages()); + let messages = transport.drain_messages_to_send(|_| sim_time.should_send_message_now()); for message in messages.iter() { match message.delivery { DeliveryRequirement::ReliableOrdered(Some(_)) => { diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs index 590a423733..39b009a76c 100644 --- a/amethyst_network/src/simulation/transport/udp.rs +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -67,7 +67,7 @@ impl<'s> System<'s> for UdpNetworkSendSystem { fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { socket.get_mut().map(|socket| { - let messages = transport.drain_messages_to_send(|_| sim_time.should_send_messages()); + let messages = transport.drain_messages_to_send(|_| sim_time.should_send_message_now()); for message in messages.iter() { match message.delivery { DeliveryRequirement::Unreliable | DeliveryRequirement::Default => { From f3b81c152c405e953e16fb5013ba4030b6f0accc Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Wed, 2 Oct 2019 02:46:41 -0400 Subject: [PATCH 12/22] Apply suggestions from code review Co-Authored-By: Timon --- amethyst_network/src/simulation/timing.rs | 10 +++++----- amethyst_network/src/simulation/transport/laminar.rs | 12 ++++++------ .../src/simulation/transport/resource.rs | 12 ++++++------ amethyst_network/src/simulation/transport/tcp.rs | 10 +++++----- amethyst_network/src/simulation/transport/udp.rs | 8 ++++---- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/amethyst_network/src/simulation/timing.rs b/amethyst_network/src/simulation/timing.rs index 0d7724bd50..5d5683e8c3 100644 --- a/amethyst_network/src/simulation/timing.rs +++ b/amethyst_network/src/simulation/timing.rs @@ -91,20 +91,20 @@ impl NetworkSimulationTime { self.elapsed_duration } - /// The duration between each simulation frame. This number is calculated when a frame rate + /// Returns the duration between each simulation frame. This number is calculated when a frame rate /// is set pub fn per_frame_duration(&self) -> Duration { self.per_frame_duration } - /// The rate at which messages should be sent over the network. - /// i.e. 'Every N frames' where N is `message_send_rate`. + /// Returns the rate at which messages should be sent over the network. + /// i.e. 'every N frames' where N is `message_send_rate`. pub fn message_send_rate(&self) -> u8 { self.message_send_rate } - /// The number of frames behind the simulation is. This will usually be 0 or 1 if the ECS system - /// is keeping up + /// Returns the number of frames which the simulation is behind. This will usually be 0 or 1 if the ECS system + /// is keeping up. pub fn frame_lag(&self) -> u32 { self.frame_lag } diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs index 67d3c2a981..384ff5852b 100644 --- a/amethyst_network/src/simulation/transport/laminar.rs +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -22,7 +22,7 @@ use bytes::Bytes; use log::error; use std::time::Instant; -/// Use this network bundle to add the various underlying laminar network systems to your game. +/// Use this network bundle to add the laminar transport layer to your game. pub struct LaminarNetworkBundle { socket: Option, } @@ -152,7 +152,7 @@ impl<'s> System<'s> for LaminarNetworkRecvSystem { } } -/// Resource to own the Laminar socket. +/// Resource that owns the Laminar socket. pub struct LaminarSocketResource { socket: Option, } @@ -164,22 +164,22 @@ impl Default for LaminarSocketResource { } impl LaminarSocketResource { - /// Create a new instance of the `UdpSocketResource` + /// Creates a new instance of the `UdpSocketResource` pub fn new(socket: Option) -> Self { Self { socket } } - /// Return a mutable reference to the socket if there is one configured. + /// Returns a mutable reference to the socket if there is one configured. pub fn get_mut(&mut self) -> Option<&mut LaminarSocket> { self.socket.as_mut() } - /// Set the bound socket to the `LaminarSocketResource` + /// Sets the bound socket to the `LaminarSocketResource`. pub fn set_socket(&mut self, socket: LaminarSocket) { self.socket = Some(socket); } - /// Drops the socket from the `LaminarSocketResource` + /// Drops the socket from the `LaminarSocketResource`. pub fn drop_socket(&mut self) { self.socket = None; } diff --git a/amethyst_network/src/simulation/transport/resource.rs b/amethyst_network/src/simulation/transport/resource.rs index e3142b0128..11208525c9 100644 --- a/amethyst_network/src/simulation/transport/resource.rs +++ b/amethyst_network/src/simulation/transport/resource.rs @@ -5,20 +5,20 @@ use crate::simulation::{ use std::{collections::VecDeque, net::SocketAddr}; /// Resource serving as the owner of the queue of messages to be sent. This resource also serves -/// as the interface for other systems to send messages +/// as the interface for other systems to send messages. pub struct TransportResource { messages: VecDeque, } impl TransportResource { - /// Create a new `TransportResource` + /// Creates a new `TransportResource`. pub fn new() -> Self { Self { messages: VecDeque::new(), } } - /// Create a `Message` with the default guarantees provided by the `Socket` implementation and + /// Creates a `Message` with the default guarantees provided by the `Socket` implementation and /// pushes it onto the messages queue to be sent on next sim tick. pub fn send(&mut self, destination: SocketAddr, payload: &[u8]) { self.send_with_requirements( @@ -29,8 +29,8 @@ impl TransportResource { ); } - /// Create a `Message` with the default guarantees provided by the `Socket` implementation and - /// pushes it onto the messages queue to be sent immediately. + /// Creates a `Message` with the default guarantees provided by the `Socket` implementation and + /// Pushes it onto the messages queue to be sent immediately. pub fn send_immediate(&mut self, destination: SocketAddr, payload: &[u8]) { self.send_with_requirements( destination, @@ -40,7 +40,7 @@ impl TransportResource { ); } - /// Create and queue a `Message` with the specified guarantee + /// Creates and queue a `Message` with the specified guarantee. pub fn send_with_requirements( &mut self, destination: SocketAddr, diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs index 7463612cda..6d17cdff5a 100644 --- a/amethyst_network/src/simulation/transport/tcp.rs +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -28,7 +28,7 @@ use std::{ const CONNECTION_LISTENER_SYSTEM_NAME: &str = "connection_listener"; const STREAM_MANAGEMENT_SYSTEM_NAME: &str = "stream_management"; -/// Use this network bundle to add the various underlying tcp network systems to your game. +/// Use this network bundle to add the TCP transport layer to your game. pub struct TcpNetworkBundle { listener: Option, recv_buffer_size_bytes: usize, @@ -261,17 +261,17 @@ impl TcpNetworkResource { } } - /// Return a mutable reference to the listener if there is one configured. + /// Returns a mutable reference to the listener if there is one configured. pub fn get_mut(&mut self) -> Option<&mut TcpListener> { self.listener.as_mut() } - /// Set the bound listener to the `TcpNetworkResource` + /// Sets the bound listener to the `TcpNetworkResource`. pub fn set_listener(&mut self, listener: TcpListener) { self.listener = Some(listener); } - /// Drops the listener from the `TcpNetworkResource` + /// Drops the listener from the `TcpNetworkResource`. pub fn drop_listener(&mut self) { self.listener = None; } @@ -281,7 +281,7 @@ impl TcpNetworkResource { self.streams.get_mut(&addr) } - /// Drop the stream with the given `SocketAddr`. This will be called when a peer seems to have + /// Drops the stream with the given `SocketAddr`. This will be called when a peer seems to have /// been disconnected pub fn drop_stream(&mut self, addr: SocketAddr) -> Option<(bool, TcpStream)> { self.streams.remove(&addr) diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs index 39b009a76c..3ff37d6c90 100644 --- a/amethyst_network/src/simulation/transport/udp.rs +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -19,7 +19,7 @@ use bytes::Bytes; use log::error; use std::{io, net::UdpSocket}; -/// Use this network bundle to add the various underlying UDP network systems to your game. +/// Use this network bundle to add the UDP transport layer to your game. pub struct UdpNetworkBundle { socket: Option, recv_buffer_size_bytes: usize, @@ -145,17 +145,17 @@ impl UdpSocketResource { Self { socket } } - /// Return a mutable reference to the socket if there is one configured. + /// Returns a mutable reference to the socket if there is one configured. pub fn get_mut(&mut self) -> Option<&mut UdpSocket> { self.socket.as_mut() } - /// Set the bound socket to the `UdpSocketResource` + /// Sets the bound socket to the `UdpSocketResource`. pub fn set_socket(&mut self, socket: UdpSocket) { self.socket = Some(socket); } - /// Drops the socket from the `UdpSocketResource` + /// Drops the socket from the `UdpSocketResource`. pub fn drop_socket(&mut self) { self.socket = None; } From a343a12c886d22b44fdbd47f1d48fc7a13e6918e Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Wed, 2 Oct 2019 21:52:16 -0400 Subject: [PATCH 13/22] small refactor to move the transport resource into the transport.rs file --- amethyst_network/src/simulation/transport.rs | 245 +++++++++++++++++- .../src/simulation/transport/resource.rs | 239 ----------------- 2 files changed, 241 insertions(+), 243 deletions(-) delete mode 100644 amethyst_network/src/simulation/transport/resource.rs diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index e029cc5322..6acdfc8b0f 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -2,15 +2,252 @@ //! protocols. One important thing to note if you're implementing your own, the underlying sockets //! MUST be non-blocking in order to play nicely with the ECS scheduler. -mod resource; - pub mod laminar; pub mod tcp; pub mod udp; -pub use resource::TransportResource; - const NETWORK_SIM_TIME_SYSTEM_NAME: &str = "simulation_time"; const NETWORK_SEND_SYSTEM_NAME: &str = "network_send"; const NETWORK_RECV_SYSTEM_NAME: &str = "network_recv"; const NETWORK_POLL_SYSTEM_NAME: &str = "network_poll"; + +use crate::simulation::{ + message::Message, + requirements::{DeliveryRequirement, UrgencyRequirement}, +}; +use std::{collections::VecDeque, net::SocketAddr}; + + +/// Resource serving as the owner of the queue of messages to be sent. This resource also serves +/// as the interface for other systems to send messages. +pub struct TransportResource { + messages: VecDeque, +} + +impl TransportResource { + /// Creates a new `TransportResource`. + pub fn new() -> Self { + Self { + messages: VecDeque::new(), + } + } + + /// Creates a `Message` with the default guarantees provided by the `Socket` implementation and + /// pushes it onto the messages queue to be sent on next sim tick. + pub fn send(&mut self, destination: SocketAddr, payload: &[u8]) { + self.send_with_requirements( + destination, + payload, + DeliveryRequirement::Default, + UrgencyRequirement::OnTick, + ); + } + + /// Creates a `Message` with the default guarantees provided by the `Socket` implementation and + /// Pushes it onto the messages queue to be sent immediately. + pub fn send_immediate(&mut self, destination: SocketAddr, payload: &[u8]) { + self.send_with_requirements( + destination, + payload, + DeliveryRequirement::Default, + UrgencyRequirement::Immediate, + ); + } + + /// Creates and queue a `Message` with the specified guarantee. + pub fn send_with_requirements( + &mut self, + destination: SocketAddr, + payload: &[u8], + delivery: DeliveryRequirement, + timing: UrgencyRequirement, + ) { + let message = Message::new(destination, payload, delivery, timing); + self.messages.push_back(message); + } + + /// Returns true if there are messages enqueued to be sent. + pub fn has_messages(&self) -> bool { + !self.messages.is_empty() + } + + /// Returns a reference to the owned messages. + pub fn get_messages(&self) -> &VecDeque { + &self.messages + } + + /// Returns the messages to send by returning the immediate messages or anything adhering to + /// the given filter. + pub fn drain_messages_to_send( + &mut self, + mut filter: impl FnMut(&mut Message) -> bool, + ) -> Vec { + self.drain_messages(|message| { + message.urgency == UrgencyRequirement::Immediate || filter(message) + }) + } + + /// Drains the messages queue and returns the drained messages. The filter allows you to drain + /// only messages that adhere to your filter. This might be useful in a scenario like draining + /// messages with a particular urgency requirement. + pub fn drain_messages(&mut self, mut filter: impl FnMut(&mut Message) -> bool) -> Vec { + let mut drained = Vec::with_capacity(self.messages.len()); + let mut i = 0; + while i != self.messages.len() { + if filter(&mut self.messages[i]) { + if let Some(m) = self.messages.remove(i) { + drained.push(m); + } + } else { + i += 1; + } + } + drained + } +} + +impl Default for TransportResource { + fn default() -> Self { + Self { + messages: VecDeque::new(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_send_with_default_requirements() { + let mut resource = create_test_resource(); + + resource.send("127.0.0.1:3000".parse().unwrap(), test_payload()); + + let packet = &resource.messages[0]; + + assert_eq!(resource.messages.len(), 1); + assert_eq!(packet.delivery, DeliveryRequirement::Default); + assert_eq!(packet.urgency, UrgencyRequirement::OnTick); + } + + #[test] + fn test_send_immediate_message() { + let mut resource = create_test_resource(); + + resource.send_immediate("127.0.0.1:3000".parse().unwrap(), test_payload()); + + let packet = &resource.messages[0]; + + assert_eq!(resource.messages.len(), 1); + assert_eq!(packet.delivery, DeliveryRequirement::Default); + assert_eq!(packet.urgency, UrgencyRequirement::Immediate); + } + + #[test] + fn test_has_messages() { + let mut resource = create_test_resource(); + assert_eq!(resource.has_messages(), false); + resource.send_immediate("127.0.0.1:3000".parse().unwrap(), test_payload()); + assert_eq!(resource.has_messages(), true); + } + + #[test] + fn test_drain_only_immediate_messages() { + let mut resource = create_test_resource(); + + let addr = "127.0.0.1:3000".parse().unwrap(); + resource.send_immediate(addr, test_payload()); + resource.send_immediate(addr, test_payload()); + resource.send(addr, test_payload()); + resource.send(addr, test_payload()); + resource.send_immediate(addr, test_payload()); + + assert_eq!(resource.drain_messages_to_send(|_| false).len(), 3); + assert_eq!(resource.drain_messages_to_send(|_| false).len(), 0); + } + + #[test] + fn test_drain_only_messages_with_specific_requirements() { + let mut resource = create_test_resource(); + + let addr = "127.0.0.1:3000".parse().unwrap(); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::Unreliable, + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::Reliable, + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::ReliableOrdered(None), + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::ReliableSequenced(None), + UrgencyRequirement::OnTick, + ); + resource.send_with_requirements( + addr, + test_payload(), + DeliveryRequirement::Unreliable, + UrgencyRequirement::OnTick, + ); + + assert_eq!( + resource + .drain_messages(|message| message.delivery == DeliveryRequirement::Unreliable) + .len(), + 2 + ); + // validate removal + assert_eq!( + resource + .drain_messages(|message| message.delivery == DeliveryRequirement::Unreliable) + .len(), + 0 + ); + } + + #[test] + fn test_send_with_requirements() { + use DeliveryRequirement::*; + let mut resource = create_test_resource(); + let addr = "127.0.0.1:3000".parse().unwrap(); + + let requirements = [ + Unreliable, + UnreliableSequenced(None), + Reliable, + ReliableSequenced(None), + ReliableOrdered(None), + ]; + + for req in requirements.iter().cloned() { + resource.send_with_requirements(addr, test_payload(), req, UrgencyRequirement::OnTick); + } + + assert_eq!(resource.messages.len(), requirements.len()); + + for (i, req) in requirements.iter().enumerate() { + assert_eq!(resource.messages[i].delivery, *req); + } + } + + fn test_payload() -> &'static [u8] { + b"test" + } + + fn create_test_resource() -> TransportResource { + TransportResource::new() + } +} diff --git a/amethyst_network/src/simulation/transport/resource.rs b/amethyst_network/src/simulation/transport/resource.rs deleted file mode 100644 index 11208525c9..0000000000 --- a/amethyst_network/src/simulation/transport/resource.rs +++ /dev/null @@ -1,239 +0,0 @@ -use crate::simulation::{ - message::Message, - requirements::{DeliveryRequirement, UrgencyRequirement}, -}; -use std::{collections::VecDeque, net::SocketAddr}; - -/// Resource serving as the owner of the queue of messages to be sent. This resource also serves -/// as the interface for other systems to send messages. -pub struct TransportResource { - messages: VecDeque, -} - -impl TransportResource { - /// Creates a new `TransportResource`. - pub fn new() -> Self { - Self { - messages: VecDeque::new(), - } - } - - /// Creates a `Message` with the default guarantees provided by the `Socket` implementation and - /// pushes it onto the messages queue to be sent on next sim tick. - pub fn send(&mut self, destination: SocketAddr, payload: &[u8]) { - self.send_with_requirements( - destination, - payload, - DeliveryRequirement::Default, - UrgencyRequirement::OnTick, - ); - } - - /// Creates a `Message` with the default guarantees provided by the `Socket` implementation and - /// Pushes it onto the messages queue to be sent immediately. - pub fn send_immediate(&mut self, destination: SocketAddr, payload: &[u8]) { - self.send_with_requirements( - destination, - payload, - DeliveryRequirement::Default, - UrgencyRequirement::Immediate, - ); - } - - /// Creates and queue a `Message` with the specified guarantee. - pub fn send_with_requirements( - &mut self, - destination: SocketAddr, - payload: &[u8], - delivery: DeliveryRequirement, - timing: UrgencyRequirement, - ) { - let message = Message::new(destination, payload, delivery, timing); - self.messages.push_back(message); - } - - /// Returns true if there are messages enqueued to be sent. - pub fn has_messages(&self) -> bool { - !self.messages.is_empty() - } - - /// Returns a reference to the owned messages. - pub fn get_messages(&self) -> &VecDeque { - &self.messages - } - - /// Returns the messages to send by returning the immediate messages or anything adhering to - /// the given filter. - pub fn drain_messages_to_send( - &mut self, - mut filter: impl FnMut(&mut Message) -> bool, - ) -> Vec { - self.drain_messages(|message| { - message.urgency == UrgencyRequirement::Immediate || filter(message) - }) - } - - /// Drains the messages queue and returns the drained messages. The filter allows you to drain - /// only messages that adhere to your filter. This might be useful in a scenario like draining - /// messages with a particular urgency requirement. - pub fn drain_messages(&mut self, mut filter: impl FnMut(&mut Message) -> bool) -> Vec { - let mut drained = Vec::with_capacity(self.messages.len()); - let mut i = 0; - while i != self.messages.len() { - if filter(&mut self.messages[i]) { - if let Some(m) = self.messages.remove(i) { - drained.push(m); - } - } else { - i += 1; - } - } - drained - } -} - -impl Default for TransportResource { - fn default() -> Self { - Self { - messages: VecDeque::new(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_send_with_default_requirements() { - let mut resource = create_test_resource(); - - resource.send("127.0.0.1:3000".parse().unwrap(), test_payload()); - - let packet = &resource.messages[0]; - - assert_eq!(resource.messages.len(), 1); - assert_eq!(packet.delivery, DeliveryRequirement::Default); - assert_eq!(packet.urgency, UrgencyRequirement::OnTick); - } - - #[test] - fn test_send_immediate_message() { - let mut resource = create_test_resource(); - - resource.send_immediate("127.0.0.1:3000".parse().unwrap(), test_payload()); - - let packet = &resource.messages[0]; - - assert_eq!(resource.messages.len(), 1); - assert_eq!(packet.delivery, DeliveryRequirement::Default); - assert_eq!(packet.urgency, UrgencyRequirement::Immediate); - } - - #[test] - fn test_has_messages() { - let mut resource = create_test_resource(); - assert_eq!(resource.has_messages(), false); - resource.send_immediate("127.0.0.1:3000".parse().unwrap(), test_payload()); - assert_eq!(resource.has_messages(), true); - } - - #[test] - fn test_drain_only_immediate_messages() { - let mut resource = create_test_resource(); - - let addr = "127.0.0.1:3000".parse().unwrap(); - resource.send_immediate(addr, test_payload()); - resource.send_immediate(addr, test_payload()); - resource.send(addr, test_payload()); - resource.send(addr, test_payload()); - resource.send_immediate(addr, test_payload()); - - assert_eq!(resource.drain_messages_to_send(|_| false).len(), 3); - assert_eq!(resource.drain_messages_to_send(|_| false).len(), 0); - } - - #[test] - fn test_drain_only_messages_with_specific_requirements() { - let mut resource = create_test_resource(); - - let addr = "127.0.0.1:3000".parse().unwrap(); - resource.send_with_requirements( - addr, - test_payload(), - DeliveryRequirement::Unreliable, - UrgencyRequirement::OnTick, - ); - resource.send_with_requirements( - addr, - test_payload(), - DeliveryRequirement::Reliable, - UrgencyRequirement::OnTick, - ); - resource.send_with_requirements( - addr, - test_payload(), - DeliveryRequirement::ReliableOrdered(None), - UrgencyRequirement::OnTick, - ); - resource.send_with_requirements( - addr, - test_payload(), - DeliveryRequirement::ReliableSequenced(None), - UrgencyRequirement::OnTick, - ); - resource.send_with_requirements( - addr, - test_payload(), - DeliveryRequirement::Unreliable, - UrgencyRequirement::OnTick, - ); - - assert_eq!( - resource - .drain_messages(|message| message.delivery == DeliveryRequirement::Unreliable) - .len(), - 2 - ); - // validate removal - assert_eq!( - resource - .drain_messages(|message| message.delivery == DeliveryRequirement::Unreliable) - .len(), - 0 - ); - } - - #[test] - fn test_send_with_requirements() { - use DeliveryRequirement::*; - let mut resource = create_test_resource(); - let addr = "127.0.0.1:3000".parse().unwrap(); - - let requirements = [ - Unreliable, - UnreliableSequenced(None), - Reliable, - ReliableSequenced(None), - ReliableOrdered(None), - ]; - - for req in requirements.iter().cloned() { - resource.send_with_requirements(addr, test_payload(), req, UrgencyRequirement::OnTick); - } - - assert_eq!(resource.messages.len(), requirements.len()); - - for (i, req) in requirements.iter().enumerate() { - assert_eq!(resource.messages[i].delivery, *req); - } - } - - fn test_payload() -> &'static [u8] { - b"test" - } - - fn create_test_resource() -> TransportResource { - TransportResource::new() - } -} From a01ae8503e7f556ebd9b61ce1ec0a446df9a3f6d Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Wed, 2 Oct 2019 22:26:47 -0400 Subject: [PATCH 14/22] add the new state transport metrics. These will be set in a future commit for each implemented backend --- amethyst_network/src/simulation/transport.rs | 44 ++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index 6acdfc8b0f..18902f1216 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -22,6 +22,9 @@ use std::{collections::VecDeque, net::SocketAddr}; /// as the interface for other systems to send messages. pub struct TransportResource { messages: VecDeque, + frame_budget_bytes: i32, + latency_nanos: i64, + packet_loss: f32 } impl TransportResource { @@ -29,9 +32,47 @@ impl TransportResource { pub fn new() -> Self { Self { messages: VecDeque::new(), + frame_budget_bytes: 0, + latency_nanos: 0, + packet_loss: 0.0 } } + /// Returns estimated number of bytes you can reliably send this frame. + pub fn frame_budget_bytes(&self) -> i32 { + self.frame_budget_bytes + } + + /// Sets the frame budget in bytes. This should be called by a transport implementation. + pub fn set_frame_budget_bytes(&mut self, budget: i32) { + self.frame_budget_bytes = budget; + } + + /// Returns the estimated microsecond round-trip latency for messages. + pub fn latency_micros(&mut self) -> i64 { + self.latency_nanos / 1000 + } + + /// Returns the estimated nanosecond round-trip latency for messages. + pub fn latency_nanos(&self) -> i64 { + self.latency_nanos + } + + /// Sets the latency value. This should be called by a transport implementation. + pub fn set_latency_nanos(&mut self, latency: i64) { + self.latency_nanos = latency; + } + + /// Returns the estimated loss percentage of packets in 0.0-1.0. + pub fn packet_loss(&self) -> f32 { + self.packet_loss + } + + /// Sets the packet loss value. This should be called by a transport implementation. + pub fn set_packet_loss(&mut self, loss: f32) { + self.packet_loss = loss; + } + /// Creates a `Message` with the default guarantees provided by the `Socket` implementation and /// pushes it onto the messages queue to be sent on next sim tick. pub fn send(&mut self, destination: SocketAddr, payload: &[u8]) { @@ -110,6 +151,9 @@ impl Default for TransportResource { fn default() -> Self { Self { messages: VecDeque::new(), + frame_budget_bytes: 0, + latency_nanos: 0, + packet_loss: 0.0 } } } From 61b695da5d9c6d68f50e473465e17338153cdd8c Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Thu, 3 Oct 2019 22:39:24 -0400 Subject: [PATCH 15/22] remove the builder pattern methods on NetworkSimulationTime and replace them with set methods --- amethyst_network/src/simulation/timing.rs | 12 ++++++------ amethyst_network/src/simulation/transport.rs | 7 +++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/amethyst_network/src/simulation/timing.rs b/amethyst_network/src/simulation/timing.rs index 5d5683e8c3..8f3e641795 100644 --- a/amethyst_network/src/simulation/timing.rs +++ b/amethyst_network/src/simulation/timing.rs @@ -110,15 +110,13 @@ impl NetworkSimulationTime { } /// Set the rate at which the network simulation progresses. Specified in hertz (frames/second). - pub fn with_sim_frame_rate(mut self, new_rate: u32) -> Self { + pub fn set_sim_frame_rate(&mut self, new_rate: u32) { self.per_frame_duration = Duration::from_secs(1) / new_rate; - self } /// Set the rate which messages are sent. Specified as "every N frames" where N is new_rate. - pub fn with_message_send_rate(mut self, new_rate: u8) -> Self { + pub fn set_message_send_rate(&mut self, new_rate: u8) { self.message_send_rate = new_rate; - self } } @@ -144,7 +142,8 @@ mod tests { #[test] fn test_calculated_properties_and_getters() { - let time = NetworkSimulationTime::default().with_sim_frame_rate(20); + let mut time = NetworkSimulationTime::default(); + time.set_sim_frame_rate(20); assert_eq!(time.frame_number(), 0); assert_eq!(time.frame_lag(), 1); assert_eq!(time.message_send_rate(), 1); @@ -154,7 +153,8 @@ mod tests { #[test] fn test_message_send_rate_should_send_every_2_frames() { - let time = NetworkSimulationTime::default().with_message_send_rate(2); + let mut time = NetworkSimulationTime::default(); + time.set_message_send_rate(2); for i in 1..100 { // every second frame (even) should return true diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index 18902f1216..833f98031e 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -17,14 +17,13 @@ use crate::simulation::{ }; use std::{collections::VecDeque, net::SocketAddr}; - /// Resource serving as the owner of the queue of messages to be sent. This resource also serves /// as the interface for other systems to send messages. pub struct TransportResource { messages: VecDeque, frame_budget_bytes: i32, latency_nanos: i64, - packet_loss: f32 + packet_loss: f32, } impl TransportResource { @@ -34,7 +33,7 @@ impl TransportResource { messages: VecDeque::new(), frame_budget_bytes: 0, latency_nanos: 0, - packet_loss: 0.0 + packet_loss: 0.0, } } @@ -153,7 +152,7 @@ impl Default for TransportResource { messages: VecDeque::new(), frame_budget_bytes: 0, latency_nanos: 0, - packet_loss: 0.0 + packet_loss: 0.0, } } } From f35c7169aa041954fdc55d47d257f0bf662393ac Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Thu, 3 Oct 2019 23:01:35 -0400 Subject: [PATCH 16/22] add the latency_millis convenience method --- amethyst_network/src/simulation/transport.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index 833f98031e..f95ba2cc6c 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -47,6 +47,11 @@ impl TransportResource { self.frame_budget_bytes = budget; } + /// Returns the estimated millisecond round-trip latency for messages. + pub fn latency_millis(&mut self) -> i64 { + self.latency_nanos / 1_000_000 + } + /// Returns the estimated microsecond round-trip latency for messages. pub fn latency_micros(&mut self) -> i64 { self.latency_nanos / 1000 From 8c762ceb12bdbc807bf34687dc4bdfedd2d1b297 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Sat, 5 Oct 2019 15:39:48 -0400 Subject: [PATCH 17/22] re-export the bytes library --- amethyst_network/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/amethyst_network/src/lib.rs b/amethyst_network/src/lib.rs index 3efcb2e06f..f1a8d78722 100644 --- a/amethyst_network/src/lib.rs +++ b/amethyst_network/src/lib.rs @@ -3,3 +3,4 @@ //! modules. Soon, we will also provide an HTTP client library. pub mod simulation; +pub use bytes::*; From 6be05d07907e66277fc070e6f6dfb8b28899d187 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Sat, 5 Oct 2019 16:15:23 -0400 Subject: [PATCH 18/22] run clippy and fix errors --- .../src/simulation/transport/laminar.rs | 14 +++++++------- amethyst_network/src/simulation/transport/tcp.rs | 4 ++-- amethyst_network/src/simulation/transport/udp.rs | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs index 384ff5852b..08876d8a0d 100644 --- a/amethyst_network/src/simulation/transport/laminar.rs +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -70,7 +70,7 @@ impl<'s> System<'s> for LaminarNetworkSendSystem { ); fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { - socket.get_mut().map(|socket| { + if let Some(socket) = socket.get_mut() { let messages = transport.drain_messages_to_send(|_| sim_time.should_send_message_now()); for message in messages.iter() { @@ -111,7 +111,7 @@ impl<'s> System<'s> for LaminarNetworkSendSystem { error!("There was an error when attempting to send packet: {:?}", e); } } - }); + } } } @@ -121,9 +121,9 @@ impl<'s> System<'s> for LaminarNetworkPollSystem { type SystemData = Write<'s, LaminarSocketResource>; fn run(&mut self, mut socket: Self::SystemData) { - socket - .get_mut() - .map(|socket| socket.manual_poll(Instant::now())); + if let Some(socket) = socket.get_mut() { + socket.manual_poll(Instant::now()); + } } } @@ -136,7 +136,7 @@ impl<'s> System<'s> for LaminarNetworkRecvSystem { ); fn run(&mut self, (mut socket, mut event_channel): Self::SystemData) { - socket.get_mut().map(|socket| { + if let Some(socket) = socket.get_mut() { while let Some(event) = socket.recv() { let event = match event { SocketEvent::Packet(packet) => NetworkSimulationEvent::Message( @@ -148,7 +148,7 @@ impl<'s> System<'s> for LaminarNetworkRecvSystem { }; event_channel.single_write(event); } - }); + } } } diff --git a/amethyst_network/src/simulation/transport/tcp.rs b/amethyst_network/src/simulation/transport/tcp.rs index 6d17cdff5a..4ffa8a23c1 100644 --- a/amethyst_network/src/simulation/transport/tcp.rs +++ b/amethyst_network/src/simulation/transport/tcp.rs @@ -183,14 +183,14 @@ impl<'s> System<'s> for TcpNetworkSendSystem { } fn write_message(message: &Message, net: &mut TcpNetworkResource) { - net.get_stream(message.destination).map(|(_, stream)| { + if let Some((_, stream)) = net.get_stream(message.destination) { if let Err(e) = stream.write(&message.payload) { error!( "There was an error when attempting to send message: {:?}", e ); } - }); + } } /// System to receive messages from all open `TcpStream`s. diff --git a/amethyst_network/src/simulation/transport/udp.rs b/amethyst_network/src/simulation/transport/udp.rs index 3ff37d6c90..5255c44ee0 100644 --- a/amethyst_network/src/simulation/transport/udp.rs +++ b/amethyst_network/src/simulation/transport/udp.rs @@ -66,7 +66,7 @@ impl<'s> System<'s> for UdpNetworkSendSystem { ); fn run(&mut self, (mut transport, mut socket, sim_time): Self::SystemData) { - socket.get_mut().map(|socket| { + if let Some(socket) = socket.get_mut() { let messages = transport.drain_messages_to_send(|_| sim_time.should_send_message_now()); for message in messages.iter() { match message.delivery { @@ -81,7 +81,7 @@ impl<'s> System<'s> for UdpNetworkSendSystem { ), } } - }); + } } } @@ -105,7 +105,7 @@ impl<'s> System<'s> for UdpNetworkRecvSystem { ); fn run(&mut self, (mut socket, mut event_channel): Self::SystemData) { - socket.get_mut().map(|socket| { + if let Some(socket) = socket.get_mut() { loop { match socket.recv_from(&mut self.recv_buffer) { Ok((recv_len, address)) => { @@ -124,7 +124,7 @@ impl<'s> System<'s> for UdpNetworkRecvSystem { } } } - }); + } } } From b70f3a573cee690ca9ad9c3ccd97f050f5eec98b Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Sun, 6 Oct 2019 13:37:46 -0400 Subject: [PATCH 19/22] Apply suggestions from code review Co-Authored-By: Timon --- amethyst_network/src/simulation/timing.rs | 6 +++--- amethyst_network/src/simulation/transport/laminar.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/amethyst_network/src/simulation/timing.rs b/amethyst_network/src/simulation/timing.rs index 8f3e641795..4816aa96dc 100644 --- a/amethyst_network/src/simulation/timing.rs +++ b/amethyst_network/src/simulation/timing.rs @@ -70,7 +70,7 @@ impl NetworkSimulationTime { self.frame_lag = 0; } - /// Increase the `elapsed_duration` by the given duration + /// Increases the `elapsed_duration` by the given duration pub fn update_elapsed(&mut self, duration: Duration) { self.elapsed_duration += duration; } @@ -109,12 +109,12 @@ impl NetworkSimulationTime { self.frame_lag } - /// Set the rate at which the network simulation progresses. Specified in hertz (frames/second). + /// Sets the rate at which the network simulation progresses. Specified in hertz (frames/second). pub fn set_sim_frame_rate(&mut self, new_rate: u32) { self.per_frame_duration = Duration::from_secs(1) / new_rate; } - /// Set the rate which messages are sent. Specified as "every N frames" where N is new_rate. + /// Set the rate which messages are sent. Specified as 'every N frames' where N is new_rate. pub fn set_message_send_rate(&mut self, new_rate: u8) { self.message_send_rate = new_rate; } diff --git a/amethyst_network/src/simulation/transport/laminar.rs b/amethyst_network/src/simulation/transport/laminar.rs index 08876d8a0d..cea36d2c4a 100644 --- a/amethyst_network/src/simulation/transport/laminar.rs +++ b/amethyst_network/src/simulation/transport/laminar.rs @@ -164,7 +164,7 @@ impl Default for LaminarSocketResource { } impl LaminarSocketResource { - /// Creates a new instance of the `UdpSocketResource` + /// Creates a new instance of the `UdpSocketResource`. pub fn new(socket: Option) -> Self { Self { socket } } From ddafa6b91c33c43063383a64940d60f6f4b99cc5 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Sun, 6 Oct 2019 13:54:39 -0400 Subject: [PATCH 20/22] add a changelog entry --- docs/CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 608fdeecbd..6ea8277353 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,9 +13,8 @@ The format is based on [Keep a Changelog][kc], and this project adheres to ### Added ### Changed - * Use a premultiplied view_proj matrix in vertex shaders. ([#1964]) - +* amethyst_network completely rewritten to provide a new baseline with which to build ### Deprecated ### Removed From 8bbffdc3eafe932c7b65e1b170b348e212648002 Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Sun, 6 Oct 2019 15:19:12 -0400 Subject: [PATCH 21/22] add the swappable network bundles to the example --- examples/net_client/main.rs | 19 ++++++++++++++++--- examples/net_server/main.rs | 32 ++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index 7b4aa362dc..b72597c1f7 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -11,18 +11,31 @@ use amethyst::{ Result, }; use log::info; -use std::net::TcpListener; +use std::net::{TcpListener, UdpSocket}; use std::time::Duration; +use amethyst_network::simulation::udp::UdpNetworkBundle; fn main() -> Result<()> { amethyst::start_logger(Default::default()); let assets_dir = application_root_dir()?.join("./"); - // let socket = LaminarSocket::bind("0.0.0.0:3457").unwrap(); +// // UDP +// let socket = UdpSocket::bind("0.0.0.0:3455")?; +// socket.set_nonblocking(true)?; + +// // TCP: No listener needed for the client. + +// // Laminar +// let socket = LaminarSocket::bind("0.0.0.0:3455")?; let game_data = GameDataBuilder::default() - .with_bundle(TcpNetworkBundle::new(None, 1500))? +// // UDP +// .with_bundle(UdpNetworkBundle::new(Some(socket), 2048))? + // TCP + .with_bundle(TcpNetworkBundle::new(None, 2048))? +// // Laminar +// .with_bundle(LaminarNetworkBundle::new(Some(socket)))? .with(SpamSystem::new(), "spam", &[]); let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( diff --git a/examples/net_server/main.rs b/examples/net_server/main.rs index 32d1313c4e..b63590913f 100644 --- a/examples/net_server/main.rs +++ b/examples/net_server/main.rs @@ -4,8 +4,9 @@ use amethyst::{ core::{bundle::SystemBundle, frame_limiter::FrameRateLimitStrategy, SystemDesc}, ecs::{DispatcherBuilder, Read, System, SystemData, World, Write}, network::simulation::{ - // laminar::{LaminarNetworkBundle, LaminarSocket}, + laminar::{LaminarNetworkBundle, LaminarSocket}, tcp::TcpNetworkBundle, + udp::UdpNetworkBundle, DeliveryRequirement, NetworkSimulationEvent, TransportResource, @@ -17,28 +18,31 @@ use amethyst::{ Result, }; use log::info; -use std::net::TcpListener; +use std::net::{TcpListener, UdpSocket}; fn main() -> Result<()> { amethyst::start_logger(Default::default()); +// // UDP +// let socket = UdpSocket::bind("0.0.0.0:3457")?; +// socket.set_nonblocking(true)?; + + // TCP let listener = TcpListener::bind("0.0.0.0:3457")?; - listener.set_nonblocking(true); + listener.set_nonblocking(true)?; - let assets_dir = application_root_dir()?.join("./"); +// // Laminar +// let socket = LaminarSocket::bind("0.0.0.0:3457")?; - // XXX: This is gross. We really need a handshake in laminar. Reliable delivery will not work - // unless you send an unreliable message first and begin the client BEFORE the 5 second disconnect - // timer. - // net.send_with_requirements( - // "127.0.0.1:3455".parse().unwrap(), - // b"", - // DeliveryRequirement::Unreliable, - // UrgencyRequirement::Immediate, - // ); + let assets_dir = application_root_dir()?.join("./"); let game_data = GameDataBuilder::default() - .with_bundle(TcpNetworkBundle::new(Some(listener), 1500))? +// // UDP +// .with_bundle(UdpNetworkBundle::new(Some(socket), 2048))? + // TCP + .with_bundle(TcpNetworkBundle::new(Some(listener), 2048))? +// // Laminar +// .with_bundle(LaminarNetworkBundle::new(Some(socket)))? .with_bundle(SpamReceiveBundle)?; let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( From 2283313def9800106bd460c234529ff815571d6b Mon Sep 17 00:00:00 2001 From: Justin LeFebvre Date: Sun, 6 Oct 2019 15:29:26 -0400 Subject: [PATCH 22/22] fix changelog merge comment --- amethyst_network/src/simulation/transport.rs | 1 + docs/CHANGELOG.md | 2 +- examples/net_client/main.rs | 28 ++++++++++---------- examples/net_server/main.rs | 26 +++++++++--------- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/amethyst_network/src/simulation/transport.rs b/amethyst_network/src/simulation/transport.rs index f95ba2cc6c..8465c2d702 100644 --- a/amethyst_network/src/simulation/transport.rs +++ b/amethyst_network/src/simulation/transport.rs @@ -278,6 +278,7 @@ mod tests { Reliable, ReliableSequenced(None), ReliableOrdered(None), + Default, ]; for req in requirements.iter().cloned() { diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6ea8277353..d3f8bbafcb 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,7 +14,7 @@ The format is based on [Keep a Changelog][kc], and this project adheres to ### Changed * Use a premultiplied view_proj matrix in vertex shaders. ([#1964]) -* amethyst_network completely rewritten to provide a new baseline with which to build +* amethyst_network completely rewritten to provide a new baseline with which to build. ([#1917]) ### Deprecated ### Removed diff --git a/examples/net_client/main.rs b/examples/net_client/main.rs index b72597c1f7..840d014585 100644 --- a/examples/net_client/main.rs +++ b/examples/net_client/main.rs @@ -2,40 +2,40 @@ use amethyst::{ core::{frame_limiter::FrameRateLimitStrategy, Time}, ecs::{Read, System, Write}, network::simulation::{ - laminar::{LaminarNetworkBundle, LaminarSocket}, + // laminar::{LaminarNetworkBundle, LaminarSocket}, tcp::TcpNetworkBundle, - NetworkSimulationTime, TransportResource, + // udp::UdpNetworkBundle, + NetworkSimulationTime, + TransportResource, }, prelude::*, utils::application_root_dir, Result, }; use log::info; -use std::net::{TcpListener, UdpSocket}; use std::time::Duration; -use amethyst_network::simulation::udp::UdpNetworkBundle; fn main() -> Result<()> { amethyst::start_logger(Default::default()); let assets_dir = application_root_dir()?.join("./"); -// // UDP -// let socket = UdpSocket::bind("0.0.0.0:3455")?; -// socket.set_nonblocking(true)?; + // // UDP + // let socket = UdpSocket::bind("0.0.0.0:3455")?; + // socket.set_nonblocking(true)?; -// // TCP: No listener needed for the client. + // // TCP: No listener needed for the client. -// // Laminar -// let socket = LaminarSocket::bind("0.0.0.0:3455")?; + // // Laminar + // let socket = LaminarSocket::bind("0.0.0.0:3455")?; let game_data = GameDataBuilder::default() -// // UDP -// .with_bundle(UdpNetworkBundle::new(Some(socket), 2048))? + // // UDP + // .with_bundle(UdpNetworkBundle::new(Some(socket), 2048))? // TCP .with_bundle(TcpNetworkBundle::new(None, 2048))? -// // Laminar -// .with_bundle(LaminarNetworkBundle::new(Some(socket)))? + // // Laminar + // .with_bundle(LaminarNetworkBundle::new(Some(socket)))? .with(SpamSystem::new(), "spam", &[]); let mut game = Application::build(assets_dir, GameState)? .with_frame_limit( diff --git a/examples/net_server/main.rs b/examples/net_server/main.rs index b63590913f..fa6d5fb254 100644 --- a/examples/net_server/main.rs +++ b/examples/net_server/main.rs @@ -4,13 +4,11 @@ use amethyst::{ core::{bundle::SystemBundle, frame_limiter::FrameRateLimitStrategy, SystemDesc}, ecs::{DispatcherBuilder, Read, System, SystemData, World, Write}, network::simulation::{ - laminar::{LaminarNetworkBundle, LaminarSocket}, + // laminar::{LaminarNetworkBundle, LaminarSocket}, tcp::TcpNetworkBundle, - udp::UdpNetworkBundle, - DeliveryRequirement, + // udp::UdpNetworkBundle, NetworkSimulationEvent, TransportResource, - UrgencyRequirement, }, prelude::*, shrev::{EventChannel, ReaderId}, @@ -18,31 +16,31 @@ use amethyst::{ Result, }; use log::info; -use std::net::{TcpListener, UdpSocket}; +use std::net::TcpListener; fn main() -> Result<()> { amethyst::start_logger(Default::default()); -// // UDP -// let socket = UdpSocket::bind("0.0.0.0:3457")?; -// socket.set_nonblocking(true)?; + // // UDP + // let socket = UdpSocket::bind("0.0.0.0:3457")?; + // socket.set_nonblocking(true)?; // TCP let listener = TcpListener::bind("0.0.0.0:3457")?; listener.set_nonblocking(true)?; -// // Laminar -// let socket = LaminarSocket::bind("0.0.0.0:3457")?; + // // Laminar + // let socket = LaminarSocket::bind("0.0.0.0:3457")?; let assets_dir = application_root_dir()?.join("./"); let game_data = GameDataBuilder::default() -// // UDP -// .with_bundle(UdpNetworkBundle::new(Some(socket), 2048))? + // // UDP + // .with_bundle(UdpNetworkBundle::new(Some(socket), 2048))? // TCP .with_bundle(TcpNetworkBundle::new(Some(listener), 2048))? -// // Laminar -// .with_bundle(LaminarNetworkBundle::new(Some(socket)))? + // // Laminar + // .with_bundle(LaminarNetworkBundle::new(Some(socket)))? .with_bundle(SpamReceiveBundle)?; let mut game = Application::build(assets_dir, GameState)? .with_frame_limit(