/// <summary>
        /// Pushes a single system message onto the wire directly
        /// </summary>
        internal void SendSingleUnreliableSystemMessage(
            NetSystemType tp,
            NetBuffer data,
            NetworkEndPoint remoteEP,
            NetConnection connection)
        {
            // packet number
            var sendBuffer = m_sendBuffer;

            sendBuffer.Reset();

            // message type and channel
            sendBuffer.Write((byte)((int)NetMessageLibraryType.System | ((int)NetChannel.Unreliable << 3)));
            //TODO: remove this line (David)
            sendBuffer.Write((ushort)0);

            // payload length; variable byte encoded
            if (data == null)
            {
                sendBuffer.WriteVariableUInt32((uint)1);
                sendBuffer.Write((byte)tp);
            }
            else
            {
                int dataLen = data.LengthBytes;
                sendBuffer.WriteVariableUInt32((uint)(dataLen + 1));
                sendBuffer.Write((byte)tp);
                sendBuffer.Write(data.Data, 0, dataLen);
            }

            SendPacket(m_sendBuffer.Data, m_sendBuffer.LengthBytes, remoteEP, connection);
        }
Example #2
0
        internal OutgoingNetMessage CreateSystemMessage(NetSystemType systemType)
        {
            OutgoingNetMessage msg = CreateOutgoingMessage();

            msg.m_type            = NetMessageLibraryType.System;
            msg.m_sequenceChannel = NetChannel.Unreliable;
            msg.m_sequenceNumber  = 0;
            msg.m_data.Write((byte)systemType);
            return(msg);
        }
        /// <summary>
        /// Thread-safe SendSingleUnreliableSystemMessage()
        /// </summary>
        internal void QueueSingleUnreliableSystemMessage(
            NetSystemType tp,
            NetBuffer data,
            NetworkEndPoint remoteEP,
            bool useBroadcast)
        {
            var susm = new SUSystemMessage();

            susm.Type         = tp;
            susm.Data         = data;
            susm.Destination  = remoteEP;
            susm.UseBroadcast = useBroadcast;
            m_susmQueue.Enqueue(susm);
        }
Example #4
0
        /// <summary>
        /// Pushes a single system message onto the wire directly
        /// </summary>
        internal void SendSingleUnreliableSystemMessage(
            NetSystemType tp,
            NetBuffer data,
            IPEndPoint remoteEP,
            bool useBroadcast)
        {
            // packet number
            m_sendBuffer.Reset();

            // message type and channel
            m_sendBuffer.Write((byte)((int)NetMessageLibraryType.System | ((int)NetChannel.Unreliable << 3)));
            m_sendBuffer.Write((ushort)0);

            // payload length; variable byte encoded
            if (data == null)
            {
                m_sendBuffer.WriteVariableUInt32((uint)1);
                m_sendBuffer.Write((byte)tp);
            }
            else
            {
                int dataLen = data.LengthBytes;
                m_sendBuffer.WriteVariableUInt32((uint)(dataLen + 1));
                m_sendBuffer.Write((byte)tp);
                m_sendBuffer.Write(data.Data, 0, dataLen);
            }

            if (useBroadcast)
            {
                bool wasSSL = m_suppressSimulatedLag;
                try
                {
                    m_suppressSimulatedLag = true;
                    m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                    SendPacket(remoteEP);
                }
                finally
                {
                    m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
                    m_suppressSimulatedLag = wasSSL;
                }
            }
            else
            {
                SendPacket(remoteEP);
            }
        }
        /*
         * internal void HandleUserMessage(NetMessage msg)
         * {
         *      int seqNr = msg.m_sequenceNumber;
         *      int chanNr = (int)msg.m_sequenceChannel;
         *      bool isDuplicate = false;
         *
         *      int relation = RelateToExpected(seqNr, chanNr, out isDuplicate);
         *
         *      //
         *      // Unreliable
         *      //
         *      if (msg.m_sequenceChannel == NetChannel.Unreliable)
         *      {
         *              // It's all good; add message
         *              if (isDuplicate)
         *              {
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      m_owner.LogVerbose("Rejecting duplicate " + msg, this);
         *              }
         *              else
         *              {
         *                      AcceptMessage(msg);
         *              }
         *              return;
         *      }
         *
         *      //
         *      // Reliable unordered
         *      //
         *      if (msg.m_sequenceChannel == NetChannel.ReliableUnordered)
         *      {
         *              // send acknowledge (even if duplicate)
         *              m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber);
         *
         *              if (isDuplicate)
         *              {
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      m_owner.LogVerbose("Rejecting duplicate " + msg, this);
         *                      return; // reject duplicates
         *              }
         *
         *              // It's good; add message
         *              AcceptMessage(msg);
         *
         *              return;
         *      }
         *
         *      ushort nextSeq = (ushort)(seqNr + 1);
         *
         *      if (chanNr < (int)NetChannel.ReliableInOrder1)
         *      {
         *              //
         *              // Sequenced
         *              //
         *              if (relation < 0)
         *              {
         *                      // late sequenced message
         *                      m_statistics.CountDroppedSequencedMessage();
         *                      m_owner.LogVerbose("Dropping late sequenced " + msg, this);
         *                      return;
         *              }
         *
         *              // It's good; add message
         *              AcceptMessage(msg);
         *
         *              m_nextExpectedSequence[chanNr] = nextSeq;
         *              return;
         *      }
         *      else
         *      {
         *              //
         *              // Ordered
         *              //
         *
         *              // send ack (regardless)
         *              m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber);
         *
         *              if (relation < 0)
         *              {
         *                      // late ordered message
         #if DEBUG
         *                      if (!isDuplicate)
         *                              m_owner.LogWrite("Ouch, weird! Late ordered message that's NOT a duplicate?! seqNr: " + seqNr + " expecting: " + m_nextExpectedSequence[chanNr], this);
         #endif
         *                      // must be duplicate
         *                      m_owner.LogVerbose("Dropping duplicate message " + seqNr, this);
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      return; // rejected; don't advance next expected
         *              }
         *
         *              if (relation > 0)
         *              {
         *                      // early message; withhold ordered
         *                      m_owner.LogVerbose("Withholding " + msg + " (expecting " + m_nextExpectedSequence[chanNr] + ")", this);
         *                      m_withheldMessages.Add(msg);
         *                      return; // return without advancing next expected
         *              }
         *
         *              // It's right on time!
         *              AcceptMessage(msg);
         *
         *              // ordered; release other withheld messages?
         *              bool released = false;
         *              do
         *              {
         *                      released = false;
         *                      foreach (NetMessage wm in m_withheldMessages)
         *                      {
         *                              if ((int)wm.m_sequenceChannel == chanNr && wm.m_sequenceNumber == nextSeq)
         *                              {
         *                                      m_owner.LogVerbose("Releasing withheld message " + wm, this);
         *                                      m_withheldMessages.Remove(wm);
         *                                      AcceptMessage(wm);
         *                                      // no need to set rounds for this message; it was one when first related() and withheld
         *                                      nextSeq++;
         *                                      if (nextSeq >= NetConstants.NumSequenceNumbers)
         *                                              nextSeq -= NetConstants.NumSequenceNumbers;
         *                                      released = true;
         *                                      break;
         *                              }
         *                      }
         *              } while (released);
         *      }
         *
         *      // Common to Sequenced and Ordered
         *
         *      //m_owner.LogVerbose("Setting next expected for " + (NetChannel)chanNr + " to " + nextSeq);
         *      m_nextExpectedSequence[chanNr] = nextSeq;
         *
         *      return;
         * }
         */

        internal void HandleSystemMessage(IncomingNetMessage msg, double now)
        {
            msg.m_data.Position = 0;
            NetSystemType      sysType  = (NetSystemType)msg.m_data.ReadByte();
            OutgoingNetMessage response = null;

            switch (sysType)
            {
            case NetSystemType.Disconnect:
                if (m_status == NetConnectionStatus.Disconnected)
                {
                    return;
                }
                Disconnect(msg.m_data.ReadString(), 0.75f + ((float)m_currentAvgRoundtrip * 4), false, false);
                break;

            case NetSystemType.ConnectionRejected:
                string reason = msg.m_data.ReadString();
                m_owner.NotifyApplication(NetMessageType.ConnectionRejected, reason, msg.m_sender, msg.m_senderEndPoint);
                Disconnect(reason, 0.0f, false, true);
                break;

            case NetSystemType.Connect:

                // ConnectReponse must have been losts

                string appIdent = msg.m_data.ReadString();
                if (appIdent != m_owner.m_config.ApplicationIdentifier)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Connect for different application identification received: " + appIdent, null, msg.m_senderEndPoint);
                    }
                    return;
                }

                // read random identifer
                byte[] rnd = msg.m_data.ReadBytes(8);
                if (NetUtility.CompareElements(rnd, m_owner.m_randomIdentifier))
                {
                    // don't allow self-connect
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                    {
                        m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, msg.m_senderEndPoint);
                    }
                    return;
                }

                // read hail data
                m_remoteHailData = null;
                int hailBytesCount = (msg.m_data.LengthBits - msg.m_data.Position) / 8;
                if (hailBytesCount > 0)
                {
                    m_remoteHailData = msg.m_data.ReadBytes(hailBytesCount);
                }

                // finalize disconnect if it's in process
                if (m_status == NetConnectionStatus.Disconnecting)
                {
                    FinalizeDisconnect();
                }

                // send response; even if connected
                response = m_owner.CreateSystemMessage(NetSystemType.ConnectResponse);
                if (m_localHailData != null)
                {
                    response.m_data.Write(m_localHailData);
                }
                m_unsentMessages.Enqueue(response);

                break;

            case NetSystemType.ConnectResponse:
                if (m_status != NetConnectionStatus.Connecting && m_status != NetConnectionStatus.Connected)
                {
                    m_owner.LogWrite("Received connection response but we're not connecting...", this);
                    return;
                }

                // read hail data
                m_remoteHailData = null;
                int numHailBytes = (msg.m_data.LengthBits - msg.m_data.Position) / 8;
                if (numHailBytes > 0)
                {
                    m_remoteHailData = msg.m_data.ReadBytes(numHailBytes);
                }

                // Send connectionestablished
                response = m_owner.CreateSystemMessage(NetSystemType.ConnectionEstablished);
                if (m_localHailData != null)
                {
                    response.m_data.Write(m_localHailData);
                }
                m_unsentMessages.Enqueue(response);

                // send first ping 250ms after connected
                m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f);
                m_statistics.Reset();
                SetInitialAveragePing(now - m_handshakeInitiated);
                SetStatus(NetConnectionStatus.Connected, "Connected");
                break;

            case NetSystemType.ConnectionEstablished:
                if (m_status != NetConnectionStatus.Connecting)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Received connection response but we're not connecting...", this, msg.m_senderEndPoint);
                    }
                    return;
                }

                // read hail data
                if (m_remoteHailData == null)
                {
                    int hbc = (msg.m_data.LengthBits - msg.m_data.Position) / 8;
                    if (hbc > 0)
                    {
                        m_remoteHailData = msg.m_data.ReadBytes(hbc);
                    }
                }

                // send first ping 100-350ms after connected
                m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f);
                m_statistics.Reset();
                SetInitialAveragePing(now - m_handshakeInitiated);
                SetStatus(NetConnectionStatus.Connected, "Connected");
                break;

            case NetSystemType.Ping:
                // also accepted as ConnectionEstablished
                if (m_isInitiator == false && m_status == NetConnectionStatus.Connecting)
                {
                    m_owner.LogWrite("Received ping; interpreted as ConnectionEstablished", this);
                    m_statistics.Reset();
                    SetInitialAveragePing(now - m_handshakeInitiated);
                    SetStatus(NetConnectionStatus.Connected, "Connected");
                }

                //LogWrite("Received ping; sending pong...");
                SendPong(m_owner, m_remoteEndPoint, now);
                break;

            case NetSystemType.Pong:
                double twoWayLatency = now - m_lastSentPing;
                if (twoWayLatency < 0)
                {
                    break;
                }
                ReceivedPong(twoWayLatency, msg);
                break;

            case NetSystemType.StringTableAck:
                ushort val = msg.m_data.ReadUInt16();
                StringTableAcknowledgeReceived(val);
                break;

            default:
                m_owner.LogWrite("Undefined behaviour in NetConnection for system message " + sysType, this);
                break;
            }
        }
        internal override void HandleReceivedMessage(IncomingNetMessage message, NetworkEndPoint senderEndpoint, double localTimeRecv)
        {
            //LogWrite("NetClient received message " + message);
            double now = NetTime.Now;

            int payLen = message.m_data.LengthBytes;

            // Discovery response?
            if (message.m_type == NetMessageLibraryType.System && payLen > 0)
            {
                NetSystemType sysType = (NetSystemType)message.m_data.PeekByte();

                if (sysType == NetSystemType.DiscoveryResponse)
                {
                    message.m_data.ReadByte();                     // step past system type byte
                    IncomingNetMessage resMsg = m_discovery.HandleResponse(message, senderEndpoint);
                    if (resMsg != null)
                    {
                        resMsg.m_senderEndPoint = senderEndpoint;
                        EnqueueReceivedMessage(resMsg);
                    }
                    return;
                }
            }

            // Out of band?
            if (message.m_type == NetMessageLibraryType.OutOfBand)
            {
                if ((m_enabledMessageTypes & NetMessageType.OutOfBandData) != NetMessageType.OutOfBandData)
                {
                    return;                     // drop
                }
                // just deliver
                message.m_msgType        = NetMessageType.OutOfBandData;
                message.m_senderEndPoint = senderEndpoint;
                EnqueueReceivedMessage(message);
                return;
            }

            if (message.m_sender != m_serverConnection && m_serverConnection != null)
            {
                return;                 // don't talk to strange senders after this
            }
            if (message.m_type == NetMessageLibraryType.Acknowledge)
            {
                m_serverConnection.HandleAckMessage(now, message);
                return;
            }

            // Handle system types
            if (message.m_type == NetMessageLibraryType.System)
            {
                if (payLen < 1)
                {
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Received malformed system message: " + message, m_serverConnection, senderEndpoint);
                    }
                    return;
                }
                NetSystemType sysType = (NetSystemType)message.m_data.Data[0];
                switch (sysType)
                {
                case NetSystemType.ConnectResponse:
                case NetSystemType.Ping:
                case NetSystemType.Pong:
                case NetSystemType.Disconnect:
                case NetSystemType.ConnectionRejected:
                case NetSystemType.StringTableAck:
                    if (m_serverConnection != null)
                    {
                        m_serverConnection.HandleSystemMessage(message, localTimeRecv);
                    }
                    return;

                case NetSystemType.Connect:
                case NetSystemType.ConnectionEstablished:
                case NetSystemType.Discovery:
                case NetSystemType.Error:
                default:
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for client and " + sysType, m_serverConnection, senderEndpoint);
                    }
                    return;
                }
            }

            Debug.Assert(
                message.m_type == NetMessageLibraryType.User ||
                message.m_type == NetMessageLibraryType.UserFragmented
                );

            if (m_serverConnection.Status == NetConnectionStatus.Connecting)
            {
                m_serverConnection.Disconnect("Received user message before ConnectResponse", 0.0f, true, true);
                return;

                /*
                 * // lost connectresponse packet?
                 * // Emulate it;
                 * LogVerbose("Received user message before ConnectResponse; emulating ConnectResponse...", m_serverConnection);
                 * IncomingNetMessage emuMsg = CreateIncomingMessage();
                 * emuMsg.m_type = NetMessageLibraryType.System;
                 * emuMsg.m_data.Reset();
                 * emuMsg.m_data.Write((byte)NetSystemType.ConnectResponse);
                 * m_serverConnection.HandleSystemMessage(emuMsg, now);
                 */

                // ... and proceed to pick up user message
            }

            // add to pick-up queue
            m_serverConnection.HandleUserMessage(message, now);
        }
        internal override void HandleReceivedMessage(IncomingNetMessage message, NetworkEndPoint senderEndpoint, double timestamp)
        {
            double now = NetTime.Now;

            int payLen = message.m_data.LengthBytes;

            // Out of band?
            if (message.m_type == NetMessageLibraryType.OutOfBand)
            {
                if ((m_enabledMessageTypes & NetMessageType.OutOfBandData) != NetMessageType.OutOfBandData)
                {
                    return;                     // drop
                }
                // just deliver
                message.m_msgType        = NetMessageType.OutOfBandData;
                message.m_senderEndPoint = senderEndpoint;

                EnqueueReceivedMessage(message);
                return;
            }

            if (message.m_sender == null)
            {
                //
                // Handle unconnected message
                //

                // not a connected sender; only allow System messages
                if (message.m_type != NetMessageLibraryType.System)
                {
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Rejecting non-system message from unconnected source: " + message, null, message.m_senderEndPoint);
                    }
                    return;
                }

                // read type of system message
                NetSystemType sysType = (NetSystemType)message.m_data.ReadByte();
                switch (sysType)
                {
                case NetSystemType.Connect:

                    LogVerbose("Connection request received from " + senderEndpoint);

                    NetConnection conn;
                    if (m_pendingLookup.TryGetValue(senderEndpoint, out conn))
                    {
                        if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                        {
                            NotifyApplication(NetMessageType.BadMessageReceived, "Ignore connection request received because already pending", conn, senderEndpoint);
                        }
                        return;
                    }

                    if (m_connectionLookup.TryGetValue(senderEndpoint, out conn))
                    {
                        if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                        {
                            NotifyApplication(NetMessageType.BadMessageReceived, "Ignore connection request received because already connected", conn, senderEndpoint);
                        }
                        return;
                    }

                    // check app id
                    if (payLen < 4)
                    {
                        if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                        {
                            NotifyApplication(NetMessageType.BadMessageReceived, "Malformed Connect message received from " + senderEndpoint, null, senderEndpoint);
                        }
                        return;
                    }
                    string appIdent = message.m_data.ReadString();
                    if (appIdent != m_config.ApplicationIdentifier)
                    {
                        if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                        {
                            NotifyApplication(NetMessageType.ConnectionRejected, "Bad app id", null, senderEndpoint);
                        }

                        // send connection rejected
                        NetBuffer rejreason = new NetBuffer("Bad app id");
                        QueueSingleUnreliableSystemMessage(
                            NetSystemType.ConnectionRejected,
                            rejreason,
                            senderEndpoint,
                            false
                            );
                        return;
                    }

                    // read random identifer
                    var rndSignature = message.m_data.ReadBytes(NetConstants.SignatureByteSize);
                    if (NetUtility.CompareElements(rndSignature, m_localRndSignature))
                    {
                        // don't allow self-connect
                        if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                        {
                            NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, senderEndpoint);
                        }
                        return;
                    }

                    int rndSeqNr = message.m_data.ReadInt32();

                    double localTimeSent  = message.m_data.ReadDouble();
                    double remoteTimeRecv = timestamp + m_localTimeOffset;

                    int    bytesReadSoFar = message.m_data.PositionBytes;
                    int    hailLen        = message.m_data.LengthBytes - bytesReadSoFar;
                    byte[] hailData       = null;
                    if (hailLen > 0)
                    {
                        hailData = new byte[hailLen];
                        Buffer.BlockCopy(message.m_data.Data, bytesReadSoFar, hailData, 0, hailLen);
                    }

                    if (m_config.m_maxConnections != -1 && m_connections.Count >= m_config.m_maxConnections)
                    {
                        if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                        {
                            NotifyApplication(NetMessageType.ConnectionRejected, "Max connections", null, senderEndpoint);
                        }

                        // send connection rejected
                        NetBuffer rejreason = new NetBuffer("Server full");
                        QueueSingleUnreliableSystemMessage(
                            NetSystemType.ConnectionRejected,
                            rejreason,
                            senderEndpoint,
                            false
                            );

                        return;
                    }

#if LIMITED_BUILD
                    if (m_connections.Count >= 2)
                    {
                        if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                        {
                            NotifyApplication(NetMessageType.ConnectionRejected, "I'm special", null, senderEndpoint);
                        }

                        // send connection rejected
                        NetBuffer rejreason = new NetBuffer("Special server");
                        QueueSingleUnreliableSystemMessage(
                            NetSystemType.ConnectionRejected,
                            rejreason,
                            senderEndpoint,
                            false
                            );

                        return;
                    }
#endif

                    // Create connection
                    LogWrite("New connection: " + senderEndpoint);
                    conn = new NetConnection(this, senderEndpoint, null, hailData, rndSignature, rndSeqNr);
                    m_pendingLookup.Add(senderEndpoint, conn);

                    conn.m_connectLocalSentTime  = localTimeSent;
                    conn.m_connectRemoteRecvTime = remoteTimeRecv;

                    // Connection approval?
                    if ((m_enabledMessageTypes & NetMessageType.ConnectionApproval) == NetMessageType.ConnectionApproval)
                    {
                        // Ask application if this connection is allowed to proceed
                        IncomingNetMessage app = CreateIncomingMessage();
                        app.m_msgType = NetMessageType.ConnectionApproval;
                        if (hailData != null)
                        {
                            app.m_data.Write(hailData);
                        }
                        app.m_sender    = conn;
                        conn.m_approved = false;
                        EnqueueReceivedMessage(app);
                        // Don't add connection; it's done as part of the approval procedure
                        return;
                    }

                    // it's ok
                    AddConnection(now, conn);
                    break;

                case NetSystemType.ConnectionEstablished:
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Connection established received from non-connection! " + senderEndpoint, null, senderEndpoint);
                    }
                    return;

                case NetSystemType.Discovery:
                    if (m_config.AnswerDiscoveryRequests)
                    {
                        m_discovery.HandleRequest(message, senderEndpoint);
                    }
                    break;

                case NetSystemType.DiscoveryResponse:
                    if (m_allowOutgoingConnections)
                    {
                        // NetPeer
                        IncomingNetMessage resMsg = m_discovery.HandleResponse(message, senderEndpoint);
                        if (resMsg != null)
                        {
                            resMsg.m_senderEndPoint = senderEndpoint;
                            EnqueueReceivedMessage(resMsg);
                        }
                    }
                    break;

                case NetSystemType.Ping:
                {
                    var tempBuffer = GetTempBuffer();
                    tempBuffer.Write("We're not connected");
                    SendSingleUnreliableSystemMessage(
                        NetSystemType.Disconnect,
                        tempBuffer,
                        senderEndpoint,
                        null
                        );

                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Ignore " + this + " receiving system type " + sysType + ": " + message + " from unconnected source", null, senderEndpoint);
                    }

                    break;
                }

                default:
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Ignore " + this + " receiving system type " + sysType + ": " + message + " from unconnected source", null, senderEndpoint);
                    }
                    break;
                }
                // done
                return;
            }

            // ok, we have a sender
            if (message.m_type == NetMessageLibraryType.Acknowledge)
            {
                message.m_sender.HandleAckMessage(now, message);
                return;
            }

            if (message.m_type == NetMessageLibraryType.System)
            {
                //
                // Handle system messages from connected source
                //

                if (payLen < 1)
                {
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Received malformed system message; payload length less than 1 byte", null, senderEndpoint);
                    }
                    return;
                }
                NetSystemType sysType = (NetSystemType)message.m_data.ReadByte();
                switch (sysType)
                {
                case NetSystemType.Connect:
                case NetSystemType.ConnectionEstablished:
                case NetSystemType.Ping:
                case NetSystemType.Pong:
                case NetSystemType.Disconnect:
                case NetSystemType.ConnectionRejected:
                case NetSystemType.StringTableAck:
                    message.m_sender.HandleSystemMessage(message, now);
                    break;

                case NetSystemType.ConnectResponse:
                    if (m_allowOutgoingConnections)
                    {
                        message.m_sender.HandleSystemMessage(message, now);
                    }
                    else
                    {
                        if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                        {
                            NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint);
                        }
                    }
                    break;

                case NetSystemType.Discovery:
                    // Allow discovery even if connected
                    if (m_config.AnswerDiscoveryRequests)
                    {
                        m_discovery.HandleRequest(message, senderEndpoint);
                    }
                    break;

                default:
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint);
                    }
                    break;
                }
                return;
            }

            message.m_sender.HandleUserMessage(message, now);
        }
Example #8
0
        /// <summary>
        /// Pushes a single system message onto the wire directly
        /// </summary>
        internal void SendSingleUnreliableSystemMessage(
            NetSystemType tp,
            NetBuffer data,
            IPEndPoint remoteEP,
            bool useBroadcast)
        {
            // packet number
            m_sendBuffer.Reset();

            // message type and channel
            m_sendBuffer.Write((byte)((int)NetMessageLibraryType.System | ((int)NetChannel.Unreliable << 3)));
            m_sendBuffer.Write((ushort)0);

            // payload length; variable byte encoded
            if (data == null)
            {
                m_sendBuffer.WriteVariableUInt32((uint)1);
                m_sendBuffer.Write((byte)tp);
            }
            else
            {
                int dataLen = data.LengthBytes;
                m_sendBuffer.WriteVariableUInt32((uint)(dataLen + 1));
                m_sendBuffer.Write((byte)tp);
                m_sendBuffer.Write(data.Data, 0, dataLen);
            }

            if (useBroadcast)
            {
                bool wasSSL = m_suppressSimulatedLag;
                try
                {
                    m_suppressSimulatedLag = true;
                    m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                    SendPacket(remoteEP);
                }
                finally
                {
                    m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
                    m_suppressSimulatedLag = wasSSL;
                }
            }
            else
            {
                SendPacket(remoteEP);
            }
        }
Example #9
0
 /// <summary>
 /// Thread-safe SendSingleUnreliableSystemMessage()
 /// </summary>
 internal void QueueSingleUnreliableSystemMessage(
     NetSystemType tp,
     NetBuffer data,
     IPEndPoint remoteEP,
     bool useBroadcast)
 {
     SUSystemMessage susm = new SUSystemMessage();
     susm.Type = tp;
     susm.Data = data;
     susm.Destination = remoteEP;
     susm.UseBroadcast = useBroadcast;
     lock (m_susmQueue)
         m_susmQueue.Enqueue(susm);
 }
Example #10
0
 internal OutgoingNetMessage CreateSystemMessage(NetSystemType systemType)
 {
     OutgoingNetMessage msg = CreateOutgoingMessage();
     msg.m_type = NetMessageLibraryType.System;
     msg.m_sequenceChannel = NetChannel.Unreliable;
     msg.m_sequenceNumber = 0;
     msg.m_data.Write((byte)systemType);
     return msg;
 }
Example #11
0
        internal override void HandleReceivedMessage(IncomingNetMessage message, IPEndPoint senderEndpoint)
        {
            double now = NetTime.Now;

            int payLen = message.m_data.LengthBytes;

            // NAT introduction?
            if (HandleNATIntroduction(message))
            {
                return;
            }

            // Out of band?
            if (message.m_type == NetMessageLibraryType.OutOfBand)
            {
                if ((m_enabledMessageTypes & NetMessageType.OutOfBandData) != NetMessageType.OutOfBandData)
                {
                    return;                     // drop
                }
                // just deliever
                message.m_msgType        = NetMessageType.OutOfBandData;
                message.m_senderEndPoint = senderEndpoint;
                lock (m_receivedMessages)
                    m_receivedMessages.Enqueue(message);
                return;
            }

            if (message.m_sender == null)
            {
                //
                // Handle unconnected message
                //

                // not a connected sender; only allow System messages
                if (message.m_type != NetMessageLibraryType.System)
                {
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Rejecting non-system message from unconnected source: " + message, null, message.m_senderEndPoint);
                    }
                    return;
                }

                // read type of system message
                NetSystemType sysType = (NetSystemType)message.m_data.ReadByte();
                switch (sysType)
                {
                case NetSystemType.Connect:

                    LogVerbose("Connection request received from " + senderEndpoint);

                    // check app ident
                    if (payLen < 4)
                    {
                        if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                        {
                            NotifyApplication(NetMessageType.BadMessageReceived, "Malformed Connect message received from " + senderEndpoint, null, senderEndpoint);
                        }
                        return;
                    }
                    string appIdent = message.m_data.ReadString();
                    if (appIdent != m_config.ApplicationIdentifier)
                    {
                        if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                        {
                            NotifyApplication(NetMessageType.BadMessageReceived, "Connect for different application identification received: " + appIdent, null, senderEndpoint);
                        }
                        return;
                    }

                    // read random identifer
                    byte[] rnd = message.m_data.ReadBytes(8);
                    if (NetUtility.CompareElements(rnd, m_randomIdentifier))
                    {
                        // don't allow self-connect
                        if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                        {
                            NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, senderEndpoint);
                        }
                        return;
                    }

                    int    bytesReadSoFar = (message.m_data.Position / 8);
                    int    hailLen        = message.m_data.LengthBytes - bytesReadSoFar;
                    byte[] hailData       = null;
                    if (hailLen > 0)
                    {
                        hailData = new byte[hailLen];
                        Buffer.BlockCopy(message.m_data.Data, bytesReadSoFar, hailData, 0, hailLen);
                    }

                    if (m_connections.Count >= m_config.m_maxConnections)
                    {
                        if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                        {
                            NotifyApplication(NetMessageType.ConnectionRejected, "Server full; rejecting connect from " + senderEndpoint, null, senderEndpoint);
                        }
                        return;
                    }

                    // Create connection
                    LogWrite("New connection: " + senderEndpoint);
                    NetConnection conn = new NetConnection(this, senderEndpoint, null, hailData);

                    // Connection approval?
                    if ((m_enabledMessageTypes & NetMessageType.ConnectionApproval) == NetMessageType.ConnectionApproval)
                    {
                        // Ask application if this connection is allowed to proceed
                        IncomingNetMessage app = CreateIncomingMessage();
                        app.m_msgType = NetMessageType.ConnectionApproval;
                        if (hailData != null)
                        {
                            app.m_data.Write(hailData);
                        }
                        app.m_sender    = conn;
                        conn.m_approved = false;
                        lock (m_receivedMessages)
                            m_receivedMessages.Enqueue(app);
                        // Don't add connection; it's done as part of the approval procedure
                        return;
                    }

                    // it's ok
                    AddConnection(now, conn);
                    break;

                case NetSystemType.ConnectionEstablished:
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Connection established received from non-connection! " + senderEndpoint, null, senderEndpoint);
                    }
                    return;

                case NetSystemType.Discovery:
                    if (m_config.AnswerDiscoveryRequests)
                    {
                        m_discovery.HandleRequest(message, senderEndpoint);
                    }
                    break;

                case NetSystemType.DiscoveryResponse:
                    if (m_allowOutgoingConnections)
                    {
                        // NetPeer
                        IncomingNetMessage resMsg = m_discovery.HandleResponse(message, senderEndpoint);
                        resMsg.m_senderEndPoint = senderEndpoint;
                        if (resMsg != null)
                        {
                            lock (m_receivedMessages)
                                m_receivedMessages.Enqueue(resMsg);
                        }
                    }
                    break;

                default:
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for " + this + " receiving system type " + sysType + ": " + message + " from unconnected source", null, senderEndpoint);
                    }
                    break;
                }
                // done
                return;
            }

            // ok, we have a sender
            if (message.m_type == NetMessageLibraryType.Acknowledge)
            {
                message.m_sender.HandleAckMessage(message);
                return;
            }

            if (message.m_type == NetMessageLibraryType.System)
            {
                //
                // Handle system messages from connected source
                //

                if (payLen < 1)
                {
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Received malformed system message; payload length less than 1 byte", null, senderEndpoint);
                    }
                    return;
                }
                NetSystemType sysType = (NetSystemType)message.m_data.ReadByte();
                switch (sysType)
                {
                case NetSystemType.Connect:
                case NetSystemType.ConnectionEstablished:
                case NetSystemType.Ping:
                case NetSystemType.Pong:
                case NetSystemType.Disconnect:
                case NetSystemType.ConnectionRejected:
                case NetSystemType.StringTableAck:
                    message.m_sender.HandleSystemMessage(message, now);
                    break;

                case NetSystemType.ConnectResponse:
                    if (m_allowOutgoingConnections)
                    {
                        message.m_sender.HandleSystemMessage(message, now);
                    }
                    else
                    {
                        if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                        {
                            NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint);
                        }
                    }
                    break;

                case NetSystemType.Discovery:
                    // Allow discovery even if connected
                    if (m_config.AnswerDiscoveryRequests)
                    {
                        m_discovery.HandleRequest(message, senderEndpoint);
                    }
                    break;

                default:
                    if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint);
                    }
                    break;
                }
                return;
            }

            message.m_sender.HandleUserMessage(message);
        }
        /*
         * internal void HandleUserMessage(NetMessage msg)
         * {
         *      int seqNr = msg.m_sequenceNumber;
         *      int chanNr = (int)msg.m_sequenceChannel;
         *      bool isDuplicate = false;
         *
         *      int relation = RelateToExpected(seqNr, chanNr, out isDuplicate);
         *
         *      //
         *      // Unreliable
         *      //
         *      if (msg.m_sequenceChannel == NetChannel.Unreliable)
         *      {
         *              // It's all good; add message
         *              if (isDuplicate)
         *              {
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      m_owner.LogVerbose("Rejecting duplicate " + msg, this);
         *              }
         *              else
         *              {
         *                      AcceptMessage(msg);
         *              }
         *              return;
         *      }
         *
         *      //
         *      // Reliable unordered
         *      //
         *      if (msg.m_sequenceChannel == NetChannel.ReliableUnordered)
         *      {
         *              // send acknowledge (even if duplicate)
         *              m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber);
         *
         *              if (isDuplicate)
         *              {
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      m_owner.LogVerbose("Rejecting duplicate " + msg, this);
         *                      return; // reject duplicates
         *              }
         *
         *              // It's good; add message
         *              AcceptMessage(msg);
         *
         *              return;
         *      }
         *
         *      ushort nextSeq = (ushort)(seqNr + 1);
         *
         *      if (chanNr < (int)NetChannel.ReliableInOrder1)
         *      {
         *              //
         *              // Sequenced
         *              //
         *              if (relation < 0)
         *              {
         *                      // late sequenced message
         *                      m_statistics.CountDroppedSequencedMessage();
         *                      m_owner.LogVerbose("Dropping late sequenced " + msg, this);
         *                      return;
         *              }
         *
         *              // It's good; add message
         *              AcceptMessage(msg);
         *
         *              m_nextExpectedSequence[chanNr] = nextSeq;
         *              return;
         *      }
         *      else
         *      {
         *              //
         *              // Ordered
         *              //
         *
         *              // send ack (regardless)
         *              m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber);
         *
         *              if (relation < 0)
         *              {
         *                      // late ordered message
         #if DEBUG
         *                      if (!isDuplicate)
         *                              m_owner.LogWrite("Ouch, weird! Late ordered message that's NOT a duplicate?! seqNr: " + seqNr + " expecting: " + m_nextExpectedSequence[chanNr], this);
         #endif
         *                      // must be duplicate
         *                      m_owner.LogVerbose("Dropping duplicate message " + seqNr, this);
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      return; // rejected; don't advance next expected
         *              }
         *
         *              if (relation > 0)
         *              {
         *                      // early message; withhold ordered
         *                      m_owner.LogVerbose("Withholding " + msg + " (expecting " + m_nextExpectedSequence[chanNr] + ")", this);
         *                      m_withheldMessages.Add(msg);
         *                      return; // return without advancing next expected
         *              }
         *
         *              // It's right on time!
         *              AcceptMessage(msg);
         *
         *              // ordered; release other withheld messages?
         *              bool released = false;
         *              do
         *              {
         *                      released = false;
         *                      foreach (NetMessage wm in m_withheldMessages)
         *                      {
         *                              if ((int)wm.m_sequenceChannel == chanNr && wm.m_sequenceNumber == nextSeq)
         *                              {
         *                                      m_owner.LogVerbose("Releasing withheld message " + wm, this);
         *                                      m_withheldMessages.Remove(wm);
         *                                      AcceptMessage(wm);
         *                                      // no need to set rounds for this message; it was one when first related() and withheld
         *                                      nextSeq++;
         *                                      if (nextSeq >= NetConstants.NumSequenceNumbers)
         *                                              nextSeq -= NetConstants.NumSequenceNumbers;
         *                                      released = true;
         *                                      break;
         *                              }
         *                      }
         *              } while (released);
         *      }
         *
         *      // Common to Sequenced and Ordered
         *
         *      //m_owner.LogVerbose("Setting next expected for " + (NetChannel)chanNr + " to " + nextSeq);
         *      m_nextExpectedSequence[chanNr] = nextSeq;
         *
         *      return;
         * }
         */

        internal void HandleSystemMessage(IncomingNetMessage msg, double timestamp)
        {
            msg.m_data.PositionBits = 0;
            NetSystemType sysType = (NetSystemType)msg.m_data.ReadByte();

            switch (sysType)
            {
            case NetSystemType.Disconnect:
                if (m_status == NetConnectionStatus.Disconnected)
                {
                    return;
                }
                Disconnect(msg.m_data.ReadString(), NetConstants.DisconnectLingerTime, false, false);
                break;

            case NetSystemType.ConnectionRejected:
                string reason = msg.m_data.ReadString();
                m_owner.NotifyApplication(NetMessageType.ConnectionRejected, reason, msg.m_sender, msg.m_senderEndPoint);
                Disconnect(reason, 0.0f, false, true);
                break;

            case NetSystemType.Connect:
            {
                // ConnectReponse must have been losts

                /*if (m_isInitiator) // this might be useful in the future
                 * {
                 *      m_owner.LogWrite("Received connect but we're the initiator...", this);
                 *      return; // ignore
                 * }*/

                if (Status != NetConnectionStatus.Connecting && Status != NetConnectionStatus.Connected)
                {
                    m_owner.LogWrite("Received connect but we're not connecting or connected...", this);
                    return;                                     // ignore if disconnecting
                }

                string appIdent = msg.m_data.ReadString();
                if (appIdent != m_owner.m_config.ApplicationIdentifier)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived,
                                                  "Connect for different application identification received: " + appIdent, null,
                                                  msg.m_senderEndPoint);
                    }
                    return;
                }

                // read random identifier
                var remoteRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize);
                if (!m_owner.Configuration.AllowConnectionToSelf && NetUtility.CompareElements(remoteRndSignature, m_owner.m_localRndSignature))
                {
                    // don't allow self-connect
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                    {
                        m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null,
                                                  msg.m_senderEndPoint);
                    }
                    return;
                }

                int remoteRndSeqNr = msg.m_data.ReadInt32();

                if (m_remoteRndSignature != null)
                {
                    if (!NetUtility.CompareElements(remoteRndSignature, m_remoteRndSignature) || remoteRndSeqNr != m_remoteRndSeqNr)
                    {
                        // this is not the same connection.
                        Disconnect("Not the same connection", 0, false, true);
                        return;
                    }
                }
                else                                 // this might happen if both try and connect at the same time.
                {
                    m_remoteRndSignature = remoteRndSignature;
                    m_remoteRndSeqNr     = remoteRndSeqNr;
                    ResetReliability();
                }

                m_connectLocalSentTime  = msg.m_data.ReadDouble();
                m_connectRemoteRecvTime = timestamp + m_owner.m_localTimeOffset;

                // read hail data
                m_remoteHailData = null;
                int hailBytesCount = (msg.m_data.LengthBits - msg.m_data.PositionBits) / 8;
                if (hailBytesCount > 0)
                {
                    m_remoteHailData = msg.m_data.ReadBytes(hailBytesCount);
                }

                // send response; even if connected
                var responseBuffer = m_owner.GetTempBuffer();
                responseBuffer.Write(m_owner.m_localRndSignature);
                responseBuffer.Write(m_localRndSeqNr);
                responseBuffer.Write(m_remoteRndSignature);
                responseBuffer.Write(m_remoteRndSeqNr);

                double localTimeSent = m_connectLocalSentTime;
                responseBuffer.Write(localTimeSent);
                double remoteTimeRecv = m_connectRemoteRecvTime;
                responseBuffer.Write(remoteTimeRecv);
                double remoteTimeSent = NetTime.Now + m_owner.m_localTimeOffset;
                responseBuffer.Write(remoteTimeSent);

                if (m_localHailData != null)
                {
                    responseBuffer.Write(m_localHailData);
                }

                m_handshakeInitiated = remoteTimeSent;
                SendSingleUnreliableSystemMessage(NetSystemType.ConnectResponse, responseBuffer);
                break;
            }

            case NetSystemType.ConnectResponse:
            {
                /*if (m_isInitiator) // this might be useful in the future
                 * {
                 *      m_owner.LogWrite("Received connect but we're the initiator...", this);
                 *      return; // ignore
                 * }*/

                if (m_status != NetConnectionStatus.Connecting && m_status != NetConnectionStatus.Connected)
                {
                    m_owner.LogWrite("Received connection response but we're not connecting or connected...", this);
                    return;
                }

                var remoteRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize);
                if (!m_owner.Configuration.AllowConnectionToSelf && NetUtility.CompareElements(remoteRndSignature, m_owner.m_localRndSignature))
                {
                    // don't allow self-connect
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                    {
                        m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null,
                                                  msg.m_senderEndPoint);
                    }
                    return;
                }

                int remoteRndSeqNr = msg.m_data.ReadInt32();

                if (m_remoteRndSignature != null)
                {
                    if (!NetUtility.CompareElements(remoteRndSignature, m_remoteRndSignature) || remoteRndSeqNr != m_remoteRndSeqNr)
                    {
                        // this is not the same connection.
                        Disconnect("Not the same connection", 0, false, true);
                        return;
                    }
                }
                else                                 // this might happen if both try and connect at the same time.
                {
                    m_remoteRndSignature = remoteRndSignature;
                    m_remoteRndSeqNr     = remoteRndSeqNr;
                    ResetReliability();
                }

                var myRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize);
                var myRndSeqNr     = msg.m_data.ReadInt32();

                if (!NetUtility.CompareElements(myRndSignature, m_owner.m_localRndSignature) || myRndSeqNr != m_localRndSeqNr)
                {
                    // this is not the same connection.
                    Disconnect("Not the same connection", 0, false, true);
                    return;
                }

                double localTimeSent  = msg.m_data.ReadDouble();
                double remoteTimeRecv = msg.m_data.ReadDouble();
                double remoteTimeSent = msg.m_data.ReadDouble();
                double localTimeRecv  = timestamp + m_owner.m_localTimeOffset;

                // read hail data
                m_remoteHailData = null;
                int numHailBytes = (msg.m_data.LengthBits - msg.m_data.PositionBits) / 8;
                if (numHailBytes > 0)
                {
                    m_remoteHailData = msg.m_data.ReadBytes(numHailBytes);
                }

                // Send connection established
                var responseBuffer = m_owner.GetTempBuffer();
                responseBuffer.Write(m_owner.m_localRndSignature);
                responseBuffer.Write(m_localRndSeqNr);
                responseBuffer.Write(m_remoteRndSignature);
                responseBuffer.Write(m_remoteRndSeqNr);

                double intiatorLocalTimeSent = remoteTimeSent;
                responseBuffer.Write(intiatorLocalTimeSent);
                double intiatorRemoteTimeRecv = localTimeRecv;
                responseBuffer.Write(intiatorRemoteTimeRecv);
                double intiatorRemoteTimeSent = NetTime.Now + m_owner.m_localTimeOffset;
                responseBuffer.Write(intiatorRemoteTimeSent);

                if (m_localHailData != null)
                {
                    responseBuffer.Write(m_localHailData);
                }

                SendSingleUnreliableSystemMessage(NetSystemType.ConnectionEstablished, responseBuffer);

                // send first ping 250ms after connected
                m_lastSentPing = timestamp - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f);
                m_statistics.Reset();
                SetInitialPongEntry(new PongEntry(localTimeSent, remoteTimeRecv, remoteTimeSent, localTimeRecv));
                SetStatus(NetConnectionStatus.Connected, "Connected");
                break;
            }

            case NetSystemType.ConnectionEstablished:
            {
                if (m_status != NetConnectionStatus.Connecting)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived,
                                                  "Received connection response but we're not connecting...", this, msg.m_senderEndPoint);
                    }
                    return;
                }

                var remoteRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize);
                if (!m_owner.Configuration.AllowConnectionToSelf && NetUtility.CompareElements(remoteRndSignature, m_owner.m_localRndSignature))
                {
                    // don't allow self-connect
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                    {
                        m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null,
                                                  msg.m_senderEndPoint);
                    }
                    return;
                }

                int remoteRndSeqNr = msg.m_data.ReadInt32();

                if (m_remoteRndSignature != null)
                {
                    if (!NetUtility.CompareElements(remoteRndSignature, m_remoteRndSignature) || remoteRndSeqNr != m_remoteRndSeqNr)
                    {
                        // this is not the same connection.
                        Disconnect("Not the same connection", 0, false, true);
                        return;
                    }
                }
                else                                 // this might happen if both try and connect at the same time.
                {
                    m_remoteRndSignature = remoteRndSignature;
                    m_remoteRndSeqNr     = remoteRndSeqNr;
                    ResetReliability();
                }

                var myRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize);
                var myRndSeqNr     = msg.m_data.ReadInt32();

                if (!NetUtility.CompareElements(myRndSignature, m_owner.m_localRndSignature) || myRndSeqNr != m_localRndSeqNr)
                {
                    // this is not the same connection.
                    Disconnect("Not the same connection", 0, false, true);
                    return;
                }

                double localTimeSent  = msg.m_data.ReadDouble();
                double remoteTimeRecv = msg.m_data.ReadDouble();
                double remoteTimeSent = msg.m_data.ReadDouble();
                double localTimeRecv  = timestamp + m_owner.m_localTimeOffset;

                // read hail data
                if (m_remoteHailData == null)
                {
                    int hbc = (msg.m_data.LengthBits - msg.m_data.PositionBits) / 8;
                    if (hbc > 0)
                    {
                        m_remoteHailData = msg.m_data.ReadBytes(hbc);
                    }
                }

                // send first ping 100-350ms after connected
                m_lastSentPing = timestamp - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f);
                m_statistics.Reset();
                SetInitialPongEntry(new PongEntry(localTimeSent, remoteTimeRecv, remoteTimeSent, localTimeRecv));
                SetStatus(NetConnectionStatus.Connected, "Connected");
                break;
            }

            case NetSystemType.Ping:
                // also accepted as ConnectionEstablished
                if (m_isInitiator == false && m_status == NetConnectionStatus.Connecting)
                {
                    m_owner.LogWrite("Received ping; interpreted as ConnectionEstablished", this);
                    m_statistics.Reset();
                    SetInitialPongEntryApprox(timestamp - m_handshakeInitiated);
                    SetStatus(NetConnectionStatus.Connected, "Connected");
                }

                if (m_status != NetConnectionStatus.Connected)
                {
                    m_owner.LogWrite("Received ping but we're not connected...", this);
                    if (m_status == NetConnectionStatus.Disconnected)
                    {
                        // this might cause a issue (especially if both are trying to connect at the same time).
                        Disconnect("We're not connected", 0, true, true);
                    }
                    return;
                }

                ReceivedPing(msg, timestamp + m_owner.m_localTimeOffset);
                break;

            case NetSystemType.Pong:
                ReceivedPong(msg, timestamp + m_owner.m_localTimeOffset);
                break;

            case NetSystemType.StringTableAck:
                ushort val = msg.m_data.ReadUInt16();
                StringTableAcknowledgeReceived(val);
                break;

            default:
                m_owner.LogWrite("Undefined behavior in NetConnection for system message " + sysType, this);
                break;
            }
        }
 internal void SendSingleUnreliableSystemMessage(NetSystemType tp, NetBuffer data)
 {
     m_owner.SendSingleUnreliableSystemMessage(tp, data, m_remoteEndPoint, this);
 }