//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// // // Purpose: // //===========================================================================// #include "mm_title.h" #include "mm_title_richpresence.h" #include "vstdlib/random.h" #include "fmtstr.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" class CMatchTitleGameSettingsMgr : public IMatchTitleGameSettingsMgr { public: // Extends server game details virtual void ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest ); // Adds the essential part of game details to be broadcast virtual void ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings ); // Extends game settings update packet for lobby transition, // either due to a migration or due to an endgame condition virtual void ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame ); // Rolls up game details for matches grouping virtual KeyValues * RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery ); // Defines session search keys for matchmaking virtual KeyValues * DefineSessionSearchKeys( KeyValues *pSettings ); // Defines dedicated server search key virtual KeyValues * DefineDedicatedSearchKeys( KeyValues *pSettings ); // Initializes full game settings from potentially abbreviated game settings virtual void InitializeGameSettings( KeyValues *pSettings ); // Extends game settings update packet before it gets merged with // session settings and networked to remote clients virtual void ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys ); // Prepares system for session creation virtual KeyValues * PrepareForSessionCreate( KeyValues *pSettings ); // Executes the command on the session settings, this function on host // is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues // When running on a remote client "ppPlayersUpdated" is NULL and players cannot // be modified virtual void ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated ); // Prepares the lobby for game or adjust settings of new players who // join a game in progress, this function is allowed to modify // Members/Game subkeys and has to fill in modified players KeyValues virtual void PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated ); // Prepares the host team lobby for game adjusting the game settings // this function is allowed to prepare modification package to update // Game subkeys. // Returns the update/delete package to be applied to session settings // and pushed to dependent two sesssion of the two teams. virtual KeyValues * PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote ); }; CMatchTitleGameSettingsMgr g_MatchTitleGameSettingsMgr; IMatchTitleGameSettingsMgr *g_pIMatchTitleGameSettingsMgr = &g_MatchTitleGameSettingsMgr; // // Implementation of CMatchTitleGameSettingsMgr // // Extends server game details void CMatchTitleGameSettingsMgr::ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest ) { // Query server info INetSupport::ServerInfo_t si; g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si ); // Server is always in game pDetails->SetString( "game/state", "game" ); // // Determine map info // { pDetails->SetString( "game/bspname", CFmtStr( "%s", si.m_szMapName ) ); } } // Adds the essential part of game details to be broadcast void CMatchTitleGameSettingsMgr::ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings ) { static KeyValues *pkvExt = KeyValues::FromString( "settings", " game { " " bspname #empty# " " } " ); pDetails->MergeFrom( pkvExt, KeyValues::MERGE_KV_UPDATE ); } // Extends game settings update packet for lobby transition, // either due to a migration or due to an endgame condition void CMatchTitleGameSettingsMgr::ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame ) { pSettingsUpdate->SetString( "game/state", "lobby" ); } // Rolls up game details for matches grouping KeyValues * CMatchTitleGameSettingsMgr::RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery ) { return NULL; } // Defines dedicated server search key KeyValues * CMatchTitleGameSettingsMgr::DefineDedicatedSearchKeys( KeyValues *pSettings ) { if ( IsPC() ) { static ConVarRef sv_search_key( "sv_search_key" ); KeyValues *pKeys = new KeyValues( "SearchKeys" ); pKeys->SetString( "gametype", CFmtStr( "%s,sv_search_key_%s%d", "empty", sv_search_key.GetString(), g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() ) ); return pKeys; } else { return NULL; } } // Defines session search keys for matchmaking KeyValues * CMatchTitleGameSettingsMgr::DefineSessionSearchKeys( KeyValues *pSettings ) { MEM_ALLOC_CREDIT(); KeyValues *pResult = new KeyValues( "SessionSearch" ); pResult->SetInt( "numPlayers", pSettings->GetInt( "members/numPlayers", XBX_GetNumGameUsers() ) ); /* char const *szGameMode = pSettings->GetString( "game/mode", "" ); if ( IsX360() ) { if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) ) { static ContextValue_t values[] = { { "versus", SESSION_MATCH_QUERY_PUBLIC_STATE_C___SORT___CHAPTER }, { "teamversus", SESSION_MATCH_QUERY_TEAM_STATE_C_CHAPTER }, { "scavenge", SESSION_MATCH_QUERY_PUBLIC_STATE_C_CHAPTER___SORT___ROUNDS }, { "teamscavenge", SESSION_MATCH_QUERY_TEAM_STATE_C_CHAPTER_ROUNDS }, { "survival", SESSION_MATCH_QUERY_PUBLIC_STATE_C_CHAPTER }, { "coop", SESSION_MATCH_QUERY_PUBLIC_STATE_C_DIFF___SORT___CHAPTER }, { "realism", SESSION_MATCH_QUERY_PUBLIC_STATE_C_DIFF___SORT___CHAPTER }, { NULL, 0xFFFF }, }; pResult->SetInt( "rule", values->ScanValues( szValue ) ); } // Set the matchmaking version pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MMVERSION ), mm_matchmaking_version.GetInt() ); #ifdef _X360 // Set the installed DLCs masks uint64 uiDlcsMask = MatchSession_GetDlcInstalledMask(); for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k ) { pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MMVERSION + k ), !!( uiDlcsMask & ( 1ull << k ) ) ); } pResult->SetInt( "dlc1", PROPERTY_MMVERSION + 1 ); pResult->SetInt( "dlcN", PROPERTY_MMVERSION + mm_matchmaking_dlcsquery.GetInt() ); #endif // X_CONTEXT_GAME_TYPE pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_TYPE ), X_CONTEXT_GAME_TYPE_STANDARD ); // X_CONTEXT_GAME_MODE if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) ) { pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_MODE ), g_pcv_CONTEXT_GAME_MODE->ScanValues( szValue ) ); } if ( char const *szValue = pSettings->GetString( "game/state", NULL ) ) { pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_STATE ), g_pcv_CONTEXT_STATE->ScanValues( szValue ) ); } if ( char const *szValue = pSettings->GetString( "game/difficulty", NULL ) ) { if ( !Q_stricmp( "coop", szGameMode ) || !Q_stricmp( "realism", szGameMode ) ) { pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_DIFFICULTY ), g_pcv_CONTEXT_DIFFICULTY->ScanValues( szValue ) ); } } if ( int val = pSettings->GetInt( "game/maxrounds" ) ) { if ( !Q_stricmp( "scavenge", szGameMode ) || !Q_stricmp( "teamscavenge", szGameMode ) ) { pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MAXROUNDS ), val ); } } char const *szCampaign = pSettings->GetString( "game/campaign" ); if ( *szCampaign ) { DWORD dwContext = CONTEXT_CAMPAIGN_UNKNOWN; if ( KeyValues *pAllMissions = g_pMatchExtL4D->GetAllMissions() ) { if ( KeyValues *pMission = pAllMissions->FindKey( szCampaign ) ) { dwContext = pMission->GetInt( "x360ctx", dwContext ); } } if ( dwContext != CONTEXT_CAMPAIGN_UNKNOWN ) { pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_CAMPAIGN ), dwContext ); } } if ( int val = pSettings->GetInt( "game/chapter" ) ) { pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_CHAPTER ), val ); } } else */ { if ( char const *szValue = pSettings->GetString( "game/bspname", NULL ) ) { pResult->SetString( "Filter=/game:bspname", szValue ); } } return pResult; } // Initializes full game settings from potentially abbreviated game settings void CMatchTitleGameSettingsMgr::InitializeGameSettings( KeyValues *pSettings ) { char const *szNetwork = pSettings->GetString( "system/network", "LIVE" ); pSettings->SetString( "game/state", "lobby" ); if ( KeyValues *kv = pSettings->FindKey( "Options", true ) ) { kv->SetString( "server", "listen" ); } // Offline games don't need slots and player setup if ( !Q_stricmp( "offline", szNetwork ) ) return; // // Set the number of slots // int numSlots = 10; pSettings->SetInt( "members/numSlots", numSlots ); } // Extends game settings update packet before it gets merged with // session settings and networked to remote clients void CMatchTitleGameSettingsMgr::ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys ) { } // Prepares system for session creation KeyValues * CMatchTitleGameSettingsMgr::PrepareForSessionCreate( KeyValues *pSettings ) { return MM_Title_RichPresence_PrepareForSessionCreate( pSettings ); } // Prepares the lobby for game or adjust settings of new players who // join a game in progress, this function is allowed to modify // Members/Game subkeys and has to write modified players XUIDs void CMatchTitleGameSettingsMgr::PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated ) { // set player avatar/teams, etc } // Prepares the host team lobby for game adjusting the game settings // this function is allowed to prepare modification package to update // Game subkeys. // Returns the update/delete package to be applied to session settings // and pushed to dependent two sesssion of the two teams. KeyValues * CMatchTitleGameSettingsMgr::PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote ) { return NULL; } static void OnRunCommand_TeamChange( KeyValues *pCommand, KeyValues *pSettings, KeyValues **ppPlayersUpdated ) { MEM_ALLOC_CREDIT(); XUID xuid = pCommand->GetUint64( "xuid" ); char const *szTeam = pCommand->GetString( "team", "" ); KeyValues *pMembers = pSettings->FindKey( "members" ); if ( !pMembers ) return; // Find the avatar that is going to be updated KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, xuid ); if ( !pPlayer ) return; // Check if the team is the same if ( !Q_stricmp( szTeam, pPlayer->GetString( "game/team", "" ) ) ) return; KeyValues *kvGame = pPlayer->FindKey( "game", true ); if ( !kvGame ) return; // If desired avatar is blank, then no validation required if ( !*szTeam ) { kvGame->SetString( "team", "random" ); } else { kvGame->SetString( "team", szTeam ); } // Notify the sessions of a player update * ( ppPlayersUpdated ++ ) = pPlayer; } // Executes the command on the session settings, this function on host // is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues // When running on a remote client "ppPlayersUpdated" is NULL and players cannot // be modified void CMatchTitleGameSettingsMgr::ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated ) { char const *szCommand = pCommand->GetName(); if ( !Q_stricmp( "Game::Team", szCommand ) ) { if ( !Q_stricmp( "host", pSessionSystemData->GetString( "type", "host" ) ) && // !Q_stricmp( "lobby", pSessionSystemData->GetString( "state", "lobby" ) ) && - avatars also update when players change team ingame ppPlayersUpdated ) { char const *szAvatar = pCommand->GetString( "team" ); if ( !*szAvatar ) { // Requesting random is only allowed in unlocked lobby if ( Q_stricmp( "lobby", pSessionSystemData->GetString( "state", "lobby" ) ) ) return; if ( *pSettings->GetString( "system/lock" ) ) return; } OnRunCommand_TeamChange( pCommand, pSettings, ppPlayersUpdated ); return; } } }