373 lines
12 KiB
C++
373 lines
12 KiB
C++
//===== 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|