summaryrefslogtreecommitdiff
path: root/networksystem
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /networksystem
downloadarchived-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.cpp922
-rw-r--r--networksystem/netchannel.h274
-rw-r--r--networksystem/networkclient.cpp178
-rw-r--r--networksystem/networkclient.h54
-rw-r--r--networksystem/networkserver.cpp224
-rw-r--r--networksystem/networkserver.h86
-rw-r--r--networksystem/networksystem.cpp491
-rw-r--r--networksystem/networksystem.h172
-rw-r--r--networksystem/networksystem.vpc54
-rw-r--r--networksystem/sm_protocol.h49
-rw-r--r--networksystem/udp_process.cpp134
-rw-r--r--networksystem/udp_process.h21
-rw-r--r--networksystem/udp_socket.cpp120
-rw-r--r--networksystem/udp_socket.h45
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