diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /networksystem | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'networksystem')
| -rw-r--r-- | networksystem/netchannel.cpp | 922 | ||||
| -rw-r--r-- | networksystem/netchannel.h | 274 | ||||
| -rw-r--r-- | networksystem/networkclient.cpp | 178 | ||||
| -rw-r--r-- | networksystem/networkclient.h | 54 | ||||
| -rw-r--r-- | networksystem/networkserver.cpp | 224 | ||||
| -rw-r--r-- | networksystem/networkserver.h | 86 | ||||
| -rw-r--r-- | networksystem/networksystem.cpp | 491 | ||||
| -rw-r--r-- | networksystem/networksystem.h | 172 | ||||
| -rw-r--r-- | networksystem/networksystem.vpc | 54 | ||||
| -rw-r--r-- | networksystem/sm_protocol.h | 49 | ||||
| -rw-r--r-- | networksystem/udp_process.cpp | 134 | ||||
| -rw-r--r-- | networksystem/udp_process.h | 21 | ||||
| -rw-r--r-- | networksystem/udp_socket.cpp | 120 | ||||
| -rw-r--r-- | networksystem/udp_socket.h | 45 |
14 files changed, 2824 insertions, 0 deletions
diff --git a/networksystem/netchannel.cpp b/networksystem/netchannel.cpp new file mode 100644 index 0000000..d588d67 --- /dev/null +++ b/networksystem/netchannel.cpp @@ -0,0 +1,922 @@ +//========= 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<<msg.ReadUBitLong( 3 ); + + for ( i=0; i<MAX_STREAMS; i++ ) + { + if ( msg.ReadOneBit() != 0 ) + { + if ( !ReadSubChannelData( msg, i ) ) + return false; // error while reading fragments, drop whole packet + } + } + + // flip subChannel bit to signal successfull receiving + FLIPBIT(m_nInReliableState, bit); + + for ( i=0; i<MAX_STREAMS; i++ ) + { + if ( !CheckReceivingList( i ) ) + return false; // error while processing + } + */ + } + + return true; +} + +bool CNetChannel::ProcessPacket( CNetPacket *packet ) +{ + return ProcessMessages( packet->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; +} diff --git a/networksystem/netchannel.h b/networksystem/netchannel.h new file mode 100644 index 0000000..e1a831f --- /dev/null +++ b/networksystem/netchannel.h @@ -0,0 +1,274 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +// Should move to common/networksystem + +#ifndef NETCHANNEL_H +#define NETCHANNEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/bitbuf.h" +#include "tier1/netadr.h" +#include "sm_Protocol.h" +#include "tier1/utlvector.h" +#include "networksystem/inetworksystem.h" +#include "tier1/mempool.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUDPSocket; +class CUtlBuffer; +class CNetPacket; +class CNetChannel; +class INetChannel; + +// 0 == regular, 1 == file stream +enum +{ + FRAG_NORMAL_STREAM = 0, + FRAG_FILE_STREAM, + + MAX_STREAMS +}; + +#define NET_MAX_DATAGRAM_PAYLOAD 1400 +#define NET_MAX_PAYLOAD_BITS 11 // 2^NET_MAX_PALYLOAD_BITS > NET_MAX_PAYLOAD +#define DEFAULT_RATE 10000 +#define SIGNON_TIME_OUT 120.0f +#define CONNECTION_PROBLEM_TIME 15.0f + +#define MAX_RATE 50000 +#define MIN_RATE 100 + +#define FRAGMENT_BITS 8 +#define FRAGMENT_SIZE (1<<FRAGMENT_BITS) +#define MAX_FILE_SIZE_BITS 26 +#define MAX_FILE_SIZE ((1<<MAX_FILE_SIZE_BITS)-1) // maximum transferable size is 64MB + +#define NET_MAX_PAYLOAD 4000 +#define NET_MAX_MESSAGE 4096 +#define MIN_ROUTEABLE_PACKET 16 +#define MAX_ROUTEABLE_PACKET 1400 // Ethernet 1518 - ( CRC + IP + UDP header) +#define UDP_HEADER_SIZE 28 + +// each channel packet has 1 byte of FLAG bits +#define PACKET_FLAG_RELIABLE (1<<0) // packet contains subchannel stream data +#define PACKET_FLAG_CHOKED (1<<1) // packet was choked by sender + + +// shared commands used by all streams, handled by stream layer, TODO + +abstract_class INetworkMessageHandler +{ +public: + virtual void OnConnectionClosing( INetChannel *channel, char const *reason ) = 0; + virtual void OnConnectionStarted( INetChannel *channel ) = 0; + + virtual void OnPacketStarted( int inseq, int outseq ) = 0; + virtual void OnPacketFinished() = 0; + +protected: + virtual ~INetworkMessageHandler() {} +}; + + + +class INetChannelHandler +{ +public: + virtual ~INetChannelHandler( void ) {}; + + virtual void ConnectionStart(INetChannel *chan) = 0; // called first time network channel is established + + virtual void ConnectionClosing(const char *reason) = 0; // network channel is being closed by remote site + + virtual void ConnectionCrashed(const char *reason) = 0; // network error occured + + virtual void PacketStart(int incoming_sequence, int outgoing_acknowledged) = 0; // called each time a new packet arrived + + virtual void PacketEnd( void ) = 0; // all messages has been parsed + + virtual void FileRequested(const char *fileName, unsigned int transferID) = 0; // other side request a file for download + + virtual void FileReceived(const char *fileName, unsigned int transferID) = 0; // we received a file + + virtual void FileDenied(const char *fileName, unsigned int transferID) = 0; // a file request was denied by other side +}; + + +// server to client +class CNetPacket +{ + DECLARE_FIXEDSIZE_ALLOCATOR( CNetPacket ); + +public: + CNetPacket(); + ~CNetPacket(); + + void AddRef(); + void Release(); + +public: + netadr_t m_From; // sender IP + CUDPSocket *m_pSource; // received source + float m_flReceivedTime; // received time + unsigned char *m_pData; // pointer to raw packet data + bf_read m_Message; // easy bitbuf data access + int m_nSizeInBytes; // size in bytes + +private: + int m_nRefCount;// Reference count +}; + + +abstract_class IConnectionlessPacketHandler +{ +public: + virtual bool ProcessConnectionlessPacket( CNetPacket *packet ) = 0; // process a connectionless packet + +protected: + virtual ~IConnectionlessPacketHandler( void ) {}; +}; + +abstract_class ILookupChannel +{ +public: + + virtual INetChannel *FindNetChannel( const netadr_t& from ) = 0; +}; + +// FIXME: Make an INetChannel +class CNetChannel : public INetChannel +{ +public: + explicit CNetChannel(); + ~CNetChannel(); + + // Methods of INetChannel + virtual ConnectionStatus_t GetConnectionState( ); + virtual const netadr_t &GetRemoteAddress( void ) const; + + void Setup( bool serverSide, const netadr_t *remote_address, CUDPSocket *sendSocket, char const *name, INetworkMessageHandler *handler ); + void Reset(); + void Clear(); + void Shutdown( const char *reason ); + + CUDPSocket *GetSocket(); + + void SetDataRate( float rate ); + void SetTimeout( float seconds ); + + bool StartProcessingPacket( CNetPacket *packet ); + bool ProcessPacket( CNetPacket *packet ); + void EndProcessingPacket( CNetPacket *packet ); + + bool CanSendPacket( void ) const; + void SetChoked( void ); // choke a packet + bool HasPendingReliableData( void ); + + // Queues data for sending: + + // send a net message + bool AddNetMsg( INetworkMessage *msg, bool bForceReliable = false ); + + // send a chunk of data + bool AddData( bf_write &msg, bool bReliable = true ); + + // Puts data onto the wire: + + int SendDatagram( bf_write *data ); // Adds data to unreliable payload and then calls transmits the data + bool Transmit( bool onlyReliable = false ); // send data from buffers (calls SendDataGram( NULL ) ) + + bool IsOverflowed( void ) const; + bool IsTimedOut( void ) const; + bool IsTimingOut() const; + +// Info: + + const char *GetName( void ) const; + const char *GetAddress( void ) const; + float GetTimeConnected( void ) const; + float GetTimeSinceLastReceived( void ) const; + int GetDataRate( void ) const; + + float GetLatency( int flow ) const; + float GetAvgLatency( int flow ) const; + float GetAvgLoss( int flow ) const; + float GetAvgData( int flow ) const; + float GetAvgChoke( int flow ) const; + float GetAvgPackets( int flow ) const; + int GetTotalData( int flow ) const; + + void SetConnectionState( ConnectionStatus_t state ); + +private: + int ProcessPacketHeader( bf_read &buf ); + bool ProcessControlMessage( int cmd, bf_read &buf ); + bool ProcessMessages( bf_read &buf ); + + ConnectionStatus_t m_ConnectionState; + +// last send outgoing sequence number + int m_nOutSequenceNr; + // last received incoming sequnec number + int m_nInSequenceNr; + // last received acknowledge outgoing sequnce number + int m_nOutSequenceNrAck; + + // state of outgoing reliable data (0/1) flip flop used for loss detection + int m_nOutReliableState; + // state of incoming reliable data + int m_nInReliableState; + + int m_nChokedPackets; //number of choked packets + int m_PacketDrop; + +// Reliable data buffer, send wich each packet (or put in waiting list) + bf_write m_StreamReliable; + byte m_ReliableDataBuffer[8 * 1024]; // In SP, we don't need much reliable buffer, so save the memory (this is mostly for xbox). + CUtlVector<byte> m_ReliableDataBufferMP; + + // unreliable message buffer, cleared wich each packet + bf_write m_StreamUnreliable; + byte m_UnreliableDataBuffer[NET_MAX_DATAGRAM_PAYLOAD]; + +// don't use any vars below this (only in net_ws.cpp) + + CUDPSocket *m_pSocket; // NS_SERVER or NS_CLIENT index, depending on channel. + int m_StreamSocket; // TCP socket handle + + unsigned int m_MaxReliablePayloadSize; // max size of reliable payload in a single packet + + // Address this channel is talking to. + netadr_t remote_address; + + // For timeouts. Time last message was received. + float last_received; + // Time when channel was connected. + float connect_time; + + // Bandwidth choke + // Bytes per second + int m_Rate; + // If realtime > cleartime, free to send next packet + float m_fClearTime; + + float m_Timeout; // in seconds + + char m_Name[32]; // channel name + +// packet history + // netflow_t m_DataFlow[ MAX_FLOWS ]; + + INetworkMessageHandler *m_MessageHandler; // who registers and processes messages +}; + + +#endif // NETCHANNEL_H
\ No newline at end of file diff --git a/networksystem/networkclient.cpp b/networksystem/networkclient.cpp new file mode 100644 index 0000000..fdd8b04 --- /dev/null +++ b/networksystem/networkclient.cpp @@ -0,0 +1,178 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "networkclient.h" +#include "UDP_Socket.h" +#include "NetChannel.h" +#include "UDP_Process.h" +#include <winsock.h> +#include "tier1/bitbuf.h" +#include "networksystem.h" +#include "sm_protocol.h" + + +//----------------------------------------------------------------------------- +// Construction/Destruction +//----------------------------------------------------------------------------- +CNetworkClient::CNetworkClient() : m_bConnected( false ) +{ + m_pSocket = new CUDPSocket; +} + +CNetworkClient::~CNetworkClient() +{ + delete m_pSocket; +} + + +//----------------------------------------------------------------------------- +// Init/Shutdown +//----------------------------------------------------------------------------- +bool CNetworkClient::Init( int nListenPort ) +{ + if ( !m_pSocket->Init( nListenPort ) ) + { + Warning( "CNetworkClient: Unable to create socket!!!\n" ); + return false; + } + return true; +} + +void CNetworkClient::Shutdown() +{ + m_pSocket->Shutdown(); +} + +#define SPEW_MESSAGES + +#if defined( SPEW_MESSAGES ) +#define SM_SPEW_MESSAGE( code, remote ) \ + Warning( "Message: %s from '%s'\n", #code, remote ); +#else +#define SM_SPEW_MESSAGE( code, remote ) +#endif + +// process a connectionless packet +bool CNetworkClient::ProcessConnectionlessPacket( CNetPacket *packet ) +{ + int code = packet->m_Message.ReadByte(); + switch ( code ) + { + case s2c_connect_accept: + { + SM_SPEW_MESSAGE( s2c_connect_accept, packet->m_From.ToString() ); + + m_NetChan.Setup( false, &packet->m_From, m_pSocket, "client", this ); + m_bConnected = true; + } + break; + default: + { + Warning( "CSmServer::ProcessConnectionlessPacket: Unknown code '%i' from '%s'\n", + code, packet->m_From.ToString() ); + } + break; + } + + return true; +} + +INetChannel *CNetworkClient::FindNetChannel( const netadr_t& from ) +{ + if ( from.CompareAdr( m_NetChan.GetRemoteAddress() ) ) + return &m_NetChan; + return NULL; +} + +void CNetworkClient::ReadPackets( void ) +{ + UDP_ProcessSocket( m_pSocket, this, this ); +} + +void CNetworkClient::SendUpdate() +{ + if ( m_bConnected ) + { + m_NetChan.SendDatagram( NULL ); + } +} + + +void CNetworkClient::Disconnect() +{ + if ( !m_bConnected ) + return; + + byte data[ 512 ]; + bf_write buf( "CNetworkClient::Connect", data, sizeof( data ) ); + + WriteSystemNetworkMessage( buf, net_disconnect ); + buf.WriteString( "client disconnected" ); + + m_NetChan.SendDatagram( &buf ); + m_NetChan.SendDatagram( &buf ); + m_NetChan.SendDatagram( &buf ); +} + +bool CNetworkClient::Connect( char const *server, int port /*=SM_SERVER_PORT*/ ) +{ + byte data[ 512 ]; + bf_write buf( "CNetworkClient::Connect", data, sizeof( data ) ); + + buf.WriteLong( -1 ); + buf.WriteByte( c2s_connect ); + + netadr_t remote; + remote.type = NA_IP; + remote.port = htons( (unsigned short)port ); + + // Resolve remote name + sockaddr sa; + if ( !Q_stricmp( server, "localhost" ) ) + { + remote.ip[ 0 ] = 127; + remote.ip[ 1 ] = 0; + remote.ip[ 2 ] = 0; + remote.ip[ 3 ] = 1; + } + else + { + if ( !g_pNetworkSystemImp->StringToSockaddr( server, &sa ) ) + { + Msg( "Unable to resolve '%s'\n", server ); + return false; + } + + remote.SetFromSockadr( &sa ); + } + + m_NetChan.SetConnectionState( CONNECTION_STATE_CONNECTING ); + + return m_pSocket->SendTo( remote, buf.GetData(), buf.GetNumBytesWritten() ); +} + +void CNetworkClient::OnConnectionClosing( INetChannel *channel, char const *reason ) +{ + Warning( "OnConnectionClosing '%s'\n", reason ); + if ( channel == &m_NetChan ) + { + m_NetChan.Shutdown( "received disconnect\n" ); + m_bConnected = false; + } +} + +void CNetworkClient::OnConnectionStarted( INetChannel *channel ) +{ + Warning( "OnConnectionStarted\n" ); +} + +void CNetworkClient::OnPacketStarted( int inseq, int outseq ) +{ +} + +void CNetworkClient::OnPacketFinished() +{ +}
\ No newline at end of file diff --git a/networksystem/networkclient.h b/networksystem/networkclient.h new file mode 100644 index 0000000..40f3467 --- /dev/null +++ b/networksystem/networkclient.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef CLIENT_H +#define CLIENT_H +#ifdef _WIN32 +#pragma once +#endif + +#include "NetChannel.h" + +class CNetworkClient : public IConnectionlessPacketHandler, public INetworkMessageHandler, public ILookupChannel +{ +public: + CNetworkClient(); + virtual ~CNetworkClient(); + + bool Init( int nListenPort ); + void Shutdown(); + + bool Connect( const char *server, int port ); + void Disconnect(); + + // IConnectionlessPacketHandler + virtual bool ProcessConnectionlessPacket( CNetPacket *packet ); // process a connectionless packet + + // INetworkMessageHandler + virtual void OnConnectionClosing( INetChannel *channel, char const *reason ); + virtual void OnConnectionStarted( INetChannel *channel ); + + virtual void OnPacketStarted( int inseq, int outseq ); + virtual void OnPacketFinished(); + + // ILookupChannel + virtual INetChannel *FindNetChannel( const netadr_t& from ) ; + + INetChannel *GetNetChannel() + { + return &m_NetChan; + } + + void ReadPackets(); + void SendUpdate(); + + CUDPSocket *m_pSocket; + + CNetChannel m_NetChan; + bool m_bConnected; +}; + +#endif // CLIENT_H diff --git a/networksystem/networkserver.cpp b/networksystem/networkserver.cpp new file mode 100644 index 0000000..9951e6c --- /dev/null +++ b/networksystem/networkserver.cpp @@ -0,0 +1,224 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "networkserver.h" +#include "networksystem.h" +#include "icvar.h" +#include "filesystem.h" +#include "UDP_Socket.h" +#include "sm_protocol.h" +#include "NetChannel.h" +#include "UDP_Process.h" +#include <winsock.h> +#include "networkclient.h" + + + +//----------------------------------------------------------------------------- +// +// Implementation of CPlayer +// +//----------------------------------------------------------------------------- +CNetworkServer::CNetworkServer( ) +{ + m_pSocket = new CUDPSocket; +} + +CNetworkServer::~CNetworkServer() +{ + delete m_pSocket; +} + +bool CNetworkServer::Init( int nServerPort ) +{ + if ( !m_pSocket->Init( nServerPort ) ) + { + Warning( "CNetworkServer: Unable to create socket!!!\n" ); + return false; + } + + return true; +} + +void CNetworkServer::Shutdown() +{ + m_pSocket->Shutdown(); +} + +CNetChannel *CNetworkServer::FindNetChannel( const netadr_t& from ) +{ + CPlayer *pl = FindPlayerByAddress( from ); + if ( pl ) + return &pl->m_NetChan; + return NULL; +} + +CPlayer *CNetworkServer::FindPlayerByAddress( const netadr_t& adr ) +{ + int c = m_Players.Count(); + for ( int i = 0; i < c; ++i ) + { + CPlayer *player = m_Players[ i ]; + if ( player->GetRemoteAddress().CompareAdr( adr ) ) + return player; + } + return NULL; +} + +CPlayer *CNetworkServer::FindPlayerByNetChannel( INetChannel *chan ) +{ + int c = m_Players.Count(); + for ( int i = 0; i < c; ++i ) + { + CPlayer *player = m_Players[ i ]; + if ( &player->m_NetChan == chan ) + return player; + } + return NULL; +} + +#define SPEW_MESSAGES + +#if defined( SPEW_MESSAGES ) +#define SM_SPEW_MESSAGE( code, remote ) \ + Warning( "Message: %s from '%s'\n", #code, remote ); +#else +#define SM_SPEW_MESSAGE( code, remote ) +#endif +// process a connectionless packet +bool CNetworkServer::ProcessConnectionlessPacket( CNetPacket *packet ) +{ + int code = packet->m_Message.ReadByte(); + switch ( code ) + { + case c2s_connect: + { + SM_SPEW_MESSAGE( c2s_connect, packet->m_From.ToString() ); + + CPlayer *pl = FindPlayerByAddress( packet->m_From ); + if ( pl ) + { + Warning( "Player already exists for %s\n", packet->m_From.ToString() ); + } + else + { + // Creates the connection + pl = new CPlayer( this, packet->m_From ); + m_Players.AddToTail( pl ); + + // Now send the conn accepted message + AcceptConnection( packet->m_From ); + } + } + break; + default: + { + Warning( "CNetworkServer::ProcessConnectionlessPacket: Unknown code '%i' from '%s'\n", + code, packet->m_From.ToString() ); + } + break; + } + + return true; +} + + +void CNetworkServer::AcceptConnection( const netadr_t& remote ) +{ + byte data[ 512 ]; + bf_write buf( "CNetworkServer::AcceptConnection", data, sizeof( data ) ); + + buf.WriteLong( -1 ); + buf.WriteByte( s2c_connect_accept ); + + m_pSocket->SendTo( remote, buf.GetData(), buf.GetNumBytesWritten() ); +} + +void CNetworkServer::ReadPackets( void ) +{ + UDP_ProcessSocket( m_pSocket, this, this ); + + int c = m_Players.Count(); + for ( int i = c - 1; i >= 0 ; --i ) + { + if ( m_Players[ i ]->m_bMarkedForDeletion ) + { + CPlayer *pl = m_Players[ i ]; + m_Players.Remove( i ); + delete pl; + } + } +} + +void CNetworkServer::SendUpdates() +{ + int c = m_Players.Count(); + for ( int i = 0; i < c; ++i ) + { + m_Players[ i ]->SendUpdate(); + } +} + +void CNetworkServer::OnConnectionStarted( INetChannel *pChannel ) +{ + // Create a network event + NetworkConnectionEvent_t *pConnection = g_pNetworkSystemImp->CreateNetworkEvent< NetworkConnectionEvent_t >( ); + pConnection->m_nType = NETWORK_EVENT_CONNECTED; + pConnection->m_pChannel = pChannel; +} + +void CNetworkServer::OnConnectionClosing( INetChannel *pChannel, char const *reason ) +{ + Warning( "OnConnectionClosing '%s'\n", reason ); + + CPlayer *pPlayer = FindPlayerByNetChannel( pChannel ); + if ( pPlayer ) + { + pPlayer->Shutdown(); + } + + // Create a network event + NetworkDisconnectionEvent_t *pDisconnection = g_pNetworkSystemImp->CreateNetworkEvent< NetworkDisconnectionEvent_t >( ); + pDisconnection->m_nType = NETWORK_EVENT_DISCONNECTED; + pDisconnection->m_pChannel = pChannel; +} + +void CNetworkServer::OnPacketStarted( int inseq, int outseq ) +{ +} + +void CNetworkServer::OnPacketFinished() +{ +} + + +//----------------------------------------------------------------------------- +// +// Implementation of CPlayer +// +//----------------------------------------------------------------------------- +CPlayer::CPlayer( CNetworkServer *server, netadr_t& remote ) : + m_bMarkedForDeletion( false ) +{ + m_NetChan.Setup( true, &remote, server->m_pSocket, "player", server ); +} + +void CPlayer::Shutdown() +{ + m_bMarkedForDeletion = true; + m_NetChan.Shutdown( "received disconnect\n" ); +} + +void CPlayer::SendUpdate() +{ + if ( m_NetChan.CanSendPacket() ) + { + m_NetChan.SendDatagram( NULL ); + } +} + + + diff --git a/networksystem/networkserver.h b/networksystem/networkserver.h new file mode 100644 index 0000000..bd88b5b --- /dev/null +++ b/networksystem/networkserver.h @@ -0,0 +1,86 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef NETWORKSERVER_H +#define NETWORKSERVER_H + +#include "networksystem/inetworksystem.h" +#include "netchannel.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CPlayer; + + +//----------------------------------------------------------------------------- +// Server class +//----------------------------------------------------------------------------- +class CNetworkServer : public IConnectionlessPacketHandler, public INetworkMessageHandler, public ILookupChannel +{ +public: + CNetworkServer( ); + virtual ~CNetworkServer(); + + bool Init( int nServerPort ); + void Shutdown(); + + // IConnectionlessPacketHandler + virtual bool ProcessConnectionlessPacket( CNetPacket *packet ); // process a connectionless packet + + // INetworkMessageHandler + virtual void OnConnectionClosing( INetChannel *channel, char const *reason ); + virtual void OnConnectionStarted( INetChannel *channel ); + + virtual void OnPacketStarted( int inseq, int outseq ); + virtual void OnPacketFinished(); + + // ILookupChannel + virtual CNetChannel *FindNetChannel( const netadr_t& from ) ; + + void ReadPackets(); + void SendUpdates(); + void AcceptConnection( const netadr_t& remote ); + + CPlayer *FindPlayerByAddress( const netadr_t& adr ); + CPlayer *FindPlayerByNetChannel( INetChannel *chan ); + + CUDPSocket *m_pSocket; + + CUtlVector< CPlayer * > m_Players; +}; + + +//----------------------------------------------------------------------------- +// Represents a connected player to the server +//----------------------------------------------------------------------------- +class CPlayer +{ +public: + CPlayer( CNetworkServer *server, netadr_t& remote ); + + void SendUpdate(); + + const netadr_t &GetRemoteAddress() + { + return m_NetChan.GetRemoteAddress(); + } + + char const *GetName() + { + return "Foozle"; + } + + void Shutdown(); + + CNetChannel m_NetChan; + bool m_bMarkedForDeletion; +}; + + +#endif // NETWORKSERVER_H + diff --git a/networksystem/networksystem.cpp b/networksystem/networksystem.cpp new file mode 100644 index 0000000..b8650ec --- /dev/null +++ b/networksystem/networksystem.cpp @@ -0,0 +1,491 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "networksystem.h" +#include "filesystem.h" +#include "UDP_Socket.h" +#include "sm_protocol.h" +#include "NetChannel.h" +#include "UDP_Process.h" +#include <winsock.h> +#include "networkclient.h" +#include "networkserver.h" +#include "networksystem/inetworkmessage.h" +#include "mathlib/mathlib.h" +#include "tier2/tier2.h" + + +//----------------------------------------------------------------------------- +// Singleton instance +//----------------------------------------------------------------------------- +static CNetworkSystem g_NetworkSystem; +CNetworkSystem *g_pNetworkSystemImp = &g_NetworkSystem; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CNetworkSystem, INetworkSystem, + NETWORKSYSTEM_INTERFACE_VERSION, g_NetworkSystem ); + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CNetworkSystem::CNetworkSystem() +{ + m_bWinsockInitialized = false; + m_bNetworkEventCreated = false; + m_bInMidPacket = false; + m_pServer = NULL; + m_pClient = NULL; + m_nGroupBits = 1; + m_nTypeBits = LargestPowerOfTwoLessThanOrEqual( net_num_messages ); +} + +CNetworkSystem::~CNetworkSystem() +{ +} + + +//----------------------------------------------------------------------------- +// Initialization, shutdown +//----------------------------------------------------------------------------- +InitReturnVal_t CNetworkSystem::Init() +{ + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + // initialize winsock 2.0 + WSAData wsaData; + + if ( WSAStartup( MAKEWORD(2,0), &wsaData ) != 0 ) + { + Warning( "Error! Failed to load network socket library.\n"); + return INIT_OK; + } + else + { + m_bWinsockInitialized = true; + } + + LPHOSTENT lp = gethostbyname("localhost"); + if ( !lp ) + { + Warning( "Error! Failed to query local host info\n"); + return INIT_OK; + } + m_LocalHostName = lp->h_name; + + lp = gethostbyname( m_LocalHostName ); + if ( !lp ) + { + Warning( "Error! Failed to query local host info\n"); + return INIT_OK; + } + + sockaddr ip; + m_LocalAddressString = inet_ntoa( *((in_addr*)(lp->h_addr_list[0])) ); + StringToSockaddr( m_LocalAddressString, &ip ); + m_LocalAddress.SetFromSockadr( &ip ); + return INIT_OK; +} + +void CNetworkSystem::Shutdown() +{ + if ( m_bWinsockInitialized ) + { + WSACleanup(); + } + CleanupNetworkMessages(); + BaseClass::Shutdown(); +} + + +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CNetworkSystem::Connect( CreateInterfaceFn factory ) +{ + if ( !BaseClass::Connect( factory ) ) + return false; + + if ( !g_pFullFileSystem ) + { + Warning( "The network system requires the filesystem to run!\n" ); + return false; + } + + return INIT_OK; +} + + +//----------------------------------------------------------------------------- +// Returns the current time +//----------------------------------------------------------------------------- +float CNetworkSystem::GetTime( void ) +{ + return Plat_FloatTime(); +} + + +//----------------------------------------------------------------------------- +// Installs network message factories to be used with all connections +//----------------------------------------------------------------------------- +bool CNetworkSystem::RegisterMessage( INetworkMessage *pMessage ) +{ + if ( m_pServer || m_pClient ) + { + Warning( "Cannot register messages after connection has started.\n" ); + return false; + } + + if ( pMessage->GetGroup() == 0 ) + { + Warning( "Network message group 0 is reserved by the network system.\n" ); + return false; + } + + // Look for already registered message + if ( m_NetworkMessages.Find( pMessage ) >= 0 ) + return false; + + // Allocate more space in messages + int nGroupBits = LargestPowerOfTwoLessThanOrEqual( pMessage->GetGroup() ); + int nTypeBits = LargestPowerOfTwoLessThanOrEqual( pMessage->GetType() ); + m_nGroupBits = max( nGroupBits, m_nGroupBits ); + m_nTypeBits = max( nTypeBits, m_nTypeBits ); + + m_NetworkMessages.AddToTail( pMessage ); + return true; +} + +void CNetworkSystem::CleanupNetworkMessages( ) +{ + int nCount = m_NetworkMessages.Count(); + for ( int i = 0; i < nCount; ++i ) + { + m_NetworkMessages[i]->Release(); + } + + m_NetworkMessages.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Finds a network message given a particular message type +//----------------------------------------------------------------------------- +INetworkMessage* CNetworkSystem::FindNetworkMessage( int group, int type ) +{ + int nCount = m_NetworkMessages.Count(); + for (int i=0; i < nCount; i++ ) + { + if ( ( m_NetworkMessages[i]->GetGroup() == group ) && ( m_NetworkMessages[i]->GetType() == type ) ) + return m_NetworkMessages[i]; + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNetworkSystem::StringToSockaddr( const char *s, struct sockaddr *sadr ) +{ + struct hostent *h; + char *colon; + char copy[128]; + + Q_memset (sadr, 0, sizeof(*sadr)); + ((struct sockaddr_in *)sadr)->sin_family = AF_INET; + ((struct sockaddr_in *)sadr)->sin_port = 0; + + Q_strncpy (copy, s, sizeof( copy ) ); + // strip off a trailing :port if present + for (colon = copy ; *colon ; colon++) + if (*colon == ':') + { + *colon = 0; + ((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1)); + } + + if (copy[0] >= '0' && copy[0] <= '9' && Q_strstr( copy, "." ) ) + { + *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy); + } + else + { +// if ( net_nodns ) +// return false; // DNS names disabled + + if ( (h = gethostbyname(copy)) == NULL ) + return false; + + *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the local address +//----------------------------------------------------------------------------- +const char* CNetworkSystem::GetLocalHostName( void ) const +{ + return m_LocalHostName; +} + +const char* CNetworkSystem::GetLocalAddress( void ) const +{ + return m_LocalAddressString; +} + + +//----------------------------------------------------------------------------- +// Start, shutdown a server +//----------------------------------------------------------------------------- +bool CNetworkSystem::StartServer( unsigned short nServerListenPort ) +{ + if ( !m_bWinsockInitialized ) + return false; + + Assert( !m_pServer ); + m_pServer = new CNetworkServer; + return m_pServer->Init( nServerListenPort ); +} + +void CNetworkSystem::ShutdownServer( ) +{ + if ( m_pServer ) + { + m_pServer->Shutdown(); + delete m_pServer; + m_pServer = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Server update +//----------------------------------------------------------------------------- +void CNetworkSystem::ServerReceiveMessages() +{ + if ( m_pServer ) + { + m_pServer->ReadPackets(); + } +} + +void CNetworkSystem::ServerSendMessages() +{ + if ( m_pServer ) + { + m_pServer->SendUpdates(); + } +} + + +//----------------------------------------------------------------------------- +// Start, shutdown a client +//----------------------------------------------------------------------------- +bool CNetworkSystem::StartClient( unsigned short nClientListenPort ) +{ + if ( !m_bWinsockInitialized ) + return false; + + Assert( !m_pClient ); + m_pClient = new CNetworkClient; + return m_pClient->Init( nClientListenPort ); +} + +void CNetworkSystem::ShutdownClient( ) +{ + if ( m_pClient ) + { + m_pClient->Shutdown(); + delete m_pClient; + m_pClient = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Server update +//----------------------------------------------------------------------------- +void CNetworkSystem::ClientReceiveMessages() +{ + if ( m_pClient ) + { + m_pClient->ReadPackets(); + } +} + +void CNetworkSystem::ClientSendMessages() +{ + if ( m_pClient ) + { + m_pClient->SendUpdate(); + } +} + + +//----------------------------------------------------------------------------- +// Server update +//----------------------------------------------------------------------------- +INetChannel* CNetworkSystem::ConnectClientToServer( const char *pServer, int nServerListenPort ) +{ + if ( m_pClient ) + { + if ( m_pClient->Connect( pServer, nServerListenPort ) ) + return m_pClient->GetNetChannel(); + } + return NULL; +} + +void CNetworkSystem::DisconnectClientFromServer( INetChannel* pChannel ) +{ + if ( m_pClient && ( m_pClient->GetNetChannel() == pChannel ) ) + { + m_pClient->Disconnect(); + } +} + + +//----------------------------------------------------------------------------- +// Queues up a network packet +//----------------------------------------------------------------------------- +void CNetworkSystem::EnqueueConnectionlessNetworkPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler ) +{ + int i = m_PacketQueue.AddToTail( ); + + PacketInfo_t& info = m_PacketQueue[i]; + info.m_pPacket = pPacket; + info.m_pHandler = pHandler; + info.m_pNetChannel = NULL; +} + +void CNetworkSystem::EnqueueNetworkPacket( CNetPacket *pPacket, CNetChannel *pNetChannel ) +{ + int i = m_PacketQueue.AddToTail( ); + + PacketInfo_t& info = m_PacketQueue[i]; + info.m_pPacket = pPacket; + info.m_pHandler = NULL; + info.m_pNetChannel = pNetChannel; +} + + +//----------------------------------------------------------------------------- +// Network event iteration helpers +//----------------------------------------------------------------------------- +bool CNetworkSystem::StartProcessingNewPacket() +{ + PacketInfo_t& info = m_PacketQueue[ m_nProcessingPacket ]; + if ( info.m_pHandler ) + { + UDP_ProcessConnectionlessPacket( info.m_pPacket, info.m_pHandler ); + return false; + } + + if ( !info.m_pNetChannel ) + { + // Not an error that may happen during connect or disconnect + Warning( "Sequenced packet without connection from %s\n" , info.m_pPacket->m_From.ToString() ); + return false; + } + + return info.m_pNetChannel->StartProcessingPacket( info.m_pPacket ); +} + +bool CNetworkSystem::AdvanceProcessingNetworkPacket( ) +{ + m_PacketQueue[ m_nProcessingPacket ].m_pPacket->Release(); + m_PacketQueue[ m_nProcessingPacket ].m_pPacket = NULL; + + ++m_nProcessingPacket; + bool bOverflowed = ( m_nProcessingPacket >= m_PacketQueue.Count() ); + if ( bOverflowed ) + { + m_PacketQueue.RemoveAll(); + m_nProcessingPacket = 0; + } + return !bOverflowed; +} + + +//----------------------------------------------------------------------------- +// Network event iteration +//----------------------------------------------------------------------------- +NetworkEvent_t *CNetworkSystem::FirstNetworkEvent( ) +{ + Assert( !m_bInMidPacket && !m_nProcessingPacket ); + m_nProcessingPacket = 0; + m_bInMidPacket = false; + return NextNetworkEvent(); +} + +NetworkEvent_t *CNetworkSystem::NextNetworkEvent( ) +{ + int nPacketCount = m_PacketQueue.Count(); + if ( m_nProcessingPacket >= nPacketCount ) + return NULL; + + while( true ) + { + // Continue processing the packet we're currently on + if ( m_bInMidPacket ) + { + PacketInfo_t& info = m_PacketQueue[ m_nProcessingPacket ]; + while( info.m_pNetChannel->ProcessPacket( info.m_pPacket ) ) + { + Assert( m_bNetworkEventCreated ); + m_bNetworkEventCreated = false; + return (NetworkEvent_t*)m_EventMessageBuffer; + } + info.m_pNetChannel->EndProcessingPacket( info.m_pPacket ); + m_bInMidPacket = false; + + if ( !AdvanceProcessingNetworkPacket() ) + return NULL; + } + + // Keep reading packets until we find one that either generates an event, + // one that encounters a packet we need to process, or we exhaust the event queue + while( !StartProcessingNewPacket() ) + { + bool bOverflowed = !AdvanceProcessingNetworkPacket(); + + if ( m_bNetworkEventCreated ) + { + m_bNetworkEventCreated = false; + return (NetworkEvent_t*)m_EventMessageBuffer; + } + + if ( bOverflowed ) + return NULL; + } + + m_bInMidPacket = true; + } +} + + +//----------------------------------------------------------------------------- +// Network event creation +//----------------------------------------------------------------------------- +NetworkEvent_t* CNetworkSystem::CreateNetworkEvent( int nSizeInBytes ) +{ + Assert( nSizeInBytes <= sizeof( m_EventMessageBuffer ) ); + + // If this assertion fails, it means two or more network events were created + // before the main network event loop had a chance to inform external code + Assert( !m_bNetworkEventCreated ); + m_bNetworkEventCreated = true; + return ( NetworkEvent_t* )m_EventMessageBuffer; +} + +bool CNetworkSystem::IsNetworkEventCreated() +{ + return m_bNetworkEventCreated; +} + diff --git a/networksystem/networksystem.h b/networksystem/networksystem.h new file mode 100644 index 0000000..894393d --- /dev/null +++ b/networksystem/networksystem.h @@ -0,0 +1,172 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef NETWORKSYSTEM_H +#define NETWORKSYSTEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "networksystem/inetworksystem.h" +#include "tier1/utlvector.h" +#include "tier1/bitbuf.h" +#include "sm_protocol.h" +#include "networksystem/inetworkmessage.h" +#include "tier1/netadr.h" +#include "tier1/utlstring.h" +#include "tier2/tier2.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CNetworkServer; +class CNetworkClient; +class IConnectionlessPacketHandler; +class CNetChannel; +enum SystemNetworkMessageType_t; + + +//----------------------------------------------------------------------------- +// Global interfaces +//----------------------------------------------------------------------------- +class CNetworkSystem; +extern CNetworkSystem *g_pNetworkSystemImp; + + +//----------------------------------------------------------------------------- +// Implementation of the network system +//----------------------------------------------------------------------------- +class CNetworkSystem : public CTier2AppSystem< INetworkSystem > +{ + typedef CTier2AppSystem< INetworkSystem > BaseClass; + +public: + // Constructor, destructor + CNetworkSystem(); + virtual ~CNetworkSystem(); + + // Inherited from IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + + // Inherited from INetworkSystem + virtual bool RegisterMessage( INetworkMessage *msg ); + virtual bool StartServer( unsigned short nServerListenPort ); + virtual void ShutdownServer( ); + virtual void ServerReceiveMessages(); + virtual void ServerSendMessages(); + + virtual bool StartClient( unsigned short nClientListenPort ); + virtual void ShutdownClient( ); + virtual void ClientSendMessages(); + virtual void ClientReceiveMessages(); + virtual INetChannel* ConnectClientToServer( const char *pServer, int nServerListenPort ); + virtual void DisconnectClientFromServer( INetChannel* pChan ); + virtual NetworkEvent_t *FirstNetworkEvent( ); + virtual NetworkEvent_t *NextNetworkEvent( ); + virtual const char* GetLocalHostName( void ) const; + virtual const char* GetLocalAddress( void ) const; + + // Methods internal for use in networksystem library + + // Method to allow systems to add network events received + NetworkEvent_t* CreateNetworkEvent( int nSizeInBytes ); + template< class T > T* CreateNetworkEvent(); + bool IsNetworkEventCreated(); + + // Finds a network message given a particular message type + INetworkMessage* FindNetworkMessage( int group, int type ); + + // Returns the number of bits to encode the type + group with + int GetTypeBitCount() const; + int GetGroupBitCount() const; + + // Returns the current time + float GetTime( void ); + + // Converts a string to a socket address + bool StringToSockaddr( const char *s, struct sockaddr *sadr ); + + // Queues up a network packet + void EnqueueConnectionlessNetworkPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler ); + void EnqueueNetworkPacket( CNetPacket *pPacket, CNetChannel *pNetChannel ); + +private: + struct PacketInfo_t + { + CNetPacket *m_pPacket; + IConnectionlessPacketHandler *m_pHandler; + CNetChannel *m_pNetChannel; + }; + + // Network event iteration helpers + bool StartProcessingNewPacket(); + bool AdvanceProcessingNetworkPacket( ); + void CleanupNetworkMessages( ); + + bool m_bWinsockInitialized : 1; + bool m_bNetworkEventCreated : 1; + bool m_bInMidPacket : 1; + int m_nTypeBits; + int m_nGroupBits; + netadr_t m_LocalAddress; + CUtlString m_LocalAddressString; + CUtlString m_LocalHostName; + CNetworkServer *m_pServer; + CNetworkClient *m_pClient; + unsigned char m_EventMessageBuffer[256]; + CUtlVector<PacketInfo_t> m_PacketQueue; + int m_nProcessingPacket; + CUtlVector<INetworkMessage*> m_NetworkMessages; +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +template< class T > +T* CNetworkSystem::CreateNetworkEvent() +{ + // Increase the size of m_EventMessageBuffer if this assertion fails + COMPILE_TIME_ASSERT( sizeof(T) <= sizeof( m_EventMessageBuffer ) ); + return (T*)CreateNetworkEvent( sizeof(T) ); +} + + +//----------------------------------------------------------------------------- +// Returns the number of bits to encode the type + group with +//----------------------------------------------------------------------------- +inline int CNetworkSystem::GetTypeBitCount() const +{ + return m_nTypeBits; +} + +inline int CNetworkSystem::GetGroupBitCount() const +{ + return m_nGroupBits; +} + + +//----------------------------------------------------------------------------- +// Writes a system network message +//----------------------------------------------------------------------------- +inline void WriteSystemNetworkMessage( bf_write &msg, SystemNetworkMessageType_t type ) +{ + msg.WriteUBitLong( net_group_networksystem, g_pNetworkSystemImp->GetGroupBitCount() ); + msg.WriteUBitLong( type, g_pNetworkSystemImp->GetTypeBitCount() ); +} + +inline void WriteNetworkMessage( bf_write &msg, INetworkMessage *pNetworkMessage ) +{ + msg.WriteUBitLong( pNetworkMessage->GetGroup(), g_pNetworkSystemImp->GetGroupBitCount() ); + msg.WriteUBitLong( pNetworkMessage->GetType(), g_pNetworkSystemImp->GetTypeBitCount() ); +} + +#endif // NETWORKSYSTEM_H + diff --git a/networksystem/networksystem.vpc b/networksystem/networksystem.vpc new file mode 100644 index 0000000..5146263 --- /dev/null +++ b/networksystem/networksystem.vpc @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// NETWORKSYSTEM.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $Compiler + { + $PreprocessorDefinitions "$BASE;NETWORKSYSTEM_EXPORTS" + } + + $Linker + { + $AdditionalDependencies "$BASE winmm.lib ws2_32.lib" + } +} + +$Project "Networksystem" +{ + $Folder "Source Files" + { + $File "netchannel.cpp" + $File "netchannel.h" + $File "networkclient.cpp" + $File "networkclient.h" + $File "networkserver.cpp" + $File "networkserver.h" + $File "networksystem.cpp" + $File "networksystem.h" + $File "sm_protocol.h" + $File "udp_process.cpp" + $File "udp_process.h" + $File "udp_socket.cpp" + $File "udp_socket.h" + } + + $Folder "Interface" + { + $File "$SRCDIR\common\networksystem\inetworkmessage.h" + $File "$SRCDIR\common\networksystem\inetworksystem.h" + } + + $Folder "Link Libraries" + { + $Lib tier2 + } +} diff --git a/networksystem/sm_protocol.h b/networksystem/sm_protocol.h new file mode 100644 index 0000000..a4f28f9 --- /dev/null +++ b/networksystem/sm_protocol.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef SM_PROTOCOL_H +#define SM_PROTOCOL_H +#ifdef _WIN32 +#pragma once +#endif + +enum +{ + CONNECTIONLESS_HEADER = 0xffffffff, +}; + +// Connectionless messages +enum +{ + c2s_connect = 1, + + c2s_num_messages +}; + +enum +{ + s2c_connect_accept = 1, + s2c_connect_reject, + + s2c_num_messages +}; + +enum NetworkMessageGroup_t +{ + net_group_networksystem = 0 +}; + +// Networksystem internal messages for use during valid connection +enum SystemNetworkMessageType_t +{ + net_nop = 0, // nop command used for padding + net_disconnect = 1, // disconnect, last message in connection + + net_num_messages +}; + + +#endif // SM_PROTOCOL_H diff --git a/networksystem/udp_process.cpp b/networksystem/udp_process.cpp new file mode 100644 index 0000000..92d08c5 --- /dev/null +++ b/networksystem/udp_process.cpp @@ -0,0 +1,134 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "tier1/utlbuffer.h" +#include "UDP_Socket.h" +#include "NetChannel.h" +#include "sm_protocol.h" +#include "networksystem.h" + + +//----------------------------------------------------------------------------- +// Allocates memory for packets +//----------------------------------------------------------------------------- +static CUtlMemoryPool s_PacketBufferAlloc( NET_MAX_MESSAGE, 8, CUtlMemoryPool::GROW_SLOW ); +DEFINE_FIXEDSIZE_ALLOCATOR( CNetPacket, 32, CUtlMemoryPool::GROW_SLOW ); + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CNetPacket::CNetPacket() +{ + m_pData = reinterpret_cast<byte*>( s_PacketBufferAlloc.Alloc() ); + m_nRefCount = 1; +} + +CNetPacket::~CNetPacket() +{ + s_PacketBufferAlloc.Free( m_pData ); +} + +void CNetPacket::AddRef() +{ + ++m_nRefCount; +} + +void CNetPacket::Release() +{ + if ( --m_nRefCount <= 0 ) + { + delete this; + } +} + +bool UDP_ReceiveDatagram( CUDPSocket *pSocket, CNetPacket* pPacket ) +{ + Assert( pPacket ); + + netadr_t packetFrom; + CUtlBuffer buf( pPacket->m_pData, NET_MAX_MESSAGE ); + if ( pSocket->RecvFrom( packetFrom, buf ) ) + { + pPacket->m_From = packetFrom; + pPacket->m_nSizeInBytes = buf.TellPut(); + return true; + } + return false; +} + +CNetPacket *UDP_GetPacket( CUDPSocket *pSocket ) +{ + CNetPacket *pPacket = new CNetPacket; + + // setup new packet + pPacket->m_From.SetType( NA_IP ); + pPacket->m_From.Clear(); + pPacket->m_flReceivedTime = g_pNetworkSystemImp->GetTime(); + pPacket->m_pSource = pSocket; + pPacket->m_nSizeInBytes = 0; + pPacket->m_Message.SetDebugName("inpacket.message"); + + // then check UDP data + if ( !UDP_ReceiveDatagram( pSocket, pPacket ) ) + { + delete pPacket; + return NULL; + } + + Assert( pPacket->m_nSizeInBytes ); + + // prepare bitbuffer for reading packet with new size + pPacket->m_Message.StartReading( pPacket->m_pData, pPacket->m_nSizeInBytes ); + return pPacket; +} + +void UDP_ProcessSocket( CUDPSocket *pSocket, IConnectionlessPacketHandler *pHandler, ILookupChannel *pLookup ) +{ + CNetPacket *pPacket; + + Assert( pSocket ); + + // Get datagrams from sockets + while ( ( pPacket = UDP_GetPacket ( pSocket ) ) != NULL ) + { + /* + if ( Filter_ShouldDiscard ( pPacket->m_From ) ) // filterning is done by network layer + { + Filter_SendBan( pPacket->m_From ); // tell them we aren't listening... + continue; + } + */ + + // Find the netchan it came from + if ( *(unsigned int *)pPacket->m_pData == CONNECTIONLESS_HEADER ) + { + g_pNetworkSystemImp->EnqueueConnectionlessNetworkPacket( pPacket, pHandler ); + } + else + { + INetChannel *pNetChan = pLookup->FindNetChannel( pPacket->m_From ); + g_pNetworkSystemImp->EnqueueNetworkPacket( pPacket, static_cast<CNetChannel*>( pNetChan ) ); + } + } +} + +void UDP_ProcessConnectionlessPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler ) +{ + // check for connectionless packet (0xffffffff) first + Assert( *(unsigned int *)pPacket->m_pData == CONNECTIONLESS_HEADER ); + + pPacket->m_Message.ReadLong(); // read the -1 + + /* + if ( net_showudp.GetInt() ) + { + Msg("UDP <- %s: sz=%i OOB '%c'\n", info.m_pPacket->m_From.ToString(), pPacket->m_nSizeInBytes, pPacket->m_pData[4] ); + } + */ + + pHandler->ProcessConnectionlessPacket( pPacket ); +} diff --git a/networksystem/udp_process.h b/networksystem/udp_process.h new file mode 100644 index 0000000..73fca50 --- /dev/null +++ b/networksystem/udp_process.h @@ -0,0 +1,21 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef UDP_PROCESS_H +#define UDP_PROCESS_H +#ifdef _WIN32 +#pragma once +#endif + +class CUDPSocket; +class IConnectionlessPacketHandler; +class ILookupChannel; +class CNetPacket; + +void UDP_ProcessSocket( CUDPSocket *socket, IConnectionlessPacketHandler *handler, ILookupChannel *lookup ); +void UDP_ProcessConnectionlessPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler ); + +#endif // UDP_PROCESS_H diff --git a/networksystem/udp_socket.cpp b/networksystem/udp_socket.cpp new file mode 100644 index 0000000..53f2fda --- /dev/null +++ b/networksystem/udp_socket.cpp @@ -0,0 +1,120 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include <winsock.h> +#include "udp_socket.h" +#include "tier1/utlbuffer.h" +#include "tier1/strtools.h" + +class CUDPSocket::CImpl +{ +public: + struct sockaddr_in m_SocketIP; +}; + +CUDPSocket::CUDPSocket( ) : + m_socketIP(), + m_Socket( INVALID_SOCKET ), + m_Port( 0 ), + m_pImpl( new CImpl ) +{ + Q_memset( &m_socketIP, 0, sizeof( m_socketIP ) ); +} + +CUDPSocket::~CUDPSocket() +{ + delete m_pImpl; +} + +bool CUDPSocket::Init( unsigned short nBindToPort ) +{ + m_Port = nBindToPort; + struct sockaddr_in address; + + m_Socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( m_Socket == INVALID_SOCKET ) + return false; + + address = m_pImpl->m_SocketIP; + address.sin_family = AF_INET; + address.sin_port = htons( m_Port ); + address.sin_addr.S_un.S_addr = INADDR_ANY; + + if ( SOCKET_ERROR == bind( m_Socket, ( struct sockaddr * )&address, sizeof( address ) ) ) + { + return false; + } + + // Set to non-blocking i/o + unsigned long value = 1; + if ( SOCKET_ERROR == ioctlsocket( m_Socket, FIONBIO, &value ) ) + { + return false; + } + + return true; +} + +void CUDPSocket::Shutdown() +{ + if ( m_Socket != INVALID_SOCKET ) + { + closesocket( static_cast<unsigned int>( m_Socket )); + } +} + +bool CUDPSocket::RecvFrom( netadr_t& packet_from, CUtlBuffer& data ) +{ + sockaddr from; + int nFromLen = sizeof( from ); + + int nMaxBytesToRead = data.Size() - data.TellPut(); + char *pBuffer = (char*)data.PeekPut(); + + int nBytesRead = recvfrom( m_Socket, pBuffer, nMaxBytesToRead, 0, &from, &nFromLen ); + if ( nBytesRead == SOCKET_ERROR ) + { + int nError = WSAGetLastError(); + if ( nError != WSAEWOULDBLOCK ) + { + Warning( "Socket error '%i'\n", nError ); + } + return false; + } + + packet_from.SetFromSockadr( &from ); + data.SeekPut( CUtlBuffer::SEEK_CURRENT, nBytesRead ); + return true; +} + +bool CUDPSocket::SendTo( const netadr_t &recipient, const CUtlBuffer& data ) +{ + return SendTo( recipient, (const byte *)data.Base(), (size_t)data.TellPut() ); +} + +bool CUDPSocket::SendTo( const netadr_t &recipient, const byte *data, size_t datalength ) +{ + sockaddr dest; + recipient.ToSockadr( &dest ); + + // Send data + int bytesSent = sendto + ( + m_Socket, + (const char *)data, + (int)datalength, + 0, + reinterpret_cast< const sockaddr * >( &dest ), + sizeof( dest ) + ); + + if ( SOCKET_ERROR == bytesSent ) + { + return false; + } + + return true; +} diff --git a/networksystem/udp_socket.h b/networksystem/udp_socket.h new file mode 100644 index 0000000..8f7c88a --- /dev/null +++ b/networksystem/udp_socket.h @@ -0,0 +1,45 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef UDP_SOCKET_H +#define UDP_SOCKET_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/netadr.h" +#include "tier0/basetypes.h" + +class CUtlBuffer; + +// Creates a non-blocking UPD socket +class CUDPSocket +{ +public: + CUDPSocket(); + ~CUDPSocket(); + + bool Init( unsigned short bindToPort ); + void Shutdown(); + + bool RecvFrom( netadr_t& packet_from, CUtlBuffer& data ); + bool SendTo( const netadr_t &recipient, const CUtlBuffer& data ); + bool SendTo( const netadr_t &recipient, const byte *data, size_t datalength ); + + bool IsValid() const { return m_Socket != 0; } + +protected: + bool CreateSocket (void); + + class CImpl; + CImpl *m_pImpl; + + netadr_t m_socketIP; + unsigned int m_Socket; + unsigned short m_Port; +}; + +#endif // UDP_SOCKET_H |