//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// // // Purpose: // //===========================================================================// #include "mm_netmsgcontroller.h" #include "matchmakingqos.h" #include "proto_oob.h" #include "bitbuf.h" #include "fmtstr.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // // Implementation // CMatchNetworkMsgControllerBase::CMatchNetworkMsgControllerBase() { } CMatchNetworkMsgControllerBase::~CMatchNetworkMsgControllerBase() { } static CMatchNetworkMsgControllerBase g_MatchNetMsgControllerBase; CMatchNetworkMsgControllerBase *g_pMatchNetMsgControllerBase = &g_MatchNetMsgControllerBase; // // A way to copy just value // static void CopyValue( KeyValues *pTo, KeyValues *pFrom ) { switch ( pFrom->GetDataType() ) { case KeyValues::TYPE_INT: pTo->SetInt( "", pFrom->GetInt() ); break; case KeyValues::TYPE_UINT64: pTo->SetUint64( "", pFrom->GetUint64() ); break; case KeyValues::TYPE_STRING: pTo->SetString( "", pFrom->GetString() ); break; default: DevWarning( "NetMsgCtrlr::CopyValue using unknown type!\n" ); Assert( 0 ); break; } } // // Implementation // MM_QOS_t CMatchNetworkMsgControllerBase::GetQOS() { return MM_GetQos(); } KeyValues * CMatchNetworkMsgControllerBase::GetActiveServerGameDetails( KeyValues *pRequest ) { // Query server info INetSupport::ServerInfo_t si; g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si ); KeyValues *pDetails = NULL; if ( si.m_bActive ) { MEM_ALLOC_CREDIT(); // // Parse the game details from the values // pDetails = KeyValues::FromString( "GameDetailsServer", " system { " " network LIVE " " access public " " } " " server { " " name = " " server = " " adronline = " " adrlocal = " " } " " members { " " numSlots #int#0 " " numPlayers #int#0 " " } " ); // // For a listen server and other MM session overlay the session settings // if ( !si.m_bDedicated && g_pMatchFramework->GetMatchSession() ) { pDetails->MergeFrom( g_pMatchFramework->GetMatchSession()->GetSessionSettings(), KeyValues::MERGE_KV_BORROW ); } // // Get server information // pDetails->SetString( "server/name", si.m_szServerName ); pDetails->SetString( "server/server", si.m_bDedicated ? "dedicated" : "listen" ); pDetails->SetString( "server/adronline", si.m_netAdrOnline.ToString() ); pDetails->SetString( "server/adrlocal", si.m_netAdr.ToString() ); if ( si.m_bDedicated && si.m_bLobbyExclusive && si.m_bGroupExclusive ) pDetails->SetString( "system/access", "friends" ); si.m_numMaxHumanPlayers = ClampArrayBounds( si.m_numMaxHumanPlayers, g_pMMF->GetMatchTitle()->GetTotalNumPlayersSupported() ); pDetails->SetInt( "members/numSlots", si.m_numMaxHumanPlayers ); si.m_numHumanPlayers = ClampArrayBounds( si.m_numHumanPlayers, si.m_numMaxHumanPlayers ); pDetails->SetInt( "members/numPlayers", si.m_numHumanPlayers ); static ConVarRef host_info_show( "host_info_show" ); if ( host_info_show.GetInt() < 2 ) pDetails->SetString( "options/action", "crypt" ); } else if ( IVEngineClient *pIVEngineClient = g_pMatchExtensions->GetIVEngineClient() ) { if ( pIVEngineClient->IsLevelMainMenuBackground() ) return NULL; char const *szLevelName = pIVEngineClient->GetLevelNameShort(); if ( !szLevelName || !*szLevelName ) return NULL; MEM_ALLOC_CREDIT(); pDetails = new KeyValues( "GameDetailsClient" ); } if ( !pDetails ) return NULL; // Allow title to add game-specific settings g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendServerDetails( pDetails, pRequest ); return pDetails; } static KeyValues * GetLobbyDetailsTemplate( char const *szReason = "", KeyValues *pSettings = NULL ) { KeyValues *pDetails = KeyValues::FromString( "settings", " system { " " network #empty# " " access #empty# " " netflag #empty# " " lock #empty# " " } " " options { " " server #empty# " " } " " members { " " numSlots #int#0 " " numPlayers #int#0 " " } " ); g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendLobbyDetailsTemplate( pDetails, szReason, pSettings ); return pDetails; } KeyValues * CMatchNetworkMsgControllerBase::UnpackGameDetailsFromQOS( MM_GameDetails_QOS_t const *pvQosReply ) { // // Check if we have correct header // CUtlBuffer bufQos( pvQosReply->m_pvData, pvQosReply->m_numDataBytes, CUtlBuffer::READ_ONLY ); bufQos.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() ); int iProtocol = bufQos.GetInt(); int iVersion = bufQos.GetInt(); if ( iProtocol != g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() ) return NULL; if ( 0 != iVersion ) return NULL; // // Read the game details that we have received // MEM_ALLOC_CREDIT(); KeyValues *pDetails = new KeyValues( "" ); if ( !pDetails->ReadAsBinary( bufQos ) ) { pDetails->deleteThis(); return NULL; } // Read the terminator int iTerm = bufQos.GetInt(); if ( iTerm != 0 ) { DevWarning( "UnpackGameDetailsFromQOS found bad QOS block terminator!\n" ); } return pDetails; } void CMatchNetworkMsgControllerBase::PackageGameDetailsForQOS( KeyValues *pSettings, CUtlBuffer &buf ) { KeyValues *pDetails = GetLobbyDetailsTemplate( "qos", pSettings ); KeyValues::AutoDelete autodelete( pDetails ); // Keep only keys specified in the template pDetails->MergeFrom( pSettings, KeyValues::MERGE_KV_BORROW ); // Write the details as binary buf.PutInt( g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() ); buf.PutInt( 0 ); pDetails->WriteAsBinary( buf ); buf.PutInt( 0 ); } #if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS ) static void UnpackGameDetailsFromSteamLobbyInKey( uint64 uiLobbyID, char const *szPath, KeyValues *pKey ) { // Iterate over all the values for ( KeyValues *val = pKey->GetFirstValue(); val; val = val->GetNextValue() ) { char const *szLobbyData = steamapicontext->SteamMatchmaking() ->GetLobbyData( uiLobbyID, CFmtStr( "%s%s", szPath, val->GetName() ) ); switch ( val->GetDataType() ) { case KeyValues::TYPE_INT: val->SetInt( "", atoi( szLobbyData ) ); break; case KeyValues::TYPE_STRING: val->SetString( "", szLobbyData ); break; default: DevWarning( "UnpackGameDetailsFromSteamLobby defined unknown type in schema!\n" ); Assert( 0 ); break; } } // Iterate over subkeys for ( KeyValues *sub = pKey->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() ) { UnpackGameDetailsFromSteamLobbyInKey( uiLobbyID, CFmtStr( "%s%s:", szPath, sub->GetName() ), sub ); } } #endif KeyValues * CMatchNetworkMsgControllerBase::UnpackGameDetailsFromSteamLobby( uint64 uiLobbyID ) { #if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS ) // Make sure the basic metadata is set on the lobby char const *arrRequiredMetadata[] = { "system:network", "system:access" }; for ( int k = 0; k < ARRAYSIZE( arrRequiredMetadata ); ++ k ) { char const *szMetadata = steamapicontext->SteamMatchmaking()->GetLobbyData( uiLobbyID, arrRequiredMetadata[k] ); if ( !szMetadata || !*szMetadata ) return NULL; } // Allocate details template KeyValues *pDetails = GetLobbyDetailsTemplate(); // Iterate over all the keys UnpackGameDetailsFromSteamLobbyInKey( uiLobbyID, "", pDetails ); // Get members info if ( KeyValues *kvMembers = pDetails->FindKey( "members", true ) ) { int numSlots = steamapicontext->SteamMatchmaking()->GetLobbyMemberLimit( uiLobbyID ); numSlots = ClampArrayBounds( numSlots, g_pMMF->GetMatchTitle()->GetTotalNumPlayersSupported() ); kvMembers->SetInt( "numSlots", numSlots ); int numPlayers = steamapicontext->SteamMatchmaking()->GetNumLobbyMembers( uiLobbyID ); numPlayers = ClampArrayBounds( numPlayers, numSlots ); kvMembers->SetInt( "numPlayers", numPlayers ); } return pDetails; #endif return NULL; } KeyValues * CMatchNetworkMsgControllerBase::PackageGameDetailsForReservation( KeyValues *pSettings ) { KeyValues *res = GetLobbyDetailsTemplate( "reserve", pSettings ); res->SetName( COM_GetModDirectory() ); // Keep only keys specified in the template res->MergeFrom( pSettings, KeyValues::MERGE_KV_BORROW ); return res; }