1
This commit is contained in:
922
networksystem/netchannel.cpp
Normal file
922
networksystem/netchannel.cpp
Normal file
@ -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;
|
||||
}
|
274
networksystem/netchannel.h
Normal file
274
networksystem/netchannel.h
Normal file
@ -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
|
178
networksystem/networkclient.cpp
Normal file
178
networksystem/networkclient.cpp
Normal file
@ -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()
|
||||
{
|
||||
}
|
54
networksystem/networkclient.h
Normal file
54
networksystem/networkclient.h
Normal file
@ -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
|
224
networksystem/networkserver.cpp
Normal file
224
networksystem/networkserver.cpp
Normal file
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
86
networksystem/networkserver.h
Normal file
86
networksystem/networkserver.h
Normal file
@ -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
|
||||
|
491
networksystem/networksystem.cpp
Normal file
491
networksystem/networksystem.cpp
Normal file
@ -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;
|
||||
}
|
||||
|
172
networksystem/networksystem.h
Normal file
172
networksystem/networksystem.h
Normal file
@ -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
|
||||
|
54
networksystem/networksystem.vpc
Normal file
54
networksystem/networksystem.vpc
Normal file
@ -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
|
||||
}
|
||||
}
|
49
networksystem/sm_protocol.h
Normal file
49
networksystem/sm_protocol.h
Normal file
@ -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
|
134
networksystem/udp_process.cpp
Normal file
134
networksystem/udp_process.cpp
Normal file
@ -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 );
|
||||
}
|
21
networksystem/udp_process.h
Normal file
21
networksystem/udp_process.h
Normal file
@ -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
|
120
networksystem/udp_socket.cpp
Normal file
120
networksystem/udp_socket.cpp
Normal file
@ -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;
|
||||
}
|
45
networksystem/udp_socket.h
Normal file
45
networksystem/udp_socket.h
Normal file
@ -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
|
Reference in New Issue
Block a user