|
|
//===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "mm_framework.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifdef _X360
#include "tier0/memdbgoff.h"
#include "fmtstr.h"
#include "protocol.h"
#include "proto_oob.h"
#include "netmessages.h"
#ifndef NETMSG_TYPE_BITS
#define NETMSG_TYPE_BITS 6
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar mm_net_channel_timeout( "mm_net_channel_timeout", "100", FCVAR_DEVELOPMENTONLY );
//
// Network manager for Xbox 360 network layer
//
CX360NetworkMgr::CX360NetworkMgr( IX360NetworkEvents *pListener, INetSupport::NetworkSocket_t eSocket ) : m_pListener( pListener ), m_eSocket( eSocket ), m_arrConnections( DefLessFunc( XUID ) ), m_eState( STATE_IDLE ), m_eUpdateResult( UPDATE_SUCCESS ) { }
void CX360NetworkMgr::SetListener( IX360NetworkEvents *pListener ) { if ( m_eState == STATE_UPDATING ) { DevMsg( "CX360NetworkMgr::SetListener during network update...\n" ); if ( m_eUpdateResult == UPDATE_SUCCESS ) m_eUpdateResult = UPDATE_LISTENER_CHANGED; }
// We allow to set listener, we will just return the change
// from inside the update loop
m_pListener = pListener; }
bool CX360NetworkMgr::IsUpdating() const { return m_eState != STATE_IDLE; }
CX360NetworkMgr::UpdateResult_t CX360NetworkMgr::Update() { if ( !m_pListener ) return UPDATE_SUCCESS;
Assert( m_eState == STATE_IDLE ); if ( m_eState != STATE_IDLE ) { DevWarning( "CX360NetworkMgr::Update when not idle! (state = %d)\n", m_eState ); return UPDATE_SUCCESS; }
m_eState = STATE_UPDATING; m_eUpdateResult = UPDATE_SUCCESS;
g_pMatchExtensions->GetINetSupport()->ProcessSocket( m_eSocket, this );
//
// Transmit all our active channels
//
for ( int idx = m_arrConnections.FirstInorder(); idx != m_arrConnections.InvalidIndex(); ) { XUID xuidKey = m_arrConnections.Key( idx ); ConnectionInfo_t const ci = m_arrConnections.Element( idx ); // get a copy
idx = m_arrConnections.NextInorder( idx );
if ( !xuidKey && m_arrConnections.Count() > 1 ) // skip host designation record if there are other records
// there should be an alias for host too
continue;
// If it is a secure connection in LOST state, then shut it down right away
if ( ci.m_xnaddr.inaOnline.s_addr ) { if ( g_pMatchExtensions->GetIXOnline()->XNetGetConnectStatus( ci.m_inaddr ) == XNET_CONNECT_STATUS_LOST ) { DevWarning( "CX360NetworkMgr::Update - Net channel %p is LOST!\n", ci.m_pNetChannel ); ConnectionPeerClose( ci.m_xuid );
// Notify listener
if ( m_pListener ) { m_pListener->OnX360NetDisconnected( ci.m_xuid ); }
continue; } }
if ( ci.m_pNetChannel->IsTimedOut() ) { DevWarning( "CX360NetworkMgr::Update - Net channel %p is timed out (%.1f s)!\n", ci.m_pNetChannel, mm_net_channel_timeout.GetFloat() ); ConnectionPeerClose( ci.m_xuid );
// Notify listener
if ( m_pListener ) { m_pListener->OnX360NetDisconnected( ci.m_xuid ); }
continue; }
ci.m_pNetChannel->Transmit(); }
bool bSelfDestroy = ( m_eState == STATE_DESTROY_DEFERRED ); m_eState = STATE_IDLE;
if ( bSelfDestroy ) { Destroy(); return UPDATE_DESTROYED; }
return m_eUpdateResult; }
void CX360NetworkMgr::Destroy() { // If we are being destroyed in the middle of update loop, then defer the actual destruction
if ( m_eState == STATE_UPDATING ) { DevMsg( "CX360NetworkMgr::Destroy is deferred...\n" ); m_pListener = NULL; m_eState = STATE_DESTROY_DEFERRED; return; } Assert( m_eState == STATE_IDLE ); DevMsg( "CX360NetworkMgr::Destroy is deleting this object [%p].\n", this );
// Shutdown all the connections with peers
while ( m_arrConnections.Count() > 0 ) { ConnectionInfo_t const &ci = m_arrConnections.Element( m_arrConnections.FirstInorder() ); XUID xuidRemote = ci.m_xuid; ConnectionPeerClose( xuidRemote ); }
delete this; }
void CX360NetworkMgr::DebugPrint() { DevMsg( "CX360NetworkMgr\n" ); DevMsg( " state: %d\n", m_eState ); DevMsg( " connections: %d\n", m_arrConnections.Count() ); for ( int k = m_arrConnections.FirstInorder(), j = 0; k != m_arrConnections.InvalidIndex(); ++j, k = m_arrConnections.NextInorder( k ) ) { ConnectionInfo_t const *ci; ci = &m_arrConnections.Element( k ); DevMsg( " connection%d: %llx [%llx] = channel %p\n", j, m_arrConnections.Key( k ), ci->m_xuid, ci->m_pNetChannel ); } }
//
// Declaration of the X360 network message
//
class CX360NetworkMessageBase : public CNetMessage { public: virtual bool ReadFromBuffer( bf_read &buffer ) { Assert( 0 ); return false; } virtual bool WriteToBuffer( bf_write &buffer ) const { Assert( 0 ); return false; } virtual const char *ToString() const { return GetName(); }
virtual int GetType() const { return 20; } // should fit in 5 bits
virtual const char *GetName() const { return "CX360NetworkMessage";} virtual size_t GetSize() const { return sizeof( *this ); } };
class CX360NetworkMessageSend : public CX360NetworkMessageBase { public: explicit CX360NetworkMessageSend( KeyValues *pData ) : m_pData( pData ) {}
virtual bool WriteToBuffer( bf_write &buffer ) const;
public: KeyValues *m_pData; };
class CX360NetworkMessageRecv : public CX360NetworkMessageBase { public: explicit CX360NetworkMessageRecv( CX360NetworkMgr *pMgr ) : m_pMgr( pMgr ), m_pData( NULL ) {} ~CX360NetworkMessageRecv();
virtual bool ReadFromBuffer( bf_read &buffer ); virtual bool Process();
public: CX360NetworkMgr *m_pMgr; KeyValues *m_pData;
struct GrowStorage_t { GrowStorage_t() : m_nBytes( 0 ), m_pbData( NULL ) {} ~GrowStorage_t() { delete [] m_pbData; m_nBytes = 0; m_pbData = NULL; } void Grow( int nBytes ) { if ( m_nBytes < nBytes ) { delete [] m_pbData; m_pbData = new unsigned char[ m_nBytes = nBytes ]; } }
int m_nBytes; unsigned char *m_pbData; }; GrowStorage_t m_gsKeyValues; GrowStorage_t m_gsBinaryData; };
bool CX360NetworkMessageSend::WriteToBuffer( bf_write &buffer ) const { buffer.WriteUBitLong( GetType(), NETMSG_TYPE_BITS );
buffer.WriteLong( g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() );
CUtlBuffer bufData; bufData.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() ); m_pData->WriteAsBinary( bufData );
buffer.WriteLong( bufData.TellMaxPut() ); buffer.WriteBytes( bufData.Base(), bufData.TellMaxPut() ); if ( KeyValues *kvBinary = m_pData->FindKey( "binary" ) ) { void *pvData = kvBinary->GetPtr( "ptr", NULL ); int nSize = kvBinary->GetInt( "size", 0 );
if ( pvData && nSize ) { buffer.WriteLong( nSize ); if ( !buffer.WriteBytes( pvData, nSize ) ) return false; } else { buffer.WriteLong( 0 ); } } else { buffer.WriteLong( 0 ); }
return !buffer.IsOverflowed(); }
bool CX360NetworkMessageRecv::ReadFromBuffer( bf_read &buffer ) { if ( buffer.ReadLong() != g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() ) return false;
int nDataLen = buffer.ReadLong(); if ( nDataLen <= 0 ) return false;
m_gsKeyValues.Grow( nDataLen ); if ( !buffer.ReadBytes( m_gsKeyValues.m_pbData, nDataLen ) ) return false;
if ( !m_pData ) m_pData = new KeyValues( "" ); else m_pData->Clear();
CUtlBuffer bufData( m_gsKeyValues.m_pbData, nDataLen, CUtlBuffer::READ_ONLY ); bufData.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() ); if ( !m_pData->ReadAsBinary( bufData ) ) { m_pData->Clear(); return false; }
int nBinaryBytes = buffer.ReadLong(); if ( nBinaryBytes > 0 ) { m_gsBinaryData.Grow( nBinaryBytes ); if ( !buffer.ReadBytes( m_gsBinaryData.m_pbData, nBinaryBytes ) ) return false;
if ( KeyValues *kvPtr = m_pData->FindKey( "binary/ptr" ) ) kvPtr->SetPtr( "", m_gsBinaryData.m_pbData ); }
return !buffer.IsOverflowed(); }
CX360NetworkMessageRecv::~CX360NetworkMessageRecv() { if ( m_pData ) m_pData->deleteThis(); m_pData = NULL; }
bool CX360NetworkMessageRecv::Process() { m_pMgr->OnConnectionMessage( m_pData ); return true; }
void CX360NetworkMgr::ConnectionMessageHandler_t::ConnectionStart( INetChannel *chan ) { m_pChannel = chan;
CX360NetworkMessageRecv *pMsg = new CX360NetworkMessageRecv( m_pMgr ); m_pChannel->RegisterMessage( pMsg ); }
void CX360NetworkMgr::ConnectionPeerSendMessage( KeyValues *pMsg ) { DevMsg( 2, "[NET] -> ConnectionPeerSendMessage\n" ); KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
for ( int idx = m_arrConnections.FirstInorder(); idx != m_arrConnections.InvalidIndex(); idx = m_arrConnections.NextInorder( idx ) ) { XUID xuidKey = m_arrConnections.Key( idx ); ConnectionInfo_t const &ci = m_arrConnections.Element( idx ); if ( !xuidKey && m_arrConnections.Count() > 1 ) // skip host designation record if there are other records
// there should be an alias for host too
continue;
CX360NetworkMessageSend msg( pMsg ); bool bVoice = !Q_stricmp( pMsg->GetName(), "SysSession::Voice" ); // marks the message as voice-channel-VDP-data
ci.m_pNetChannel->SendNetMsg( msg, msg.IsReliable(), bVoice ); ci.m_pNetChannel->Transmit();
DevMsg( 2, " -> %llx (%p : %s)\n", ci.m_xuid, ci.m_pNetChannel, ci.m_pNetChannel ? ci.m_pNetChannel->GetRemoteAddress().ToString() : "" ); } }
char const * CX360NetworkMgr::ConnectionPeerGetAddress( XUID xuidRemote ) { int idxConn = m_arrConnections.Find( xuidRemote ); if ( idxConn == m_arrConnections.InvalidIndex() ) return NULL;
ConnectionInfo_t const &ci = m_arrConnections.Element( idxConn ); if ( !ci.m_pNetChannel ) return NULL;
return ci.m_pNetChannel->GetRemoteAddress().ToString( true ); }
//
// Network communication implementation
//
void CX360NetworkMgr::ConnectionPeerSendConnectionless( XUID xuidRemote, KeyValues *pMsg ) { // Client should have started an active connection
int idxConn = m_arrConnections.Find( xuidRemote ); if ( idxConn == m_arrConnections.InvalidIndex() ) { DevWarning( "CX360NetworkMgr::ConnectionPeerSendConnectionless( xuidRemote = %llx ) XUID is not registered!\n", xuidRemote ); Assert( 0 ); return; }
DevMsg( 2, "[NET] -> ConnectionPeerSendConnectionless( %llx )\n", xuidRemote ); KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
ConnectionInfo_t const &ci = m_arrConnections.Element( idxConn );
g_pConnectionlessLanMgr->SendPacket( pMsg, ci.m_pNetChannel->GetRemoteAddress().ToString(), m_eSocket ); }
bool CX360NetworkMgr::ProcessConnectionlessPacket( netpacket_t *packet ) { Assert( m_pListener );
// Unpack key values
if ( KeyValues *pMsg = g_pConnectionlessLanMgr->UnpackPacket( packet ) ) { KeyValues::AutoDelete autodelete( pMsg );
DevMsg( 2, "[NET] <- OnX360NetConnectionlessPacket( %s )\n", packet->from.ToString() ); KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
if ( m_pListener && m_pListener->OnX360NetConnectionlessPacket( packet, pMsg ) ) return true; }
// Otherwise this is an unwanted packet, prevent any more packets from this peer
ConnectionPeerClose( packet ); return false; }
void CX360NetworkMgr::OnConnectionMessage( KeyValues *pMsg ) { Assert( m_pListener );
DevMsg( 2, "[NET] <- OnConnectionMessage\n" ); KeyValuesDumpAsDevMsg( pMsg, 1, 2 );
if ( m_pListener ) { m_pListener->OnX360NetPacket( pMsg ); } }
void CX360NetworkMgr::OnConnectionClosing( INetChannel *pNetChannel ) { // This is called in several cases:
// - we called pNetChannel->Shutdown (we wouldn't find this channel then)
// - remote side shut down the channel (we should still find this channel)
// - we crashed while pumping buffered messages (we should still find this channel)
ConnectionInfo_t const *ci = NULL; for ( int idx = m_arrConnections.FirstInorder(); !ci && idx != m_arrConnections.InvalidIndex(); idx = m_arrConnections.NextInorder( idx ) ) { ConnectionInfo_t const &record = m_arrConnections.Element( idx ); if ( record.m_pNetChannel == pNetChannel ) ci = &record; }
if ( !ci ) { // This is a notification from inside Shutdown initiated by us, ignore
return; }
// Otherwise something went wrong with the connection and we
// have no other option, but close it and release secure association
XUID xuidRemote = ci->m_xuid; DevMsg( "CX360NetworkMgr::OnConnectionClosing( xuidRemote = %llx, ip = %s )\n", xuidRemote, pNetChannel->GetRemoteAddress().ToString( true ) ); ConnectionPeerClose( xuidRemote );
// Notify listener
if ( m_pListener ) { m_pListener->OnX360NetDisconnected( xuidRemote ); } }
bool CX360NetworkMgr::ConnectionPeerOpenPassive( XUID xuidRemote, netpacket_t *pktIncoming, XNKID *pxnkidSession /*= NULL*/ ) { // Check if we already have the corresponding connection open, close it if it is open
if ( m_arrConnections.Find( xuidRemote ) != m_arrConnections.InvalidIndex() ) { DevWarning( "CX360NetworkMgr::ConnectionPeerOpenPassive( xuidRemote = %llx, ip = %s ) attempted on a duplicate XUID!\n", xuidRemote, pktIncoming->from.ToString() ); ConnectionPeerClose( xuidRemote );
// Notify listener
if ( m_pListener ) { m_pListener->OnX360NetDisconnected( xuidRemote ); }
DevWarning( "CX360NetworkMgr::ConnectionPeerOpenPassive proceeding after stale duplicate connection closed...\n" ); }
//
// XNetInAddrToXnAddr
//
XNADDR xnaddrRemote = {0}; XNKID xnkidRemote = {0}; IN_ADDR inaddrRemote = {0}; inaddrRemote.s_addr = pktIncoming->from.GetIPNetworkByteOrder(); if ( pxnkidSession ) xnkidRemote = *pxnkidSession; if ( pxnkidSession ) // if ( pxnkidSession && !XNetXnKidIsSystemLink( pxnkidSession ) )
{ if ( int err = g_pMatchExtensions->GetIXOnline()->XNetInAddrToXnAddr( inaddrRemote, &xnaddrRemote, &xnkidRemote ) ) { DevWarning( "CX360NetworkMgr::ConnectionPeerOpenPassive( xuidRemote = %llx, ip = %s ) failed to resolve XNADDR ( code 0x%08X )\n", xuidRemote, pktIncoming->from.ToString(), err ); return false; } } else { xnaddrRemote.ina = inaddrRemote; xnaddrRemote.wPortOnline = pktIncoming->from.GetPort(); } if ( pxnkidSession ) *pxnkidSession = xnkidRemote;
//
// Register the connection internally
//
ConnectionMessageHandler_t *pHandler = new ConnectionMessageHandler_t( this, NULL );
INetChannel *pChannel = g_pMatchExtensions->GetINetSupport()->CreateChannel( m_eSocket, pktIncoming->from, CFmtStr( "MM:%llx", xuidRemote ), pHandler );
ConnectionInfo_t ci; ci.m_xuid = xuidRemote; ci.m_inaddr = inaddrRemote; ci.m_xnaddr = xnaddrRemote; ci.m_pNetChannel = pChannel; ci.m_pHandler = pHandler; m_arrConnections.Insert( xuidRemote, ci ); DevMsg( "CX360NetworkMgr::ConnectionPeerOpenPassive( xuidRemote = %llx, ip = %s ) succeeded (pNetChannel = %p).\n", xuidRemote, pktIncoming->from.ToString(), pChannel ); return true; }
bool CX360NetworkMgr::ConnectionPeerOpenActive( XUID xuidRemote, XSESSION_INFO const &xRemote ) { // Check if we already have the corresponding connection open
if ( m_arrConnections.Find( xuidRemote ) != m_arrConnections.InvalidIndex() ) { DevWarning( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx ) attempted on a duplicate XUID!\n", xuidRemote ); Assert( 0 ); return false; }
//
// XNetXnAddrToInAddr
//
XNADDR xnaddrRemote = xRemote.hostAddress; IN_ADDR inaddrRemote; if ( 1 ) // if ( !XNetXnKidIsSystemLink( &xRemote.sessionID ) )
{ if ( int err = g_pMatchExtensions->GetIXOnline()->XNetXnAddrToInAddr( &xRemote.hostAddress, &xRemote.sessionID, &inaddrRemote ) ) { DevWarning( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx ) failed to resolve XNADDR ( code 0x%08X )\n", xuidRemote, err ); return false; }
// Initiate secure connection and key exchange
if ( int err = g_pMatchExtensions->GetIXOnline()->XNetConnect( inaddrRemote ) ) { DevWarning( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx ) failed to start key exchange ( code 0x%08X )\n", xuidRemote, err ); g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( inaddrRemote ); // need to unregister inaddr, otherwise we will leak a secure association
return false; } } else { inaddrRemote = xnaddrRemote.ina; xnaddrRemote.inaOnline.s_addr = 0; }
//
// Register the connection internally
//
netadr_t inetAddr; inetAddr.SetType( NA_IP ); inetAddr.SetIPAndPort( inaddrRemote.s_addr, 0 );
ConnectionMessageHandler_t *pHandler = new ConnectionMessageHandler_t( this, NULL );
INetChannel *pChannel = g_pMatchExtensions->GetINetSupport()->CreateChannel( m_eSocket, inetAddr, CFmtStr( "MM:XNKID:%llx", ( const uint64 & ) xRemote.sessionID ), pHandler );
ConnectionInfo_t ci; ci.m_xuid = xuidRemote; ci.m_inaddr = inaddrRemote; ci.m_xnaddr = xnaddrRemote; ci.m_pNetChannel = pChannel; ci.m_pHandler = pHandler;
m_arrConnections.Insert( xuidRemote, ci );
DevMsg( "CX360NetworkMgr::ConnectionPeerOpenActive( xuidRemote = %llx, xnkid = %llx, ip = %s ) succeeded (pNetChannel = %p), connection %s.\n", xuidRemote, ( const uint64 & ) xRemote.sessionID, inetAddr.ToString( true ), pChannel, XNetXnKidIsSystemLink( &xRemote.sessionID ) ? "LAN" : "SecureXnet" );
// Caller is required to transmit a first connectionless packet on net channel address
// to poke through secure handshaking
pChannel->SetTimeout( mm_net_channel_timeout.GetFloat() ); // pChannel->Transmit();
return true; }
void CX360NetworkMgr::ConnectionPeerUpdateXuid( XUID xuidRemoteOld, XUID xuidRemoteNew ) { Assert( !xuidRemoteOld != !xuidRemoteNew ); // either of the XUIDs must be NULL
int idxOldRecord = m_arrConnections.Find( xuidRemoteOld ); Assert( idxOldRecord != m_arrConnections.InvalidIndex() );
int idxNewRecord = m_arrConnections.Find( xuidRemoteNew ); // Assert( idxNewRecord == m_arrConnections.InvalidIndex() );
if ( idxOldRecord != m_arrConnections.InvalidIndex() && idxNewRecord == m_arrConnections.InvalidIndex() ) { // Adding an alias to a peer network connection:
// - either found out a host xuid after anonymous connect by NULL xuid
// - or another client migrated to become a new host and adding a NULL xuid alias for that client
ConnectionInfo_t &ci = m_arrConnections.Element( idxOldRecord ); ci.m_xuid = xuidRemoteNew ? xuidRemoteNew : xuidRemoteOld;
ConnectionInfo_t const ciAlias = ci; // need to keep a copy on the stack in case insert reallocates memory
m_arrConnections.Insert( xuidRemoteNew, ciAlias );
DevMsg( "CX360NetworkMgr::ConnectionPeerUpdateXuid: xuidRemote = %llx + %llx, ip = %s, pNetChannel = %p.\n", xuidRemoteOld, xuidRemoteNew, ciAlias.m_pNetChannel->GetRemoteAddress().ToString(), ciAlias.m_pNetChannel ); } else if ( idxOldRecord != m_arrConnections.InvalidIndex() && idxNewRecord != m_arrConnections.InvalidIndex() && !xuidRemoteNew ) { // Handling a case when client migrated to become a new host and needs a NULL xuid alias,
// but the old host is still registered in network manager with a NULL xuid alias:
// just overwrite the existing NULL alias to point to the new host
ConnectionInfo_t &ciOld = m_arrConnections.Element( idxOldRecord ); ConnectionInfo_t &ciNew = m_arrConnections.Element( idxNewRecord );
DevMsg( "CX360NetworkMgr::ConnectionPeerUpdateXuid: xuidRemote = %llx + %llx, ip = %s, pNetChannel = %p (alias override for host).\n", xuidRemoteOld, xuidRemoteNew, ciOld.m_pNetChannel->GetRemoteAddress().ToString(), ciOld.m_pNetChannel );
ciNew = ciOld; } else { DevWarning( "CX360NetworkMgr::ConnectionPeerUpdateXuid: ERROR: xuidRemote = %llx (%s) + %llx (%s)!\n", xuidRemoteOld, ( ( idxOldRecord != m_arrConnections.InvalidIndex() ) ? "valid" : "missing" ), xuidRemoteNew, ( ( idxNewRecord != m_arrConnections.InvalidIndex() ) ? "valid" : "missing" ) ); Assert( 0 ); } }
void CX360NetworkMgr::ConnectionPeerClose( netpacket_t *pktIncoming ) { IN_ADDR inaddrRemote; inaddrRemote.s_addr = pktIncoming->from.GetIPNetworkByteOrder();
g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( inaddrRemote ); }
void CX360NetworkMgr::ConnectionPeerClose( XUID xuidRemote ) { //
// Find the connection record
//
int idx = m_arrConnections.Find( xuidRemote ); if ( !m_arrConnections.IsValidIndex( idx ) ) { DevWarning( "CX360NetworkMgr::ConnectionPeerClose( xuidRemote = %llx ) failed: not registered!\n", xuidRemote ); Assert( 0 ); return; }
ConnectionInfo_t const ci = m_arrConnections[ idx ]; // get a copy
//
// Remove the connection record and host identifier if it refers to the same machine
//
m_arrConnections.RemoveAt( idx );
Assert( ci.m_xuid == xuidRemote || !ci.m_xuid || !xuidRemote ); // NULL record identifies host and only it can be duplicated
m_arrConnections.Remove( ci.m_xuid );
int idxHost = m_arrConnections.Find( 0ull ); if ( m_arrConnections.IsValidIndex( idxHost ) ) { ConnectionInfo_t const &ciHost = m_arrConnections.Element( idxHost ); if ( ciHost.m_xuid == xuidRemote ) m_arrConnections.RemoveAt( idxHost ); }
//
// Cleanup resources associated with the connection
//
netadr_t inetAddr = ci.m_pNetChannel->GetRemoteAddress(); ci.m_pNetChannel->Shutdown( "" ); delete ci.m_pHandler; if ( ci.m_xnaddr.inaOnline.s_addr ) { g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( ci.m_inaddr ); }
DevMsg( "CX360NetworkMgr::ConnectionPeerClose: xuidRemote = %llx, ip = %s, pNetChannel = %p.\n", ci.m_xuid, inetAddr.ToString(), ci.m_pNetChannel ); }
#endif
|