internal override NetSendResult Enqueue(NetOutgoingMessage message)
 {
     m_queuedSends.Enqueue(message);
     m_connection.m_peer.m_needFlushSendQueue = true; // a race condition to this variable will simply result in a single superflous call to FlushSendQueue()
     if (m_queuedSends.Count <= GetAllowedSends())
         return NetSendResult.Sent;
     return NetSendResult.Queued;
 }
 /// <summary>
 /// Encrypt an outgoing message
 /// </summary>
 public override bool Encrypt(NetOutgoingMessage msg)
 {
     int numBytes = msg.LengthBytes;
     for (int i = 0; i < numBytes; i++)
     {
         int offset = i % m_key.Length;
         msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
     }
     return true;
 }
        /// <summary>
        /// Send a message to all connections
        /// </summary>
        /// <param name="msg">The message to send</param>
        /// <param name="method">How to deliver the message</param>
        public void SendToAll(NetOutgoingMessage msg, NetDeliveryMethod method)
        {
            var all = this.Connections;
            if (all.Count <= 0) {
                if (msg.m_isSent == false)
                    Recycle(msg);
                return;
            }

            SendMessage(msg, all, method, 0);
        }
        /// <summary>
        /// Approves this connection; sending a connection response to the remote host
        /// </summary>
        /// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param>
        public void Approve(NetOutgoingMessage localHail)
        {
            if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
            {
                m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
                return;
            }

            m_localHailMessage = localHail;
            m_handshakeAttempts = 0;
            SendConnectResponse(NetTime.Now, false);
        }
        /// <summary>
        /// Creates a new message for sending
        /// </summary>
        /// <param name="initialCapacity">initial capacity in bytes</param>
        public NetOutgoingMessage CreateMessage(int initialCapacity)
        {
            NetOutgoingMessage retval;
            if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
                retval = new NetOutgoingMessage();

            NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount);

            if (initialCapacity > 0)
                retval.m_data = GetStorage(initialCapacity);

            return retval;
        }
        /// <summary>
        /// Send a message to a list of connections
        /// </summary>
        /// <param name="msg">The message to send</param>
        /// <param name="recipients">The list of recipients to send to</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
        public void SendMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
        {
            if (msg == null)
                throw new ArgumentNullException("msg");
            if (recipients == null)
            {
                if (msg.m_isSent == false)
                    Recycle(msg);
                throw new ArgumentNullException("recipients");
            }
            if (recipients.Count < 1)
            {
                if (msg.m_isSent == false)
                    Recycle(msg);
                throw new NetException("recipients must contain at least one item");
            }
            if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
                NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
            if (msg.m_isSent)
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
            msg.m_isSent = true;

            int mtu = GetMTU(recipients);

            int len = msg.GetEncodedSize();
            if (len <= mtu)
            {
                Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
                foreach (NetConnection conn in recipients)
                {
                    if (conn == null)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                        continue;
                    }
                    NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
                    if (res == NetSendResult.Dropped)
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                }
            }
            else
            {
                // message must be fragmented!
                SendFragmentedMessage(msg, recipients, method, sequenceChannel);
            }

            return;
        }
        /// <summary>
        /// Send a discovery response message
        /// </summary>
        public void SendDiscoveryResponse(NetOutgoingMessage msg, NetEndPoint recipient)
        {
            if (recipient == null)
                throw new ArgumentNullException("recipient");

            if (msg == null)
                msg = CreateMessage(0);
            else if (msg.m_isSent)
                throw new NetException("Message has already been sent!");

            if (msg.LengthBytes >= m_configuration.MaximumTransmissionUnit)
                throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)");

            msg.m_messageType = NetMessageType.DiscoveryResponse;
            Interlocked.Increment(ref msg.m_recyclingCount);
            m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(recipient, msg));
        }
        public override bool Encrypt(NetOutgoingMessage msg)
        {
            int unEncLenBits = msg.LengthBits;

            var ms = new MemoryStream();
            var cs = GetEncryptStream(ms);
            cs.Write(msg.m_data, 0, msg.LengthBytes);
            cs.Close();

            // get results
            var arr = ms.ToArray();
            ms.Close();

            msg.EnsureBufferSize((arr.Length + 4) * 8);
            msg.LengthBits = 0; // reset write pointer
            msg.Write((uint)unEncLenBits);
            msg.Write(arr);
            msg.LengthBits = (arr.Length + 4) * 8;

            return true;
        }
        /// <summary>
        /// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
        /// </summary>
        public override bool Encrypt(NetOutgoingMessage msg)
        {
            int payloadBitLength = msg.LengthBits;
            int numBytes = msg.LengthBytes;
            int blockSize = BlockSize;
            int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize);
            int dstSize = numBlocks * blockSize;

            msg.EnsureBufferSize(dstSize * 8 + (4 * 8)); // add 4 bytes for payload length at end
            msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written

            for(int i=0;i<numBlocks;i++)
            {
                EncryptBlock(msg.m_data, (i * blockSize), m_tmp);
                Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
            }

            // add true payload length last
            msg.Write((UInt32)payloadBitLength);

            return true;
        }
        /// <summary>
        /// Send a message to all connections except one
        /// </summary>
        /// <param name="msg">The message to send</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="except">Don't send to this particular connection</param>
        /// <param name="sequenceChannel">Which sequence channel to use for the message</param>
        public void SendToAll(NetOutgoingMessage msg, NetConnection except, NetDeliveryMethod method, int sequenceChannel)
        {
            var all = this.Connections;
            if (all.Count <= 0) {
                if (msg.m_isSent == false)
                    Recycle(msg);
                return;
            }

            if (except == null)
            {
                SendMessage(msg, all, method, sequenceChannel);
                return;
            }

            List<NetConnection> recipients = new List<NetConnection>(all.Count - 1);
            foreach (var conn in all)
                if (conn != except)
                    recipients.Add(conn);

            if (recipients.Count > 0)
                SendMessage(msg, recipients, method, sequenceChannel);
        }
        /// <summary>
        /// Send a message to a specific connection
        /// </summary>
        /// <param name="msg">The message to send</param>
        /// <param name="recipient">The recipient connection</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
        public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel)
        {
            if (msg == null)
                throw new ArgumentNullException("msg");
            if (recipient == null)
                throw new ArgumentNullException("recipient");
            if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod)
                throw new ArgumentOutOfRangeException("sequenceChannel");

            NetException.Assert(
                ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) ||
                ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)),
                "Delivery method " + method + " cannot use sequence channels other than 0!"
            );

            NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!");

            if (msg.m_isSent)
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
            msg.m_isSent = true;

            bool suppressFragmentation = (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.UnreliableSequenced) && m_configuration.UnreliableSizeBehaviour != NetUnreliableSizeBehaviour.NormalFragmentation;

            int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize
            if (len <= recipient.m_currentMTU || suppressFragmentation)
            {
                Interlocked.Increment(ref msg.m_recyclingCount);
                return recipient.EnqueueMessage(msg, method, sequenceChannel);
            }
            else
            {
                // message must be fragmented!
                if (recipient.m_status != NetConnectionStatus.Connected)
                    return NetSendResult.FailedNotConnected;
                return SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
            }
        }
 /// <summary>
 /// Send a message to a specific connection
 /// </summary>
 /// <param name="msg">The message to send</param>
 /// <param name="recipient">The recipient connection</param>
 /// <param name="method">How to deliver the message</param>
 public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method)
 {
     return SendMessage(msg, recipient, method, 0);
 }
        // Queue an item for immediate sending on the wire
        // This method is called from the ISenderChannels
        internal void QueueSendMessage(NetOutgoingMessage om, int seqNr)
        {
            m_peer.VerifyNetworkThread();

            int sz = om.GetEncodedSize();
            //if (sz > m_currentMTU)
            //	m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");

            bool connReset; // TODO: handle connection reset

            // can fit this message together with previously written to buffer?
            if (m_sendBufferWritePtr + sz > m_currentMTU)
            {
                if (m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0)
                {
                    // previous message in buffer; send these first
                    m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
                    m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                    m_sendBufferWritePtr = 0;
                    m_sendBufferNumMessages = 0;
                }
            }

            // encode it into buffer regardless if it (now) fits within MTU or not
            m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr);
            m_sendBufferNumMessages++;

            if (m_sendBufferWritePtr > m_currentMTU)
            {
                // send immediately; we're already over MTU
                m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
                m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                m_sendBufferWritePtr = 0;
                m_sendBufferNumMessages = 0;
            }

            if (m_sendBufferWritePtr > 0)
                m_peer.m_needFlushSendQueue = true; // flush in heartbeat

            Interlocked.Decrement(ref om.m_recyclingCount);
        }
        /// <summary>
        /// Connect to a remote server
        /// </summary>
        /// <param name="remoteEndPoint">The remote endpoint to connect to</param>
        /// <param name="hailMessage">The hail message to pass</param>
        /// <returns>server connection, or null if already connected</returns>
        public override NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
        {
            lock (m_connections)
            {
                if (m_connections.Count > 0)
                {
                    LogWarning("Connect attempt failed; Already connected");
                    return null;
                }
            }

            lock (m_handshakes)
            {
                if (m_handshakes.Count > 0)
                {
                    LogWarning("Connect attempt failed; Handshake already in progress");
                    return null;
                }
            }

            return base.Connect(remoteEndPoint, hailMessage);
        }
 /// <summary>
 /// Send a message to this remote connection
 /// </summary>
 /// <param name="msg">The message to send</param>
 /// <param name="method">How to deliver the message</param>
 /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
 public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
 {
     return m_peer.SendMessage(msg, this, method, sequenceChannel);
 }
        // called by SendMessage() and NetPeer.SendMessage; ie. may be user thread
        internal NetSendResult EnqueueMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
        {
            if (m_status != NetConnectionStatus.Connected)
                return NetSendResult.FailedNotConnected;

            NetMessageType tp = (NetMessageType)((int)method + sequenceChannel);
            msg.m_messageType = tp;

            // TODO: do we need to make this more thread safe?
            int channelSlot = (int)method - 1 + sequenceChannel;
            NetSenderChannelBase chan = m_sendChannels[channelSlot];
            if (chan == null)
                chan = CreateSenderChannel(tp);

            if ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.UnreliableSequenced) && msg.GetEncodedSize() > m_currentMTU)
                m_peer.ThrowOrLog("Reliable message too large! Fragmentation failure?");

            var retval = chan.Enqueue(msg);
            //if (retval == NetSendResult.Sent && m_peerConfiguration.m_autoFlushSendQueue == false)
            //	retval = NetSendResult.Queued; // queued since we're not autoflushing
            return retval;
        }
 public void Reset()
 {
     NumSent = 0;
     LastSent = 0.0;
     Message = null;
 }
 /// <summary>
 /// Encrypt an outgoing message in place
 /// </summary>
 public abstract bool Encrypt(NetOutgoingMessage msg);
        internal void Recycle(NetOutgoingMessage msg)
        {
            if (m_outgoingMessagesPool == null)
                return;
            #if DEBUG
            NetException.Assert(m_outgoingMessagesPool.Contains(msg) == false, "Recyling already recycled outgoing message! Thread race?");
            if (msg.m_recyclingCount != 0)
                LogWarning("Wrong recycling count! should be zero; found " + msg.m_recyclingCount);
            #endif
            // setting m_recyclingCount to zero SHOULD be an unnecessary maneuver, if it's not zero something is wrong
            // however, in RELEASE, we'll just have to accept this and move on with life
            msg.m_recyclingCount = 0;

            byte[] storage = msg.m_data;
            msg.m_data = null;

            // message fragments cannot be recycled
            // TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them
            if (msg.m_fragmentGroup == 0)
                Recycle(storage);

            msg.Reset();
            if (m_outgoingMessagesPool.Count < m_maxCacheCount)
                m_outgoingMessagesPool.Enqueue(msg);
        }
        /// <summary>
        /// Sends message to server
        /// </summary>
        public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
        {
            NetConnection serverConnection = ServerConnection;
            if (serverConnection == null)
            {
                LogWarning("Cannot send message, no server connection!");
                Recycle(msg);
                return NetSendResult.FailedNotConnected;
            }

            return serverConnection.SendMessage(msg, method, sequenceChannel);
        }
        /// <summary>
        /// Send a message to this exact same netpeer (loopback)
        /// </summary>
        public void SendUnconnectedToSelf(NetOutgoingMessage om)
        {
            if (om == null)
                throw new ArgumentNullException("msg");
            if (om.m_isSent)
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");

            om.m_messageType = NetMessageType.Unconnected;
            om.m_isSent = true;

            if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false)
            {
                Interlocked.Decrement(ref om.m_recyclingCount);
                return; // dropping unconnected message since it's not enabled for receiving
            }

            // convert outgoing to incoming
            NetIncomingMessage im = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, om.LengthBytes);
            im.Write(om);
            im.m_isFragment = false;
            im.m_receiveTime = NetTime.Now;
            im.m_senderConnection = null;
            im.m_senderEndPoint = m_socket.LocalEndPoint as NetEndPoint;
            NetException.Assert(im.m_bitLength == om.LengthBits);

            // recycle outgoing message
            Recycle(om);

            ReleaseMessage(im);
        }
        private void ExecuteSend(double now, NetOutgoingMessage message)
        {
            int seqNr = m_sendStart;
            m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;

            // must increment recycle count here, since it's decremented in QueueSendMessage and we want to keep it for the future in case or resends
            // we will decrement once more in DestoreMessage for final recycling
            Interlocked.Increment(ref message.m_recyclingCount);

            m_connection.QueueSendMessage(message, seqNr);

            int storeIndex = seqNr % m_windowSize;
            NetException.Assert(m_storedMessages[storeIndex].Message == null);

            m_storedMessages[storeIndex].NumSent++;
            m_storedMessages[storeIndex].Message = message;
            m_storedMessages[storeIndex].LastSent = now;
            m_storedMessages[storeIndex].SequenceNumber = seqNr;
            m_anyStoredResends = true;

            return;
        }
        /// <summary>
        /// Send a message to an unconnected host
        /// </summary>
        public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<NetEndPoint> recipients)
        {
            if (msg == null)
                throw new ArgumentNullException("msg");
            if (recipients == null)
                throw new ArgumentNullException("recipients");
            if (recipients.Count < 1)
                throw new NetException("recipients must contain at least one item");
            if (msg.m_isSent)
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
            if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
                throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");

            msg.m_messageType = NetMessageType.Unconnected;
            msg.m_isSent = true;

            Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
            foreach (NetEndPoint ep in recipients)
                m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(ep, msg));
        }
 private void WriteLocalHail(NetOutgoingMessage om)
 {
     if (m_localHailMessage != null)
     {
         byte[] hi = m_localHailMessage.Data;
         if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
         {
             if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10)
                 m_peer.ThrowOrLog("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes));
             om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes);
         }
     }
 }
        // send message immediately and recycle it
        internal void SendLibrary(NetOutgoingMessage msg, NetEndPoint recipient)
        {
            VerifyNetworkThread();
            NetException.Assert(msg.m_isSent == false);

            bool connReset;
            int len = msg.Encode(m_sendBuffer, 0, 0);
            SendPacket(len, recipient, 1, out connReset);

            // no reliability, no multiple recipients - we can just recycle this message immediately
            msg.m_recyclingCount = 0;
            Recycle(msg);
        }
        /// <summary>
        /// Create a connection to a remote endpoint
        /// </summary>
        public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
        {
            if (remoteEndPoint == null)
                throw new ArgumentNullException("remoteEndPoint");

            lock (m_connections)
            {
                if (m_status == NetPeerStatus.NotRunning)
                    throw new NetException("Must call Start() first");

                if (m_connectionLookup.ContainsKey(remoteEndPoint))
                    throw new NetException("Already connected to that endpoint!");

                NetConnection hs;
                if (m_handshakes.TryGetValue(remoteEndPoint, out hs))
                {
                    // already trying to connect to that endpoint; make another try
                    switch (hs.m_status)
                    {
                        case NetConnectionStatus.InitiatedConnect:
                            // send another connect
                            hs.m_connectRequested = true;
                            break;
                        case NetConnectionStatus.RespondedConnect:
                            // send another response
                            hs.SendConnectResponse(NetTime.Now, false);
                            break;
                        default:
                            // weird
                            LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.m_status);
                            break;
                    }
                    return hs;
                }

                NetConnection conn = new NetConnection(this, remoteEndPoint);
                conn.m_status = NetConnectionStatus.InitiatedConnect;
                conn.m_localHailMessage = hailMessage;

                // handle on network thread
                conn.m_connectRequested = true;
                conn.m_connectionInitiator = true;

                m_handshakes.Add(remoteEndPoint, conn);

                return conn;
            }
        }
 /// <summary>
 /// Create a connection to a remote endpoint
 /// </summary>
 public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage)
 {
     return Connect(GetNetEndPoint(host, port), hailMessage);
 }
        /// <summary>
        /// Send a message to an unconnected host
        /// </summary>
        public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
        {
            if (msg == null)
                throw new ArgumentNullException("msg");
            if (host == null)
                throw new ArgumentNullException("host");
            if (msg.m_isSent)
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
            if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
                throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");

            msg.m_isSent = true;
            msg.m_messageType = NetMessageType.Unconnected;

            var adr = NetUtility.Resolve(host);
            if (adr == null)
                throw new NetException("Failed to resolve " + host);

            Interlocked.Increment(ref msg.m_recyclingCount);
            m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(new NetEndPoint(adr, port), msg));
        }
 internal abstract NetSendResult Enqueue(NetOutgoingMessage message);