csgo-2018-source/matchmaking/x360_netmgr.cpp
2021-07-24 21:11:47 -07:00

733 lines
21 KiB
C++

//===== 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