Example #1
0
        /// <summary>
        /// Sends a message, but notify when it is delivered or lost
        /// </summary>
        /// <typeparam name="T">type of message to send</typeparam>
        /// <param name="message">message to send</param>
        /// <param name="token">a arbitrary object that the sender will receive with their notification</param>
        public void SendNotify <T>(T message, object token, int channelId = Channel.Unreliable)
        {
            if (sendWindow.Count == WINDOW_SIZE)
            {
                NotifyLost?.Invoke(this, token);
                return;
            }

            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                var notifyPacket = new NotifyPacket
                {
                    Sequence        = (ushort)sequencer.Next(),
                    ReceiveSequence = receiveSequence,
                    AckMask         = receiveMask
                };

                sendWindow.Enqueue(new PacketEnvelope
                {
                    Sequence = notifyPacket.Sequence,
                    Token    = token
                });

                MessagePacker.Pack(notifyPacket, writer);
                MessagePacker.Pack(message, writer);
                NetworkDiagnostics.OnSend(message, channelId, writer.Length, 1);
                Send(writer.ToArraySegment(), channelId);
                lastNotifySentTime = Time.unscaledTime;
            }
        }
Example #2
0
        /// <summary>
        /// Send a message to all connected clients.
        /// </summary>
        /// <typeparam name="T">Message type</typeparam>
        /// <param name="msg">Message</param>
        /// <param name="channelId">Transport channel to use</param>
        public void SendToAll <T>(T msg, int channelId = Channel.Reliable)
        {
            if (logger.LogEnabled())
            {
                logger.Log("Server.SendToAll id:" + typeof(T));
            }

            using (var writer = NetworkWriterPool.GetWriter())
            {
                // pack message into byte[] once
                MessagePacker.Pack(msg, writer);
                var segment = writer.ToArraySegment();
                var count   = 0;

                // using SendToMany (with IEnumerable) will cause Enumerator to be boxed and create GC/alloc
                // instead we can use while loop and MoveNext to avoid boxing
                var enumerator = _connections.Values.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    var player = enumerator.Current;
                    player.Send(segment, channelId);
                    count++;
                }
                enumerator.Dispose();

                NetworkDiagnostics.OnSend(msg, segment.Count, count);
            }
        }
Example #3
0
        private static NetworkMessageDelegate MessageHandler <T>(Action <INetworkPlayer, T> handler)
        {
            void AdapterFunction(INetworkPlayer player, NetworkReader reader, int channelId)
            {
                // protect against DOS attacks if attackers try to send invalid
                // data packets to crash the server/client. there are a thousand
                // ways to cause an exception in data handling:
                // - invalid headers
                // - invalid message ids
                // - invalid data causing exceptions
                // - negative ReadBytesAndSize prefixes
                // - invalid utf8 strings
                // - etc.
                //
                // let's catch them all and then disconnect that connection to avoid
                // further attacks.
                var message = default(T);

                try
                {
                    message = reader.Read <T>();
                }
                finally
                {
                    NetworkDiagnostics.OnReceive(message, channelId, reader.Length);
                }

                handler(player, message);
            }

            return(AdapterFunction);
        }
Example #4
0
 /// <summary>
 /// This sends a network message to the connection.
 /// </summary>
 /// <typeparam name="T">The message type</typeparam>
 /// <param name="msg">The message to send.</param>
 /// <param name="channelId">The transport layer channel to send on.</param>
 /// <returns></returns>
 public void Send <T>(T message, int channelId = Channel.Reliable)
 {
     using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
     {
         // pack message and send allocation free
         MessagePacker.Pack(message, writer);
         NetworkDiagnostics.OnSend(message, channelId, writer.Length, 1);
         Send(writer.ToArraySegment(), channelId);
     }
 }
Example #5
0
 /// <summary>
 /// This sends a network message to the connection. You can await it to check for errors
 /// </summary>
 /// <typeparam name="T">The message type</typeparam>
 /// <param name="msg">The message to send.</param>
 /// <param name="channelId">The transport layer channel to send on.</param>
 /// <returns></returns>
 public virtual UniTask SendAsync <T>(T msg, int channelId = Channel.Reliable)
 {
     using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
     {
         // pack message and send allocation free
         MessagePacker.Pack(msg, writer);
         NetworkDiagnostics.OnSend(msg, channelId, writer.Length, 1);
         return(SendAsync(writer.ToArraySegment(), channelId));
     }
 }
Example #6
0
        private static NetworkMessageDelegate MessageWrapper <T>(MessageDelegateWithPlayer <T> handler)
        {
            void AdapterFunction(INetworkPlayer player, NetworkReader reader)
            {
                T message = NetworkDiagnostics.ReadWithDiagnostics <T>(reader);

                handler.Invoke(player, message);
            }

            return(AdapterFunction);
        }
Example #7
0
        /// <summary>
        /// This sends a network message to the connection.
        /// </summary>
        /// <typeparam name="T">The message type</typeparam>
        /// <param name="msg">The message to send.</param>
        /// <param name="channelId">The transport layer channel to send on.</param>
        /// <returns></returns>
        public void Send <T>(T message, INotifyCallBack token)
        {
            if (isDisconnected)
            {
                return;
            }

            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                MessagePacker.Pack(message, writer);

                var segment = writer.ToArraySegment();
                NetworkDiagnostics.OnSend(message, segment.Count, 1);
                connection.SendNotify(segment, token);
            }
        }
Example #8
0
        /// <summary>
        /// This sends a network message to the connection.
        /// </summary>
        /// <typeparam name="T">The message type</typeparam>
        /// <param name="msg">The message to send.</param>
        /// <param name="channelId">The transport layer channel to send on.</param>
        /// <returns></returns>
        public void Send <T>(T message, int channelId = Channel.Reliable)
        {
            if (isDisconnected)
            {
                return;
            }

            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                MessagePacker.Pack(message, writer);

                var segment = writer.ToArraySegment();
                NetworkDiagnostics.OnSend(message, segment.Count, 1);
                Send(segment, channelId);
            }
        }
Example #9
0
        /// <summary>
        /// Sends a message to many connections
        /// <para>
        /// Same as <see cref="SendToMany{T}(IEnumerable{INetworkPlayer}, T, int)"/> but uses for loop to avoid allocations
        /// </para>
        /// </summary>
        /// <remarks>
        /// Using list in foreach loop causes Unity's mono version to box the struct which causes allocations, <see href="https://docs.unity3d.com/2019.4/Documentation/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html">Understanding the managed heap</see>
        /// </remarks>
        public static void SendToMany <T>(IReadOnlyList <INetworkPlayer> players, T msg, int channelId = Channel.Reliable)
        {
            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                // pack message into byte[] once
                MessagePacker.Pack(msg, writer);
                var segment = writer.ToArraySegment();
                int count   = players.Count;

                for (int i = 0; i < count; i++)
                {
                    players[i].Send(segment, channelId);
                }

                NetworkDiagnostics.OnSend(msg, segment.Count, count);
            }
        }
Example #10
0
        /// <summary>
        /// Sends a message to many connections
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="players"></param>
        /// <param name="msg"></param>
        /// <param name="channelId"></param>
        public static void SendToMany <T>(IEnumerable <INetworkPlayer> players, T msg, int channelId = Channel.Reliable)
        {
            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                // pack message into byte[] once
                MessagePacker.Pack(msg, writer);
                var segment = writer.ToArraySegment();
                int count   = 0;

                foreach (INetworkPlayer player in players)
                {
                    player.Send(segment, channelId);
                    count++;
                }

                NetworkDiagnostics.OnSend(msg, segment.Count, count);
            }
        }
Example #11
0
        public static void Send <T>(IEnumerable <INetworkConnection> connections, T msg, int channelId = Channel.Reliable)
        {
            using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
            {
                // pack message into byte[] once
                MessagePacker.Pack(msg, writer);
                var segment = writer.ToArraySegment();
                int count   = 0;

                foreach (INetworkConnection conn in connections)
                {
                    // send to all connections, but don't wait for them
                    conn.Send(segment, channelId);
                    count++;
                }

                NetworkDiagnostics.OnSend(msg, channelId, segment.Count, count);
            }
        }
Example #12
0
        /// <summary>
        /// Calls <see cref="Reader{T}.Read"/> and measures number of bytes read
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <returns></returns>
        internal static T ReadWithDiagnostics <T>(NetworkReader reader)
        {
            var message = default(T);

            // record start position for NetworkDiagnostics because reader might contain multiple messages if using batching
            int startPos = reader.BitPosition;

            try
            {
                message = reader.Read <T>();
            }
            finally
            {
                int endPos     = reader.BitPosition;
                int byteLength = (endPos - startPos) / 8;
                NetworkDiagnostics.OnReceive(message, byteLength);
            }

            return(message);
        }