//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //===========================================================================// #include "NetChannel.h" #include "UDP_Socket.h" #include "tier1/utlbuffer.h" #include "networksystem/inetworkmessage.h" #include "networksystem.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Construction/Destruction //----------------------------------------------------------------------------- CNetChannel::CNetChannel() { m_pSocket = NULL; // invalid remote_address.Clear(); last_received = 0; connect_time = 0; m_ConnectionState = CONNECTION_STATE_DISCONNECTED; Q_strncpy( m_Name, "", sizeof(m_Name) ); m_MessageHandler = NULL; m_StreamUnreliable.StartWriting(m_UnreliableDataBuffer, sizeof(m_UnreliableDataBuffer)); m_StreamUnreliable.SetDebugName( "netchan_t::unreliabledata" ); m_StreamReliable.StartWriting(m_ReliableDataBuffer, sizeof(m_ReliableDataBuffer)); m_StreamReliable.SetDebugName( "netchan_t::reliabledata" ); m_Rate = DEFAULT_RATE; m_Timeout = SIGNON_TIME_OUT; m_PacketDrop = 0; // Prevent the first message from getting dropped after connection is set up. m_nOutSequenceNr = 1; // otherwise it looks like a m_nInSequenceNr = 0; m_nOutSequenceNrAck = 0; m_nOutReliableState = 0; // our current reliable state m_nInReliableState = 0; // last remote reliable state // FlowReset(); } CNetChannel::~CNetChannel() { Shutdown( "NetChannel removed." ); } //----------------------------------------------------------------------------- // called to open a channel to a remote system //----------------------------------------------------------------------------- void CNetChannel::Setup( bool serverSide, const netadr_t *adr, CUDPSocket *sendSocket, char const *name, INetworkMessageHandler *handler ) { Assert( name ); Assert( handler ); Assert( adr ); m_pSocket = sendSocket; // remote_address may be NULL for fake channels (demo playback etc) remote_address = *adr; last_received = g_pNetworkSystemImp->GetTime(); connect_time = last_received; Q_strncpy( m_Name, name, sizeof(m_Name) ); m_MessageHandler = handler; m_StreamUnreliable.StartWriting(m_UnreliableDataBuffer, sizeof(m_UnreliableDataBuffer)); m_StreamUnreliable.SetDebugName( "netchan_t::unreliabledata" ); m_ReliableDataBufferMP.EnsureCapacity( NET_MAX_PAYLOAD ); m_StreamReliable.StartWriting( m_ReliableDataBufferMP.Base(), NET_MAX_PAYLOAD ); m_StreamReliable.SetDebugName( "netchan_t::reliabledata" ); m_Rate = DEFAULT_RATE; m_Timeout = SIGNON_TIME_OUT; m_PacketDrop = 0; // Prevent the first message from getting dropped after connection is set up. m_nOutSequenceNr = 1; // otherwise it looks like a m_nInSequenceNr = 0; m_nOutSequenceNrAck = 0; m_nOutReliableState = 0; // our current reliable state m_nInReliableState = 0; // last remote reliable state m_nChokedPackets = 0; m_fClearTime = 0.0; m_ConnectionState = CONNECTION_STATE_CONNECTED; // FlowReset(); // tell message handler to register know netmessages m_MessageHandler->OnConnectionStarted( this ); } void CNetChannel::Shutdown( const char *pReason ) { // send discconect if ( !m_pSocket ) return; Clear(); // free all buffers (reliable & unreliable) if ( pReason ) { // send disconnect message WriteSystemNetworkMessage( m_StreamUnreliable, net_disconnect ); m_StreamUnreliable.WriteString( pReason ); Transmit(); // push message out } m_pSocket = NULL; // signals that netchannel isn't valid anymore remote_address.Clear(); m_ConnectionState = CONNECTION_STATE_DISCONNECTED; if ( m_MessageHandler ) { m_MessageHandler->OnConnectionClosing( this, pReason ); m_MessageHandler = NULL; } } //----------------------------------------------------------------------------- // Channel connection state //----------------------------------------------------------------------------- ConnectionStatus_t CNetChannel::GetConnectionState( ) { return m_ConnectionState; } void CNetChannel::SetConnectionState( ConnectionStatus_t state ) { m_ConnectionState = state; } /* void CNetChannel::GetSequenceData( int &nOutSequenceNr, int &nInSequenceNr, int &nOutSequenceNrAck ) { nOutSequenceNr = m_nOutSequenceNr; nInSequenceNr = m_nInSequenceNr; nOutSequenceNrAck = m_nOutSequenceNrAck; } void CNetChannel::SetSequenceData( int nOutSequenceNr, int nInSequenceNr, int nOutSequenceNrAck ) { Assert( IsPlayback() ); m_nOutSequenceNr = nOutSequenceNr; m_nInSequenceNr = nInSequenceNr; m_nOutSequenceNrAck = nOutSequenceNrAck; } */ void CNetChannel::SetTimeout(float seconds) { m_Timeout = seconds; if ( m_Timeout > 3600.0f ) { m_Timeout = 3600.0f; // 1 hour maximum } else if ( m_Timeout < CONNECTION_PROBLEM_TIME ) { m_Timeout = CONNECTION_PROBLEM_TIME; // allow at least this minimum } } void CNetChannel::SetDataRate(float rate) { m_Rate = clamp( rate, (float)MIN_RATE, (float)MAX_RATE ); } const char * CNetChannel::GetName() const { return m_Name; } const char * CNetChannel::GetAddress() const { return remote_address.ToString(); } /* int CNetChannel::GetDropNumber() const { return m_PacketDrop; } */ /* =============== CNetChannel::CanSendPacket Returns true if the bandwidth choke isn't active ================ */ bool CNetChannel::CanSendPacket () const { return m_fClearTime < g_pNetworkSystemImp->GetTime(); } /* void CNetChannel::FlowReset( void ) { Q_memset( m_DataFlow, 0, sizeof( m_DataFlow ) ); Q_memset( m_MsgStats, 0, sizeof( m_MsgStats ) ); } void CNetChannel::FlowNewPacket(int flow, int seqnr, int acknr, int nChoked, int nSize ) { netflow_t * pflow = &m_DataFlow[ flow ]; // if frame_number != ( current + 1 ) mark frames between as invalid netframe_t *pframe = NULL; if ( seqnr > pflow->currentindex ) { for ( int i = pflow->currentindex+1; i <= seqnr; i++ ) { pframe = &pflow->frames[ i & NET_FRAMES_MASK ]; pframe->time = GetTime(); // now pframe->valid = false; pframe->size = 0; pframe->latency = -1.0f; // not acknowledged yet pframe->choked = 0; // not acknowledged yet Q_memset( &pframe->msggroups, 0, sizeof(pframe->msggroups) ); } pframe->choked = nChoked; pframe->size = nSize; pframe->valid = true; } else { Assert( seqnr > pflow->currentindex ); } pflow->totalpackets++; pflow->currentindex = seqnr; pflow->currentframe = pframe; // updated ping for acknowledged packet int aflow = (flow==FLOW_OUTGOING) ? FLOW_INCOMING : FLOW_OUTGOING; if ( acknr <= (m_DataFlow[aflow].currentindex - NET_FRAMES_BACKUP) ) return; // acknowledged packet isn't in backup buffer anymore netframe_t * aframe = &m_DataFlow[aflow].frames[ acknr & NET_FRAMES_MASK ]; if ( aframe->valid && aframe->latency == -1.0f ) { // update ping for acknowledged packet, if not already acknowledged before aframe->latency = GetTime() - aframe->time; if ( aframe->latency < 0.0f ) aframe->latency = 0.0f; } } void CNetChannel::FlowUpdate(int flow, int addbytes) { netflow_t * pflow = &m_DataFlow[ flow ]; pflow->totalbytes += addbytes; if ( pflow->nextcompute > GetTime() ) return; pflow->nextcompute = GetTime() + FLOW_INTERVAL; int totalvalid = 0; int totalinvalid = 0; int totalbytes = 0; float totallatency = 0.0f; int totallatencycount = 0; int totalchoked = 0; float starttime = FLT_MAX; float endtime = 0.0f; netframe_t *pprev = &pflow->frames[ NET_FRAMES_BACKUP-1 ]; for ( int i = 0; i < NET_FRAMES_BACKUP; i++ ) { // Most recent message then backward from there netframe_t * pcurr = &pflow->frames[ i ]; if ( pcurr->valid ) { if ( pcurr->time < starttime ) starttime = pcurr->time; if ( pcurr->time > endtime ) endtime = pcurr->time; totalvalid++; totalchoked += pcurr->choked; totalbytes += pcurr->size; if ( pcurr->latency > -1.0f ) { totallatency += pcurr->latency; totallatencycount++; } } else { totalinvalid++; } pprev = pcurr; } float totaltime = endtime - starttime; if ( totaltime > 0.0f ) { pflow->avgbytespersec *= FLOW_AVG; pflow->avgbytespersec += ( 1.0f - FLOW_AVG ) * ((float)totalbytes / totaltime); pflow->avgpacketspersec *= FLOW_AVG; pflow->avgpacketspersec += ( 1.0f - FLOW_AVG ) * ((float)totalvalid / totaltime); } int totalPackets = totalvalid + totalinvalid; if ( totalPackets > 0 ) { pflow->avgloss *= FLOW_AVG; pflow->avgloss += ( 1.0f - FLOW_AVG ) * ((float)(totalinvalid-totalchoked)/totalPackets); if ( pflow->avgloss < 0 ) pflow->avgloss = 0; pflow->avgchoke *= FLOW_AVG; pflow->avgchoke += ( 1.0f - FLOW_AVG ) * ((float)totalchoked/totalPackets); } if ( totallatencycount>0 ) { float newping = totallatency / totallatencycount ; pflow->latency = newping; pflow->avglatency*= FLOW_AVG; pflow->avglatency += ( 1.0f - FLOW_AVG ) * newping; } } */ void CNetChannel::SetChoked( void ) { m_nOutSequenceNr++; // sends to be done since move command use sequence number m_nChokedPackets++; } bool CNetChannel::Transmit( bool onlyReliable /* =false */ ) { if ( onlyReliable ) { m_StreamUnreliable.Reset(); } return ( SendDatagram( NULL ) != 0 ); } /* =============== CNetChannel::TransmitBits tries to send an unreliable message to a connection, and handles the transmition / retransmition of the reliable messages. A 0 length will still generate a packet and deal with the reliable messages. ================ */ int CNetChannel::SendDatagram( bf_write *datagram ) { byte send_buf[ NET_MAX_MESSAGE ]; // first increase out sequence number // check, if fake client, then fake send also if ( remote_address.GetType() == NA_NULL ) { // this is a demo channel, fake sending all data m_fClearTime = 0.0; // no bandwidth delay m_nChokedPackets = 0; // Reset choke state m_StreamReliable.Reset(); // clear current reliable buffer m_StreamUnreliable.Reset(); // clear current unrelaible buffer m_nOutSequenceNr++; return m_nOutSequenceNr-1; } // process all new and pending reliable data, return true if reliable data should // been send with this packet if ( m_StreamReliable.IsOverflowed() ) { Msg ("%s:send reliable stream overflow\n" ,remote_address.ToString()); return 0; } bf_write send( "CNetChannel_TransmitBits->send", send_buf, sizeof(send_buf) ); // Prepare the packet header // build packet flags unsigned char flags = 0; // start writing packet send.WriteLong ( m_nOutSequenceNr ); send.WriteLong ( m_nInSequenceNr ); bf_write flagsPos = send; // remember flags byte position send.WriteByte ( 0 ); // write correct flags value later send.WriteByte ( m_nInReliableState ); if ( m_nChokedPackets > 0 ) { flags |= PACKET_FLAG_CHOKED; send.WriteByte ( m_nChokedPackets & 0xFF ); // send number of choked packets } /* if ( SendSubChannelData( send ) ) { flags |= PACKET_FLAG_RELIABLE; } */ // Is there room for given datagram data. the datagram data // is somewhat more important than the normal unreliable data // this is done to allow some kind of snapshot behaviour // weather all data in datagram is transmitted or none. if ( datagram ) { if( datagram->GetNumBitsWritten() < send.GetNumBitsLeft() ) { send.WriteBits( datagram->GetData(), datagram->GetNumBitsWritten() ); } else { DevMsg("CNetChannel::SendDatagram: data would overfow, ignoring\n"); } } // Is there room for the unreliable payload? if ( m_StreamUnreliable.GetNumBitsWritten() < send.GetNumBitsLeft() ) { send.WriteBits(m_StreamUnreliable.GetData(), m_StreamUnreliable.GetNumBitsWritten() ); } else { DevMsg("CNetChannel::SendDatagram: Unreliable would overfow, ignoring\n"); } m_StreamUnreliable.Reset(); // clear unreliable data buffer // Deal with packets that are too small for some networks while ( send.GetNumBytesWritten() < MIN_ROUTEABLE_PACKET ) { // Go ahead and pad some bits as long as needed WriteSystemNetworkMessage( send, net_nop ); } // fill last bits in last byte with NOP if necessary int nRemainingBits = send.GetNumBitsWritten() % 8; int nHeaderSize = g_pNetworkSystemImp->GetGroupBitCount() + g_pNetworkSystemImp->GetTypeBitCount(); while ( nRemainingBits > 0 && nRemainingBits <= ( 8 - nHeaderSize ) ) { WriteSystemNetworkMessage( send, net_nop ); nRemainingBits += nHeaderSize; } flagsPos.WriteByte( flags ); // write correct flags value // Send the datagram m_pSocket->SendTo( remote_address, send.GetData(), send.GetNumBytesWritten() ); // update stats int nTotalSize = send.GetNumBytesWritten() + UDP_HEADER_SIZE; // FlowNewPacket( FLOW_OUTGOING, m_nOutSequenceNr, m_nInSequenceNr, m_nChokedPackets, nTotalSize ); // FlowUpdate( FLOW_OUTGOING, nTotalSize ); float flTime = g_pNetworkSystemImp->GetTime(); if ( m_fClearTime < flTime ) { m_fClearTime = flTime; } // calc cleantime when channel will be ready for next packet Assert( m_Rate != 0.0f ); m_fClearTime += (float)( nTotalSize ) / (float) m_Rate; m_nChokedPackets = 0; m_nOutSequenceNr++; return m_nOutSequenceNr-1; // return send seq nr } bool CNetChannel::ProcessControlMessage( int cmd, bf_read &buf ) { switch( cmd ) { case net_nop: return true; case net_disconnect: { char pReason[1024]; buf.ReadString( pReason, sizeof(pReason) ); Shutdown( pReason ); } return false; default: Msg( "CNetChannel: received bad control cmd %i from %s.\n", cmd, remote_address.ToString() ); return false; } } bool CNetChannel::ProcessMessages( bf_read &buf ) { //int startbit = buf.GetNumBitsRead(); int nGroupCount = g_pNetworkSystemImp->GetGroupBitCount(); int nTypeCount = g_pNetworkSystemImp->GetTypeBitCount(); while ( true ) { if ( buf.IsOverflowed() ) return false; // Are we at the end? if ( buf.GetNumBitsLeft() < ( nGroupCount + nTypeCount ) ) break; unsigned int group = buf.ReadUBitLong( nGroupCount ); unsigned int type = buf.ReadUBitLong( nTypeCount ); if ( group == net_group_networksystem ) { if ( !ProcessControlMessage( type, buf ) ) return g_pNetworkSystemImp->IsNetworkEventCreated(); // disconnect or error continue; } // see if we have a registered message object for this type INetworkMessage *pNetMessage = g_pNetworkSystemImp->FindNetworkMessage( group, type ); if ( !pNetMessage ) { Msg( "Netchannel: unknown net message (%i:%i) from %s.\n", group, type, remote_address.ToString() ); Assert ( 0 ); return false; } // Attach it to the correct netchannel pNetMessage->SetNetChannel( this ); // let message parse itself from buffe const char *pGroupName = pNetMessage->GetGroupName(); const char *pMessageName = pNetMessage->GetName(); //int startbit = buf.GetNumBitsRead(); if ( !pNetMessage->ReadFromBuffer( buf ) ) { Msg( "Netchannel: failed reading message %s [%s] from %s.\n", pMessageName, pGroupName, remote_address.ToString() ); Assert ( 0 ); return false; } // UpdateMessageStats( netmsg->GetGroup(), buf.GetNumBitsRead() - startbit ); // Create a network event NetworkMessageReceivedEvent_t *pReceived = g_pNetworkSystemImp->CreateNetworkEvent< NetworkMessageReceivedEvent_t >( ); pReceived->m_nType = NETWORK_EVENT_MESSAGE_RECEIVED; pReceived->m_pChannel = this; pReceived->m_pNetworkMessage = pNetMessage; return true; // ok fine } return false; // ok fine, but don't keep processing this packet } int CNetChannel::ProcessPacketHeader( bf_read& message ) { // get sequence numbers int sequence = message.ReadLong(); int sequence_ack= message.ReadLong(); int flags = message.ReadByte(); int relState = message.ReadByte(); // reliable state of 8 subchannels int nChoked = 0; // read later if choked flag is set //int i,j; NOTE_UNUSED( relState ); if ( flags & PACKET_FLAG_CHOKED ) nChoked = message.ReadByte(); // discard stale or duplicated packets if (sequence <= m_nInSequenceNr ) { /* if ( net_showdrop.GetInt() ) { if ( sequence == m_nInSequenceNr ) { Msg ("%s:duplicate packet %i at %i\n" , remote_address.ToString () , sequence , m_nInSequenceNr); } else { Msg ("%s:out of order packet %i at %i\n" , remote_address.ToString () , sequence , m_nInSequenceNr); } } */ return -1; } // // dropped packets don't keep the message from being used // m_PacketDrop = sequence - (m_nInSequenceNr + nChoked + 1); if ( m_PacketDrop > 0 ) { /* if ( net_showdrop.GetInt() ) { Msg ("%s:Dropped %i packets at %i\n" ,remote_address.ToString(), m_PacketDrop, sequence ); } */ } m_nInSequenceNr = sequence; m_nOutSequenceNrAck = sequence_ack; // Update data flow stats // FlowNewPacket( FLOW_INCOMING, m_nInSequenceNr, m_nOutSequenceNrAck, nChoked, packet->size + UDP_HEADER_SIZE ); return flags; } //----------------------------------------------------------------------------- // CNetChannel::ProcessPacket // // called when a new packet has arrived for this netchannel // sequence numbers are extracted, fragments/file streams stripped // and then the netmessages processed //----------------------------------------------------------------------------- bool CNetChannel::StartProcessingPacket( CNetPacket *packet ) { if ( !m_MessageHandler ) return false; netadr_t from = packet->m_From; if ( remote_address.IsValid() && !from.CompareAdr ( remote_address ) ) return false; // Update data flow stats //FlowUpdate( FLOW_INCOMING, msg.TellPut() + UDP_HEADER_SIZE ); int flags = ProcessPacketHeader( packet->m_Message ); if ( flags == -1 ) return false; // invalid header/packet /* if ( net_showudp.GetInt() && net_showudp.GetInt() != 3 ) { Msg ("UDP <- %s: sz=%i seq=%i ack=%i rel=%i tm=%f\n" , GetName() , packet->m_nSizeInBytes , m_nInSequenceNr & 63 , m_nOutSequenceNrAck & 63 , flags & PACKET_FLAG_RELIABLE ? 1 : 0 , GetTime() ); } */ last_received = g_pNetworkSystemImp->GetTime(); // tell message handler that a new packet has arrived m_MessageHandler->OnPacketStarted( m_nInSequenceNr, m_nOutSequenceNrAck ); if ( flags & PACKET_FLAG_RELIABLE ) { /* int i, bit = 1<m_Message ); } void CNetChannel::EndProcessingPacket( CNetPacket *packet ) { // tell message handler that packet is completely parsed if ( m_MessageHandler ) { m_MessageHandler->OnPacketFinished(); } } bool CNetChannel::AddNetMsg( INetworkMessage *msg, bool bForceReliable ) { if ( msg->IsReliable() || bForceReliable ) { WriteNetworkMessage( m_StreamReliable, msg ); if ( m_StreamReliable.IsOverflowed() ) return false; return msg->WriteToBuffer( m_StreamReliable ); } WriteNetworkMessage( m_StreamUnreliable, msg ); if ( m_StreamUnreliable.IsOverflowed() ) return false; return msg->WriteToBuffer( m_StreamUnreliable ); } bool CNetChannel::AddData( bf_write &msg, bool bReliable ) { // Always queue any pending reliable data ahead of the fragmentation buffer if ( msg.GetNumBitsWritten() <= 0 ) return true; bf_write * buf = bReliable ? &m_StreamReliable : &m_StreamUnreliable; if ( msg.GetNumBitsWritten() > buf->GetNumBitsLeft() ) { if ( bReliable ) { Msg( "ERROR! SendData reliabe data too big (%i)", msg.GetNumBytesWritten() ); } return false; } return buf->WriteBits( msg.GetData(), msg.GetNumBitsWritten() ); } int CNetChannel::GetDataRate() const { return m_Rate; } bool CNetChannel::HasPendingReliableData( void ) { return ( m_StreamReliable.GetNumBitsWritten() > 0 ); } float CNetChannel::GetTimeConnected() const { float t = g_pNetworkSystemImp->GetTime() - connect_time; return (t>0.0f) ? t : 0.0f ; } const netadr_t & CNetChannel::GetRemoteAddress() const { return remote_address; } bool CNetChannel::IsTimedOut() const { if ( m_Timeout == -1.0f ) return false; else return ( last_received + m_Timeout ) < g_pNetworkSystemImp->GetTime(); } bool CNetChannel::IsTimingOut() const { if ( m_Timeout == -1.0f ) return false; else return (last_received + CONNECTION_PROBLEM_TIME) < g_pNetworkSystemImp->GetTime(); } float CNetChannel::GetTimeSinceLastReceived() const { float t = g_pNetworkSystemImp->GetTime() - last_received; return (t>0.0f) ? t : 0.0f ; } bool CNetChannel::IsOverflowed() const { return m_StreamReliable.IsOverflowed(); } void CNetChannel::Clear() { Reset(); } void CNetChannel::Reset() { // FlowReset(); m_StreamUnreliable.Reset(); // clear any pending unreliable data messages m_StreamReliable.Reset(); // clear any pending reliable data messages m_fClearTime = 0.0; // ready to send m_nChokedPackets = 0; } CUDPSocket *CNetChannel::GetSocket() { return m_pSocket; } float CNetChannel::GetAvgData( int flow ) const { return 0.0f; // return m_DataFlow[flow].avgbytespersec; } float CNetChannel::GetAvgPackets( int flow ) const { return 0.0f; // return m_DataFlow[flow].avgpacketspersec; } //----------------------------------------------------------------------------- // Purpose: // Input : *chan - //----------------------------------------------------------------------------- int CNetChannel::GetTotalData(int flow ) const { return 0; // return m_DataFlow[flow].totalbytes; } /* int CNetChannel::GetSequenceNr( int flow ) const { if ( flow == FLOW_OUTGOING ) { return m_nOutSequenceNr; } else if ( flow == FLOW_INCOMING ) { return m_nInSequenceNr; } return 0; } */ float CNetChannel::GetLatency( int flow ) const { return 0.0f; // return m_DataFlow[flow].latency; } float CNetChannel::GetAvgChoke( int flow ) const { return 0.0f; //return m_DataFlow[flow].avgchoke; } float CNetChannel::GetAvgLatency( int flow ) const { return 0.0f; //return m_DataFlow[flow].avglatency; } float CNetChannel::GetAvgLoss( int flow ) const { return 0.0f; //return m_DataFlow[flow].avgloss; }