initial
This commit is contained in:
423
matchmaking/swarm/matchext_swarm.cpp
Normal file
423
matchmaking/swarm/matchext_swarm.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "matchext_swarm.h"
|
||||
#include "swarm.spa.h"
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "utlstringmap.h"
|
||||
#include "fmtstr.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
#define g_pFileSystem g_pFullFileSystem
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
CMatchExtSwarm::CMatchExtSwarm()
|
||||
{
|
||||
m_pKeyValues = NULL;
|
||||
}
|
||||
|
||||
CMatchExtSwarm::~CMatchExtSwarm()
|
||||
{
|
||||
if ( m_pKeyValues )
|
||||
{
|
||||
m_pKeyValues->deleteThis();
|
||||
m_pKeyValues = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static CMatchExtSwarm g_MatchExtSwarm;
|
||||
CMatchExtSwarm *g_pMatchExtSwarm = &g_MatchExtSwarm;
|
||||
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMatchExtSwarm, IMatchExtSwarm,
|
||||
IMATCHEXT_SWARM_INTERFACE, g_MatchExtSwarm );
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
void CMatchExtSwarm::ParseMissionFromFile( char const *szFile, bool bBuiltIn )
|
||||
{
|
||||
KeyValues *pMissionModes = m_pKeyValues->FindKey( "GameModes" );
|
||||
KeyValues *pMissionsRoot = m_pKeyValues->FindKey( "Missions" );
|
||||
if ( !pMissionsRoot || !pMissionModes )
|
||||
return;
|
||||
|
||||
// See if we already have this mission
|
||||
if ( m_mapFilesLoaded.Find( szFile ) != m_mapFilesLoaded.InvalidIndex() )
|
||||
return;
|
||||
m_mapFilesLoaded[ szFile ] = NULL;
|
||||
|
||||
KeyValues *missionKeys = new KeyValues( "mission" );
|
||||
KeyValues::AutoDelete autodelete_missionKeys( missionKeys ); // allows for early error-return
|
||||
|
||||
bool bLoadResult = missionKeys->LoadFromFile( g_pFileSystem, szFile );
|
||||
if ( !bLoadResult )
|
||||
{
|
||||
Warning( "MissionManager: Mission file \"%s\" is malformed, failed to parse.\n", szFile );
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = missionKeys->GetString( "name", NULL );
|
||||
const char *campaignVersion = missionKeys->GetString( "version", NULL );
|
||||
|
||||
if ( !name || !campaignVersion )
|
||||
{
|
||||
Warning( "MissionManager: Mission file \"%s\" is missing name and version\n", szFile );
|
||||
return;
|
||||
}
|
||||
|
||||
// Check invalid characters
|
||||
for ( char const *pCharCheck = name; *pCharCheck; ++ pCharCheck )
|
||||
{
|
||||
char const c = *pCharCheck;
|
||||
if ( !( ( c >= 'a' && c <= 'z' ) ||
|
||||
( c >= 'A' && c <= 'Z' ) ||
|
||||
( c >= '0' && c <= '9' ) ) )
|
||||
{
|
||||
Warning( "MissionManager: Only alphanumeric characters allowed in mission name: \"%s\" in \"%s\"\n", name, szFile );
|
||||
return;
|
||||
}
|
||||
}
|
||||
for ( char const *pCharCheck = campaignVersion; *pCharCheck; ++ pCharCheck )
|
||||
{
|
||||
char const c = *pCharCheck;
|
||||
if ( !( c >= '0' && c <= '9' ) )
|
||||
{
|
||||
Warning( "MissionManager: Only numeric characters allowed in mission version: \"%s\" in \"%s\"\n", campaignVersion, szFile );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now go through the game modes
|
||||
KeyValues *modes = missionKeys->FindKey( "modes" );
|
||||
if ( !modes )
|
||||
{
|
||||
Warning( "MissionManager: Mission file \"%s\" is missing data for any game modes\n", szFile );
|
||||
return;
|
||||
}
|
||||
|
||||
CFmtStr sNameVersion( "%s_%s", name, campaignVersion );
|
||||
|
||||
// Don't allow duplicates
|
||||
if ( m_mapMissionsLoaded.Find( name ) != m_mapMissionsLoaded.InvalidIndex() )
|
||||
{
|
||||
Warning( "MissionManager: Duplicate mission \"%s\" in file \"%s\", already loaded from \"%s\".\n",
|
||||
name, szFile, m_mapMissionsLoaded[ name ]->GetString( "cfgfile" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
DevMsg( "\tMission %s ver %s loading...\n", name, campaignVersion );
|
||||
|
||||
// Validate DisplayTitle and Description
|
||||
char const *szDisplayTitle = missionKeys->GetString( "displaytitle" );
|
||||
if ( !szDisplayTitle || !*szDisplayTitle )
|
||||
missionKeys->SetString( "displaytitle", name );
|
||||
|
||||
// Set auto-generated fields
|
||||
missionKeys->SetName( name );
|
||||
missionKeys->SetInt( "builtin", bBuiltIn ? 1 : 0 );
|
||||
missionKeys->SetString( "cfgfile", szFile );
|
||||
missionKeys->SetString( "cfgtag", sNameVersion );
|
||||
|
||||
// Load game modes
|
||||
int numGameModes = 0;
|
||||
for ( KeyValues *modeName = pMissionModes->GetFirstTrueSubKey(); modeName; modeName = modeName->GetNextTrueSubKey() )
|
||||
{
|
||||
KeyValues *mode = modes->FindKey( modeName->GetName() );
|
||||
if ( !mode )
|
||||
continue;
|
||||
|
||||
int numChapters = 0;
|
||||
while ( KeyValues *pChapterKey = mode->FindKey( CFmtStr( "%d", numChapters + 1 ) ) )
|
||||
{
|
||||
// Check required fields
|
||||
char const *szMap = pChapterKey->GetString( "map" );
|
||||
if ( !szMap || !*szMap )
|
||||
{
|
||||
Warning( "MissionManager: Mission file \"%s\" has invalid map specified for modes/%s/%s\n",
|
||||
szFile, modeName->GetName(), pChapterKey->GetName() );
|
||||
numChapters = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// DisplayName
|
||||
char const *szDisplayName = pChapterKey->GetString( "displayname" );
|
||||
if ( !szDisplayName || !*szDisplayName )
|
||||
{
|
||||
pChapterKey->SetString( "displayname", CFmtStr( "%s-%s", name, pChapterKey->GetName() ) );
|
||||
}
|
||||
|
||||
// Image
|
||||
char const *szImage = pChapterKey->GetString( "image" );
|
||||
if ( !szImage || !*szImage )
|
||||
{
|
||||
pChapterKey->SetString( "image", "maps/unknown" );
|
||||
}
|
||||
|
||||
// Set the automatic fields
|
||||
pChapterKey->SetInt( "chapter", numChapters + 1 );
|
||||
|
||||
// This chapter was valid
|
||||
++ numChapters;
|
||||
}
|
||||
|
||||
if ( !numChapters )
|
||||
{
|
||||
modes->RemoveSubKey( mode );
|
||||
mode->deleteThis();
|
||||
mode = NULL;
|
||||
|
||||
Warning( "MissionManager: Mission file \"%s\" has invalid settings for game mode %s\n",
|
||||
szFile, modeName->GetName() );
|
||||
continue;
|
||||
}
|
||||
|
||||
mode->SetInt( "chapters", numChapters );
|
||||
DevMsg( "\t\tloaded %d %s chapters.\n", numChapters, modeName->GetName() );
|
||||
|
||||
++ numGameModes;
|
||||
}
|
||||
|
||||
if ( !numGameModes )
|
||||
{
|
||||
Warning( "MissionManager: Mission file \"%s\" does not have valid data for any supported game mode\n", szFile );
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Bind the loaded mission keys into the system
|
||||
//
|
||||
m_mapFilesLoaded[ szFile ] = missionKeys;
|
||||
m_mapMissionsLoaded[ name ] = missionKeys;
|
||||
|
||||
pMissionsRoot->AddSubKey( missionKeys );
|
||||
autodelete_missionKeys.Assign( NULL ); // prevent automatic deletion
|
||||
|
||||
// Register all the loaded game modes
|
||||
for ( KeyValues *modeName = pMissionModes->GetFirstTrueSubKey(); modeName; modeName = modeName->GetNextTrueSubKey() )
|
||||
{
|
||||
KeyValues *mode = modes->FindKey( modeName->GetName() );
|
||||
if ( !mode )
|
||||
continue;
|
||||
|
||||
modeName->SetPtr( name, missionKeys );
|
||||
}
|
||||
|
||||
DevMsg( "\tMission %s ver %s loaded %d game modes.\n", name, campaignVersion, numGameModes );
|
||||
}
|
||||
|
||||
void CMatchExtSwarm::MakeGameModeCopy( char const *szGameMode, char const *szCopyName )
|
||||
{
|
||||
// Fix the GameModes key
|
||||
if ( KeyValues *pKeyMode = m_pKeyValues->FindKey( CFmtStr( "GameModes/%s", szGameMode ) ) )
|
||||
{
|
||||
pKeyMode = pKeyMode->MakeCopy();
|
||||
pKeyMode->SetName( szCopyName );
|
||||
m_pKeyValues->FindKey( "GameModes" )->AddSubKey( pKeyMode );
|
||||
}
|
||||
|
||||
// Fix all missions
|
||||
KeyValues *pMission = GetAllMissions();
|
||||
for ( pMission = pMission ? pMission->GetFirstTrueSubKey() : NULL;
|
||||
pMission; pMission = pMission->GetNextTrueSubKey() )
|
||||
{
|
||||
if ( KeyValues *pKeyMode = pMission->FindKey( CFmtStr( "modes/%s", szGameMode ) ) )
|
||||
{
|
||||
pKeyMode = pKeyMode->MakeCopy();
|
||||
pKeyMode->SetName( szCopyName );
|
||||
pMission->FindKey( "modes" )->AddSubKey( pKeyMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchExtSwarm::Initialize()
|
||||
{
|
||||
DevMsg( "Loading Mission Data\n" );
|
||||
MEM_ALLOC_CREDIT();
|
||||
|
||||
if ( m_pKeyValues )
|
||||
{
|
||||
m_pKeyValues->deleteThis();
|
||||
m_pKeyValues = NULL;
|
||||
}
|
||||
|
||||
m_mapFilesLoaded.Purge();
|
||||
m_mapMissionsLoaded.Purge();
|
||||
|
||||
m_pKeyValues = KeyValues::FromString(
|
||||
"AlienSwarm",
|
||||
" GameModes { "
|
||||
" coop { } "
|
||||
#ifndef _DEMO
|
||||
" versus { } "
|
||||
" survival { } "
|
||||
" scavenge { } "
|
||||
#endif
|
||||
" } "
|
||||
" Missions { "
|
||||
// read from mission files
|
||||
" } "
|
||||
);
|
||||
|
||||
//
|
||||
// Parse built-in missions
|
||||
//
|
||||
|
||||
#ifndef _DEMO
|
||||
ParseMissionFromFile( "missions/campaign1.txt", true );
|
||||
ParseMissionFromFile( "missions/campaign2.txt", true );
|
||||
ParseMissionFromFile( "missions/campaign3.txt", true );
|
||||
ParseMissionFromFile( "missions/campaign4.txt", true );
|
||||
ParseMissionFromFile( "missions/campaign5.txt", true );
|
||||
ParseMissionFromFile( "missions/credits.txt", true );
|
||||
|
||||
//
|
||||
// Search missions using the wildcards
|
||||
//
|
||||
char szMissionPath[_MAX_PATH];
|
||||
Q_snprintf( szMissionPath, sizeof( szMissionPath ), "missions/*.txt" );
|
||||
Q_FixSlashes( szMissionPath );
|
||||
|
||||
FileFindHandle_t handle;
|
||||
const char *pFoundFile = g_pFileSystem->FindFirst( szMissionPath, &handle );
|
||||
while ( pFoundFile )
|
||||
{
|
||||
char pFilename[ MAX_PATH ];
|
||||
V_snprintf( pFilename, ARRAYSIZE(pFilename), "missions/%s", pFoundFile );
|
||||
pFoundFile = g_pFileSystem->FindNext( handle );
|
||||
|
||||
ParseMissionFromFile( pFilename, false );
|
||||
}
|
||||
|
||||
#else
|
||||
ParseMissionFromFile( "missions/demo.txt", true );
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef _DEMO
|
||||
// Make game mode copies
|
||||
MakeGameModeCopy( "versus", "teamversus" );
|
||||
MakeGameModeCopy( "scavenge", "teamscavenge" );
|
||||
MakeGameModeCopy( "coop", "realism" );
|
||||
#endif
|
||||
|
||||
DevMsg( "Loading Mission Data Finished\n" );
|
||||
}
|
||||
|
||||
void CMatchExtSwarm::DebugPrint()
|
||||
{
|
||||
KeyValuesDumpAsDevMsg( m_pKeyValues, 1, 0 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
CON_COMMAND( mission_reload, "Reload mission metadata" )
|
||||
{
|
||||
g_MatchExtSwarm.Initialize();
|
||||
}
|
||||
|
||||
CON_COMMAND_F( mission_debug_print, "Print all mission metadata", FCVAR_DEVELOPMENTONLY )
|
||||
{
|
||||
g_MatchExtSwarm.DebugPrint();
|
||||
}
|
||||
|
||||
KeyValues * CMatchExtSwarm::GetAllMissions()
|
||||
{
|
||||
if ( !m_pKeyValues )
|
||||
return NULL;
|
||||
|
||||
return m_pKeyValues->FindKey( "Missions" );
|
||||
}
|
||||
|
||||
// Get server map information for the session settings
|
||||
KeyValues * CMatchExtSwarm::GetMapInfo( KeyValues *pSettings, KeyValues **ppMissionInfo )
|
||||
{
|
||||
if ( !m_pKeyValues )
|
||||
return NULL;
|
||||
|
||||
char const *szGameMode = pSettings->GetString( "game/mode", NULL );
|
||||
if ( !szGameMode || !*szGameMode )
|
||||
return NULL;
|
||||
|
||||
char const *szCampaign = pSettings->GetString( "game/campaign", NULL );
|
||||
if ( !szCampaign || !*szCampaign )
|
||||
return NULL;
|
||||
|
||||
int nMapNumber = pSettings->GetInt( "game/chapter", 0 );
|
||||
if ( nMapNumber <= 0 )
|
||||
return NULL;
|
||||
|
||||
// Find the campaign key
|
||||
KeyValues *pMissionKey = ( KeyValues * ) m_pKeyValues->GetPtr( CFmtStr( "GameModes/%s/%s", szGameMode, szCampaign ), NULL );
|
||||
if ( !pMissionKey )
|
||||
return NULL;
|
||||
|
||||
// Find the total number of chapters in that mission's game mode
|
||||
int numChapters = pMissionKey->GetInt( CFmtStr( "modes/%s/chapters", szGameMode ), 0 );
|
||||
if ( nMapNumber > numChapters )
|
||||
return NULL;
|
||||
|
||||
KeyValues *pChapterKey = pMissionKey->FindKey( CFmtStr( "modes/%s/%d", szGameMode, nMapNumber ) );
|
||||
if ( !pChapterKey )
|
||||
return NULL;
|
||||
|
||||
if ( ppMissionInfo )
|
||||
*ppMissionInfo = pMissionKey;
|
||||
|
||||
return pChapterKey;
|
||||
}
|
||||
|
||||
KeyValues * CMatchExtSwarm::GetMapInfoByBspName( KeyValues *pSettings, char const *szBspMapName, KeyValues **ppMissionInfo )
|
||||
{
|
||||
if ( !m_pKeyValues )
|
||||
return NULL;
|
||||
|
||||
Assert( szBspMapName );
|
||||
if ( !szBspMapName || !*szBspMapName )
|
||||
return NULL;
|
||||
|
||||
char const *szGameMode = pSettings->GetString( "game/mode", NULL );
|
||||
if ( !szGameMode || !*szGameMode )
|
||||
return NULL;
|
||||
|
||||
// Walk all the missions in that game mode
|
||||
KeyValues *pModeMissions = m_pKeyValues->FindKey( CFmtStr( "GameModes/%s", szGameMode ) );
|
||||
if ( !pModeMissions )
|
||||
return NULL;
|
||||
|
||||
for ( KeyValues *pMissionName = pModeMissions->GetFirstValue(); pMissionName; pMissionName = pMissionName->GetNextValue() )
|
||||
{
|
||||
KeyValues *pMission = ( KeyValues * ) pMissionName->GetPtr();
|
||||
if ( !pMission )
|
||||
continue;
|
||||
|
||||
KeyValues *pChapters = pMission->FindKey( CFmtStr( "modes/%s", szGameMode ) );
|
||||
if ( !pChapters )
|
||||
continue;
|
||||
|
||||
int numChapters = pChapters->GetInt( "chapters" );
|
||||
for ( int k = 1; k <= numChapters; ++ k )
|
||||
{
|
||||
KeyValues *pMap = pChapters->FindKey( CFmtStr( "%d", k ) );
|
||||
if ( !pMap )
|
||||
break;
|
||||
|
||||
char const *szBspName = pMap->GetString( "map" );
|
||||
if ( !Q_stricmp( szBspName, szBspMapName ) )
|
||||
{
|
||||
if ( ppMissionInfo )
|
||||
*ppMissionInfo = pMission;
|
||||
|
||||
return pMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
45
matchmaking/swarm/matchext_swarm.h
Normal file
45
matchmaking/swarm/matchext_swarm.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MATCHEXT_SWARM_H
|
||||
#define MATCHEXT_SWARM_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "../mm_framework.h"
|
||||
#include "matchmaking/swarm/imatchext_swarm.h"
|
||||
|
||||
class CMatchExtSwarm : public IMatchExtSwarm
|
||||
{
|
||||
// Methods of IMatchExtSwarm
|
||||
public:
|
||||
// Get server map information for the session settings
|
||||
virtual KeyValues * GetAllMissions();
|
||||
virtual KeyValues * GetMapInfo( KeyValues *pSettings, KeyValues **ppMissionInfo = NULL );
|
||||
virtual KeyValues * GetMapInfoByBspName( KeyValues *pSettings, char const *szBspMapName, KeyValues **ppMissionInfo = NULL );
|
||||
|
||||
public:
|
||||
void Initialize();
|
||||
void DebugPrint();
|
||||
|
||||
void ParseMissionFromFile( char const *szFile, bool bBuiltIn );
|
||||
void MakeGameModeCopy( char const *szGameMode, char const *szCopyName );
|
||||
|
||||
public:
|
||||
CMatchExtSwarm();
|
||||
~CMatchExtSwarm();
|
||||
|
||||
private:
|
||||
KeyValues *m_pKeyValues;
|
||||
CUtlStringMap< KeyValues * > m_mapFilesLoaded;
|
||||
CUtlStringMap< KeyValues * > m_mapMissionsLoaded;
|
||||
};
|
||||
|
||||
// Match extension singleton
|
||||
extern CMatchExtSwarm *g_pMatchExtSwarm;
|
||||
|
||||
#endif // MATCHEXT_L4D_H
|
||||
462
matchmaking/swarm/mm_title.cpp
Normal file
462
matchmaking/swarm/mm_title.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "swarm.spa.h"
|
||||
#include "matchext_swarm.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
CMatchTitle::CMatchTitle()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
CMatchTitle::~CMatchTitle()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t CMatchTitle::Init()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->AddListener( this, "server_pre_shutdown", false );
|
||||
mgr->AddListener( this, "game_newmap", false );
|
||||
mgr->AddListener( this, "finale_start", false );
|
||||
mgr->AddListener( this, "round_start", false );
|
||||
mgr->AddListener( this, "round_end", false );
|
||||
mgr->AddListener( this, "difficulty_changed", false );
|
||||
}
|
||||
|
||||
// Initialize all the campaigns
|
||||
g_pMatchExtSwarm->Initialize();
|
||||
|
||||
#ifdef _X360
|
||||
// Initialize Title Update version
|
||||
extern ConVar mm_tu_string;
|
||||
mm_tu_string.SetValue( "00000000" );
|
||||
#endif
|
||||
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CMatchTitle::Shutdown()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->RemoveListener( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
uint64 CMatchTitle::GetTitleID()
|
||||
{
|
||||
#ifdef _X360
|
||||
#ifndef _DEMO
|
||||
return TITLEID_SWARM;
|
||||
#else
|
||||
return TITLEID_SWARM_DEMO;
|
||||
#endif
|
||||
#elif !defined( SWDS )
|
||||
static uint64 uiAppID = 0ull;
|
||||
if ( !uiAppID && steamapicontext && steamapicontext->SteamUtils() )
|
||||
{
|
||||
uiAppID = steamapicontext->SteamUtils()->GetAppID();
|
||||
}
|
||||
return uiAppID;
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64 CMatchTitle::GetTitleServiceID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return 0x45410880ull; // Left 4 Dead 1 Service ID
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMatchTitle::IsMultiplayer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
|
||||
{
|
||||
#ifdef _X360
|
||||
XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
|
||||
|
||||
xnsp.cfgQosDataLimitDiv4 = 128; // 512 bytes
|
||||
xnsp.cfgSockDefaultRecvBufsizeInK = 64; // Increase receive size for UDP to 64k
|
||||
xnsp.cfgSockDefaultSendBufsizeInK = 64; // Keep send size at 64k too
|
||||
|
||||
int numGamePlayersMax = GetTotalNumPlayersSupported();
|
||||
|
||||
int numConnections = 4 * ( numGamePlayersMax - 1 );
|
||||
// - the max number of connections to members of your game party
|
||||
// - the max number of connections to members of your social party
|
||||
// - the max number of connections to a pending game party (if you are joining a new one ).
|
||||
// - matchmakings client info structure also creates a connection per client for the lobby.
|
||||
|
||||
// 1 - the main game session
|
||||
int numTotalConnections = 1 + numConnections;
|
||||
|
||||
// 29 - total Connections (XNADDR/XNKID pairs) ,using 5 sessions (XNKID/XNKEY pairs).
|
||||
|
||||
xnsp.cfgKeyRegMax = 16; //adding some extra room because of lazy dealocation of these pairs.
|
||||
xnsp.cfgSecRegMax = MAX( 64, numTotalConnections ); //adding some extra room because of lazy dealocation of these pairs.
|
||||
|
||||
xnsp.cfgSockMaxDgramSockets = xnsp.cfgSecRegMax;
|
||||
xnsp.cfgSockMaxStreamSockets = xnsp.cfgSecRegMax;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CMatchTitle::GetTotalNumPlayersSupported()
|
||||
{
|
||||
// Alien Swarm is a 4-player game
|
||||
return 4;
|
||||
}
|
||||
|
||||
// Get a guest player name
|
||||
char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
|
||||
{
|
||||
if ( vgui::ILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
|
||||
{
|
||||
if ( wchar_t* wStringTableEntry = pLocalize->Find( "#L4D360UI_Character_Guest" ) )
|
||||
{
|
||||
static char szName[ MAX_PLAYER_NAME_LENGTH ] = {0};
|
||||
pLocalize->ConvertUnicodeToANSI( wStringTableEntry, szName, ARRAYSIZE( szName ) );
|
||||
return szName;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
void CMatchTitle::PrepareClientForConnect( KeyValues *pSettings )
|
||||
{
|
||||
#ifndef SWDS
|
||||
int numPlayers = 1;
|
||||
#ifdef _X360
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
//
|
||||
// Now we set the convars
|
||||
//
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
int iController = k;
|
||||
#ifdef _X360
|
||||
iController = XBX_GetUserId( k );
|
||||
#endif
|
||||
IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController );
|
||||
if ( !pPlayerLocal )
|
||||
continue;
|
||||
|
||||
// Set "name"
|
||||
static SplitScreenConVarRef s_cl_name( "name" );
|
||||
char const *szName = pPlayerLocal->GetName();
|
||||
s_cl_name.SetValue( k, szName );
|
||||
|
||||
// Set "networkid_force"
|
||||
if ( IsX360() )
|
||||
{
|
||||
static SplitScreenConVarRef s_networkid_force( "networkid_force" );
|
||||
uint64 xid = pPlayerLocal->GetXUID();
|
||||
s_networkid_force.SetValue( k, CFmtStr( "%08X:%08X:", uint32( xid >> 32 ), uint32( xid ) ) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMatchTitle::StartServerMap( KeyValues *pSettings )
|
||||
{
|
||||
int numPlayers = 1;
|
||||
#ifdef _X360
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
KeyValues *pInfoMission = NULL;
|
||||
KeyValues *pInfoChapter = g_pMatchExtSwarm->GetMapInfo( pSettings, &pInfoMission );
|
||||
Assert( pInfoChapter );
|
||||
if ( !pInfoChapter || !pInfoMission )
|
||||
return false;
|
||||
|
||||
// Check that we have the server interface and that the map is valid
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer() )
|
||||
return false;
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer()->IsMapValid( pInfoChapter->GetString( "map" ) ) )
|
||||
return false;
|
||||
|
||||
//
|
||||
// Prepare game dll reservation package
|
||||
//
|
||||
KeyValues *pGameDllReserve = g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( pSettings );
|
||||
KeyValues::AutoDelete autodelete( pGameDllReserve );
|
||||
|
||||
pGameDllReserve->SetString( "map/mapcommand", ( numPlayers <= 1 ) ? "map" : "ss_map" );
|
||||
|
||||
if ( !Q_stricmp( "commentary", pSettings->GetString( "options/play", "" ) ) )
|
||||
pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
|
||||
|
||||
// Run map based off the faked reservation packet
|
||||
g_pMatchExtensions->GetIServerGameDLL()->ApplyGameSettings( pGameDllReserve );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static KeyValues * GetCurrentMatchSessionSettings()
|
||||
{
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
|
||||
}
|
||||
|
||||
#ifndef SWDS
|
||||
static void SendPreConnectClientDataToServer( int nSlot )
|
||||
{
|
||||
// We have just connected to the server,
|
||||
// send our avatar information
|
||||
|
||||
int iController = nSlot;
|
||||
#ifdef _X360
|
||||
iController = XBX_GetUserId( nSlot );
|
||||
#endif
|
||||
|
||||
// Swarm for now has no preconnect data
|
||||
if ( 1 )
|
||||
return;
|
||||
|
||||
KeyValues *pPreConnectData = new KeyValues( "preconnectdata" );
|
||||
|
||||
//
|
||||
// Now we prep the keyvalues for server
|
||||
//
|
||||
if ( IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController ) )
|
||||
{
|
||||
// Set session-specific user info
|
||||
XUID xuid = pPlayerLocal->GetXUID();
|
||||
pPreConnectData->SetUint64( "xuid", xuid );
|
||||
|
||||
KeyValues *pSettings = GetCurrentMatchSessionSettings();
|
||||
|
||||
KeyValues *pMachine = NULL;
|
||||
if ( KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, xuid, &pMachine ) )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Deliver the keyvalues to the server
|
||||
int nRestoreSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
|
||||
g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nSlot );
|
||||
g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( pPreConnectData );
|
||||
g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nRestoreSlot );
|
||||
}
|
||||
#endif
|
||||
|
||||
void CMatchTitle::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
|
||||
!Q_stricmp( "OnPlayerUpdated", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_PlayersChanged( GetCurrentMatchSessionSettings() );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
|
||||
{
|
||||
if ( !Q_stricmp( pEvent->GetString( "state" ), "updated" ) )
|
||||
{
|
||||
if ( KeyValues *kvUpdate = pEvent->FindKey( "update" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), kvUpdate );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "created" ) ||
|
||||
!Q_stricmp( pEvent->GetString( "state" ), "ready" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
#ifndef SWDS
|
||||
else if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
|
||||
{
|
||||
int nSlot = pEvent->GetInt( "slot" );
|
||||
int iOldState = pEvent->GetInt( "old" );
|
||||
int iNewState = pEvent->GetInt( "new" );
|
||||
|
||||
if ( iOldState < SIGNONSTATE_CONNECTED &&
|
||||
iNewState >= SIGNONSTATE_CONNECTED )
|
||||
{
|
||||
SendPreConnectClientDataToServer( nSlot );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineSplitscreenClientAdded", szEvent ) )
|
||||
{
|
||||
int nSlot = pEvent->GetInt( "slot" );
|
||||
SendPreConnectClientDataToServer( nSlot );
|
||||
}
|
||||
else if ( !Q_stricmp( "Client::CmdKeyValues", szEvent ) )
|
||||
{
|
||||
KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
|
||||
if ( !pCmd )
|
||||
return;
|
||||
|
||||
char const *szCmd = pCmd->GetName();
|
||||
szCmd;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int CMatchTitle::GetEventDebugID( void )
|
||||
{
|
||||
return EVENT_DEBUG_ID_INIT;
|
||||
}
|
||||
|
||||
void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
|
||||
{
|
||||
// Check if the current match session is on an active game server
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return;
|
||||
KeyValues *pSessionSettings = pMatchSession->GetSessionSettings();
|
||||
char const *szGameServer = pSessionSettings->GetString( "server/server", "" );
|
||||
char const *szSystemLock = pSessionSettings->GetString( "system/lock", "" );
|
||||
if ( ( !szGameServer || !*szGameServer ) &&
|
||||
( !szSystemLock || !*szSystemLock ) )
|
||||
return;
|
||||
|
||||
char const *szGameMode = pSessionSettings->GetString( "game/mode", "coop" );
|
||||
szGameMode;
|
||||
|
||||
// Also don't run on the client when there's a host
|
||||
char const *szSessionType = pMatchSession->GetSessionSystemData()->GetString( "type", NULL );
|
||||
if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
|
||||
return;
|
||||
|
||||
// Parse the game event
|
||||
char const *szGameEvent = pIGameEvent->GetName();
|
||||
if ( !szGameEvent || !*szGameEvent )
|
||||
return;
|
||||
|
||||
if ( !Q_stricmp( "round_start", szGameEvent ) )
|
||||
{
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "round_end", szGameEvent ) )
|
||||
{
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnProfilesWriteOpportunity", "reason", "checkpoint"
|
||||
) );
|
||||
}
|
||||
else if ( !Q_stricmp( "finale_start", szGameEvent ) )
|
||||
{
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state finale "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "game_newmap", szGameEvent ) )
|
||||
{
|
||||
const char *szMapName = pIGameEvent->GetString( "mapname", "" );
|
||||
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
KeyValues *pInfoMission = NULL;
|
||||
KeyValues *pInfoChapter = g_pMatchExtSwarm->GetMapInfoByBspName( pSessionSettings, szMapName, &pInfoMission );
|
||||
Assert( pInfoChapter && pInfoMission );
|
||||
if ( pInfoChapter && pInfoMission )
|
||||
{
|
||||
kvUpdate->SetString( "update/game/campaign", pInfoMission->GetString( "name" ) );
|
||||
kvUpdate->SetInt( "update/game/chapter", pInfoChapter->GetInt( "chapter" ) );
|
||||
}
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnProfilesWriteOpportunity", "reason", "checkpoint"
|
||||
) );
|
||||
}
|
||||
else if ( !Q_stricmp( "difficulty_changed", szGameEvent ) )
|
||||
{
|
||||
char const *szDifficulty = pIGameEvent->GetString( "strDifficulty", "normal" );
|
||||
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" difficulty = "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
kvUpdate->SetString( "update/game/difficulty", szDifficulty );
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
else if ( !Q_stricmp( "server_pre_shutdown", szGameEvent ) )
|
||||
{
|
||||
char const *szReason = pIGameEvent->GetString( "reason", "quit" );
|
||||
if ( !Q_stricmp( szReason, "quit" ) )
|
||||
{
|
||||
DevMsg( "Received server_pre_shutdown notification - server is shutting down...\n" );
|
||||
|
||||
// Transform the server shutdown event into game end event
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnEngineDisconnectReason", "reason", "Server shutting down"
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
matchmaking/swarm/mm_title.h
Normal file
83
matchmaking/swarm/mm_title.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_H
|
||||
#define MM_TITLE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "../mm_framework.h"
|
||||
|
||||
class CMatchTitle :
|
||||
public IMatchTitle,
|
||||
public IMatchEventsSink,
|
||||
public IGameEventListener2
|
||||
{
|
||||
// Methods exposed for the framework
|
||||
public:
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Methods of IMatchTitle
|
||||
public:
|
||||
// Title ID
|
||||
virtual uint64 GetTitleID();
|
||||
|
||||
// Service ID for XLSP
|
||||
virtual uint64 GetTitleServiceID();
|
||||
|
||||
// Whether we are a single-player title or multi-player title
|
||||
virtual bool IsMultiplayer();
|
||||
|
||||
// Prepare network startup params for the title
|
||||
virtual void PrepareNetStartupParams( void *pNetStartupParams );
|
||||
|
||||
// Get total number of players supported by the title
|
||||
virtual int GetTotalNumPlayersSupported();
|
||||
|
||||
// Get a guest player name
|
||||
virtual char const * GetGuestPlayerName( int iUserIndex );
|
||||
|
||||
// Decipher title data fields
|
||||
virtual TitleDataFieldsDescription_t const * DescribeTitleDataStorage();
|
||||
|
||||
// Title achievements
|
||||
virtual TitleAchievementsDescription_t const * DescribeTitleAchievements();
|
||||
|
||||
// Title avatar awards
|
||||
virtual TitleAvatarAwardsDescription_t const * DescribeTitleAvatarAwards();
|
||||
|
||||
// Title leaderboards
|
||||
virtual KeyValues * DescribeTitleLeaderboard( char const *szLeaderboardView );
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
virtual void PrepareClientForConnect( KeyValues *pSettings );
|
||||
|
||||
// Start up a listen server with the given settings
|
||||
virtual bool StartServerMap( KeyValues *pSettings );
|
||||
|
||||
// Methods of IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
// Methods of IGameEventListener2
|
||||
public:
|
||||
// FireEvent is called by EventManager if event just occured
|
||||
// KeyValue memory will be freed by manager if not needed anymore
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
virtual int GetEventDebugID( void );
|
||||
|
||||
public:
|
||||
CMatchTitle();
|
||||
~CMatchTitle();
|
||||
};
|
||||
|
||||
// Match title singleton
|
||||
extern CMatchTitle *g_pMatchTitle;
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
1076
matchmaking/swarm/mm_title_gamesettingsmgr.cpp
Normal file
1076
matchmaking/swarm/mm_title_gamesettingsmgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
43
matchmaking/swarm/mm_title_main.cpp
Normal file
43
matchmaking/swarm/mm_title_main.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "../mm_title_main.h"
|
||||
|
||||
#include "matchext_swarm.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
LINK_MATCHMAKING_LIB();
|
||||
|
||||
|
||||
static CMatchTitle g_MatchTitle;
|
||||
CMatchTitle *g_pMatchTitle = &g_MatchTitle;
|
||||
|
||||
IMatchTitle *g_pIMatchTitle = g_pMatchTitle;
|
||||
IMatchEventsSink *g_pIMatchTitleEventsSink = g_pMatchTitle;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t MM_Title_Init()
|
||||
{
|
||||
return g_pMatchTitle->Init();
|
||||
}
|
||||
|
||||
void MM_Title_Shutdown()
|
||||
{
|
||||
if ( g_pMatchTitle )
|
||||
g_pMatchTitle->Shutdown();
|
||||
}
|
||||
|
||||
241
matchmaking/swarm/mm_title_richpresence.cpp
Normal file
241
matchmaking/swarm/mm_title_richpresence.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "swarm.spa.h"
|
||||
|
||||
#include "matchext_swarm.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//
|
||||
// Mapping of context values
|
||||
//
|
||||
|
||||
ContextValue_t g_pcv_CONTEXT_GAME_MODE[] = {
|
||||
{ "versus", CONTEXT_GAME_MODE_VERSUS },
|
||||
{ "teamversus", CONTEXT_GAME_MODE_TEAMVERSUS },
|
||||
{ "scavenge", CONTEXT_GAME_MODE_SCAVENGE },
|
||||
{ "teamscavenge", CONTEXT_GAME_MODE_TEAMSCAVENGE },
|
||||
{ "survival", CONTEXT_GAME_MODE_SURVIVAL },
|
||||
{ "realism", CONTEXT_GAME_MODE_REALISM },
|
||||
{ NULL, CONTEXT_GAME_MODE_COOP },
|
||||
};
|
||||
|
||||
ContextValue_t g_pcv_CONTEXT_STATE[] = {
|
||||
{ "game", CONTEXT_STATE_GAME },
|
||||
{ "finale", CONTEXT_STATE_FINALE },
|
||||
{ NULL, CONTEXT_STATE_LOBBY },
|
||||
};
|
||||
|
||||
ContextValue_t g_pcv_CONTEXT_DIFFICULTY[] = {
|
||||
{ "easy", CONTEXT_DIFFICULTY_EASY },
|
||||
{ "hard", CONTEXT_DIFFICULTY_HARD },
|
||||
{ "impossible", CONTEXT_DIFFICULTY_IMPOSSIBLE },
|
||||
{ NULL, CONTEXT_DIFFICULTY_NORMAL },
|
||||
};
|
||||
|
||||
ContextValue_t g_pcv_CONTEXT_ACCESS[] = {
|
||||
{ "public", CONTEXT_ACCESS_PUBLIC },
|
||||
{ "friends", CONTEXT_ACCESS_FRIENDS },
|
||||
{ NULL, CONTEXT_ACCESS_PRIVATE },
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// User context and property setting
|
||||
//
|
||||
|
||||
static void SetAllUsersContext( DWORD dwContextId, DWORD dwValue, bool bAsync = true )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
if ( bAsync )
|
||||
XUserSetContextEx( iCtrlr, dwContextId, dwValue, MMX360_NewOverlappedDormant() );
|
||||
else
|
||||
XUserSetContext( iCtrlr, dwContextId, dwValue );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetAllUsersProperty( DWORD dwPropertyId, DWORD cbValue, void const *pvValue )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
XUserSetPropertyEx( iCtrlr, dwPropertyId, cbValue, pvValue, MMX360_NewOverlappedDormant() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings )
|
||||
{
|
||||
if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_GAME_MODE, g_pcv_CONTEXT_GAME_MODE->ScanValues( szValue ), false );
|
||||
}
|
||||
|
||||
// matchmaking version
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
extern ConVar mm_matchmaking_version;
|
||||
val = mm_matchmaking_version.GetInt();
|
||||
SetAllUsersProperty( PROPERTY_MMVERSION, sizeof( val ), &val );
|
||||
DevMsg( "PrepareForSessionCreate: matchmaking version %d\n", val );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings )
|
||||
{
|
||||
if ( !pFullSettings )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_PRESENCE, 1 ); // main menu
|
||||
return;
|
||||
}
|
||||
|
||||
// Also set players information during initial rich presence update
|
||||
if ( !pUpdatedSettings && pFullSettings )
|
||||
{
|
||||
MM_Title_RichPresence_PlayersChanged( pFullSettings );
|
||||
}
|
||||
|
||||
// pUpdatedSettings = NULL when the session is created and all contexts need to be set
|
||||
KeyValues *pNewSettings = pUpdatedSettings ? pUpdatedSettings : pFullSettings;
|
||||
|
||||
// Current mission/map (can be NULL!)
|
||||
char const *szCampaign = pFullSettings->GetString( "game/campaign", "" );
|
||||
KeyValues *pInfoMission = szCampaign[0] ? g_pMatchExtSwarm->GetAllMissions()->FindKey( szCampaign ) : NULL;
|
||||
KeyValues *pInfoChapter = g_pMatchExtSwarm->GetMapInfo( pFullSettings );
|
||||
|
||||
if ( char const *szValue = pNewSettings->GetString( "system/access", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_ACCESS, g_pcv_CONTEXT_ACCESS->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( char const *szValue = pNewSettings->GetString( "game/state", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_STATE, g_pcv_CONTEXT_STATE->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( char const *szValue = pNewSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_GAME_MODE, g_pcv_CONTEXT_GAME_MODE->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( char const *szValue = pNewSettings->GetString( "game/difficulty", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_DIFFICULTY, g_pcv_CONTEXT_DIFFICULTY->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( KeyValues *kvVal = pNewSettings->FindKey( "game/maxrounds" ) )
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = kvVal->GetInt();
|
||||
SetAllUsersProperty( PROPERTY_MAXROUNDS, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
bool bSetLevelDescription = false;
|
||||
|
||||
if ( char const *szValue = pNewSettings->GetString( "game/campaign", NULL ) )
|
||||
{
|
||||
int iCampaign = pInfoMission->GetInt( "x360ctx", CONTEXT_CAMPAIGN_ANY );
|
||||
|
||||
SetAllUsersContext( CONTEXT_CAMPAIGN, iCampaign );
|
||||
bSetLevelDescription = true;
|
||||
}
|
||||
|
||||
if ( KeyValues *kvVal = pNewSettings->FindKey( "game/chapter" ) )
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = kvVal->GetInt();
|
||||
SetAllUsersProperty( PROPERTY_CHAPTER, sizeof( val ), &val );
|
||||
bSetLevelDescription = true;
|
||||
}
|
||||
|
||||
if ( bSetLevelDescription )
|
||||
{
|
||||
int iContext = CONTEXT_LEVELDESCRIPTION_ANY;
|
||||
if ( pInfoChapter )
|
||||
iContext = pInfoChapter->GetInt( "x360ctx", iContext );
|
||||
else if ( pInfoMission )
|
||||
iContext = pInfoMission->GetInt( "x360ctx", iContext ); // mission contexts correspond to chapters too
|
||||
SetAllUsersContext( CONTEXT_LEVELDESCRIPTION, iContext );
|
||||
}
|
||||
|
||||
if ( KeyValues *kvVal = pNewSettings->FindKey( "game/dlcrequired" ) )
|
||||
{
|
||||
static int val[10]; // must be valid for the async call
|
||||
uint64 uiDlcRequired = kvVal->GetUint64();
|
||||
extern ConVar mm_matchmaking_dlcsquery;
|
||||
for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
|
||||
{
|
||||
val[k] = !!( uiDlcRequired & ( 1ull << k ) );
|
||||
DevMsg( "DLC%d required: %d\n", k, val[k] );
|
||||
SetAllUsersProperty( PROPERTY_MMVERSION + k, sizeof( val ), &val );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Determine Rich Presence Display
|
||||
//
|
||||
if ( char const *szGameModeForRichPresence = pFullSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
unsigned int dwLevelPresence = CONTEXT_PRESENCE_MAINMENU;
|
||||
|
||||
static ContextValue_t values[] = {
|
||||
{ "versus", CONTEXT_PRESENCE_MODE_GAP_MISSION_LOBBY },
|
||||
{ "teamversus", CONTEXT_PRESENCE_MODE_GAP_MISSION_LOBBY },
|
||||
|
||||
{ "survival", CONTEXT_PRESENCE_MODE_GAP_LEVEL_LOBBY },
|
||||
{ "scavenge", CONTEXT_PRESENCE_MODE_GAP_LEVEL_LOBBY },
|
||||
{ "teamscavenge", CONTEXT_PRESENCE_MODE_GAP_LEVEL_LOBBY },
|
||||
|
||||
{ "coop", CONTEXT_PRESENCE_MODE_DIFF_MISSION_LOBBY },
|
||||
{ "realism", CONTEXT_PRESENCE_MODE_DIFF_MISSION_LOBBY },
|
||||
|
||||
{ NULL, CONTEXT_PRESENCE_MAINMENU },
|
||||
};
|
||||
|
||||
dwLevelPresence = values->ScanValues( szGameModeForRichPresence );
|
||||
|
||||
// Hook for offline game
|
||||
if ( !Q_stricmp( "offline", pFullSettings->GetString( "system/network" ) ) )
|
||||
dwLevelPresence = CONTEXT_PRESENCE_LOCAL_DIFF_MISSION_SETTINGS;
|
||||
|
||||
// Special hook for developers commentary
|
||||
if ( !Q_stricmp( "commentary", pFullSettings->GetString( "options/play" ) ) )
|
||||
dwLevelPresence = CONTEXT_PRESENCE_DEVELOPER_COMM_SETTINGS;
|
||||
|
||||
// If the game is active, then use the +1 presence
|
||||
if ( Q_stricmp( "lobby", pFullSettings->GetString( "game/state", "lobby" ) ) )
|
||||
++ dwLevelPresence;
|
||||
|
||||
dwLevelPresence = pInfoMission->GetInt( "x360presence", dwLevelPresence ); // let the mission override
|
||||
dwLevelPresence = pInfoChapter->GetInt( "x360presence", dwLevelPresence ); // let the chapter override
|
||||
|
||||
SetAllUsersContext( X_CONTEXT_PRESENCE, dwLevelPresence );
|
||||
}
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings )
|
||||
{
|
||||
if ( int numPlayers = pFullSettings->GetInt( "members/numPlayers" ) )
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = numPlayers;
|
||||
SetAllUsersProperty( PROPERTY_NUMPLAYERS, sizeof( val ), &val );
|
||||
}
|
||||
}
|
||||
25
matchmaking/swarm/mm_title_richpresence.h
Normal file
25
matchmaking/swarm/mm_title_richpresence.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_RICHPRESENCE_H
|
||||
#define MM_TITLE_RICHPRESENCE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_title.h"
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings );
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings );
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings );
|
||||
|
||||
extern ContextValue_t g_pcv_CONTEXT_ACCESS[];
|
||||
extern ContextValue_t g_pcv_CONTEXT_GAME_MODE[];
|
||||
extern ContextValue_t g_pcv_CONTEXT_STATE[];
|
||||
extern ContextValue_t g_pcv_CONTEXT_DIFFICULTY[];
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
143
matchmaking/swarm/mm_title_titledata.cpp
Normal file
143
matchmaking/swarm/mm_title_titledata.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "matchext_swarm.h"
|
||||
#include "swarm.spa.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
TitleDataFieldsDescription_t const * CMatchTitle::DescribeTitleDataStorage()
|
||||
{
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }
|
||||
|
||||
static TitleDataFieldsDescription_t tdfd[] =
|
||||
{
|
||||
#if 0
|
||||
// ACHIEVEMENTS
|
||||
TD_ENTRY( "TD2.COUNT.ACH_BEAT_CAMPAIGNS_EXPERT_MODE", DB_TD2, DT_U8, offsetof( TitleData2, iCountBeatCampaignsExpertMode ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_CUT_OFF_HEADS_MELEE", DB_TD2, DT_U8, offsetof( TitleData2, iCountCutOffHeadsMelee ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_KILL_WITH_EVERY_MELEE", DB_TD2, DT_U8, offsetof( TitleData2, iCountKillWithEveryMelee ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_KILL_INFECTED_WITH_CHAINSAW", DB_TD2, DT_U8, offsetof( TitleData2, iCountKillInfectedWithChainsaw ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_RES_SURVIVORS_WITH_DEFIB", DB_TD2, DT_U8, offsetof( TitleData2, iCountResSurvivorsWithDefib ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_SPEED_REVIVE_WITH_ADRENALINE", DB_TD2, DT_U8, offsetof( TitleData2, iCountSpeedReviveWithAdrenaline ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_IGNITE_INFECTED_FIRE_AMMO", DB_TD2, DT_U8, offsetof( TitleData2, iCountIgniteInfectedFireAmmo ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_KILL_EVERY_UNCOMMON_INFECTED", DB_TD2, DT_U8, offsetof( TitleData2, iCountKillEveryUncommonInfected ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_KILL_SUBMERGED_MUDMEN", DB_TD2, DT_U8, offsetof( TitleData2, iCountKillSubmergedMudmen ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_COLLECT_CEDA_VIALS", DB_TD2, DT_U8, offsetof( TitleData2, iCountCollectCEDAVials ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_SCAVENGE_COLLECT_CAN_GRIND", DB_TD2, DT_U8, offsetof( TitleData2, iCountScavengeCollectCanGrind ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_SCAVENGE_CAN_DROP_GRIND", DB_TD2, DT_U8, offsetof( TitleData2, iCountScavengeCanDropGrind ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ACH_HONK_A_CLOWNS_NOSE", DB_TD2, DT_U8, offsetof( TitleData2, iCountHonkAClownsNose ) ),
|
||||
TD_ENTRY( "TD2.COMP.ACH_BEAT_CAMPAIGNS_EXPERT_MODE", DB_TD2, DT_U8, offsetof( TitleData2, iCompBeatCampaignsExpertMode ) ),
|
||||
TD_ENTRY( "TD2.COMP.ACH_KILL_WITH_EVERY_MELEE", DB_TD2, DT_U16, offsetof( TitleData2, iCompKillWithEveryMelee ) ),
|
||||
TD_ENTRY( "TD2.COMP.ACH_KILL_EVERY_UNCOMMON_INFECTED", DB_TD2, DT_U16, offsetof( TitleData2, iCompKillEveryUncommonInfected ) ),
|
||||
|
||||
// AVATAR AWARDS
|
||||
TD_ENTRY( "TD2.COUNT.ASSET_MED_KIT", DB_TD2, DT_U8, offsetof( TitleData2, iCountBeatCampaignsAnyMode ) ),
|
||||
TD_ENTRY( "TD2.COMP.ASSET_MED_KIT", DB_TD2, DT_U8, offsetof( TitleData2, iCompBeatCampaignsAnyMode ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ASSET_PIPE_BOMB", DB_TD2, DT_U16, offsetof( TitleData2, iCountKillTenThousandZombies ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ASSET_GAS_CAN", DB_TD2, DT_U8, offsetof( TitleData2, iCountWinScavengerMatches ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ASSET_FRYING_PAN", DB_TD2, DT_U8, offsetof( TitleData2, iCountWinVersusMatches ) ),
|
||||
|
||||
// ZOMBIE PANEL SEEN COUNTS
|
||||
TD_ENTRY( "TD2.COUNT.ZOMBIE_PANEL_INTRO", DB_TD2, DT_U8, offsetof( TitleData2, iCountZombiePanelIntro ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ZOMBIE_PANEL_SMOKER", DB_TD2, DT_U8, offsetof( TitleData2, iCountZombiePanelSmoker ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ZOMBIE_PANEL_BOOMER", DB_TD2, DT_U8, offsetof( TitleData2, iCountZombiePanelBoomer ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ZOMBIE_PANEL_HUNTER", DB_TD2, DT_U8, offsetof( TitleData2, iCountZombiePanelHunter ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ZOMBIE_PANEL_SPITTER", DB_TD2, DT_U8, offsetof( TitleData2, iCountZombiePanelSpitter ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ZOMBIE_PANEL_JOCKEY", DB_TD2, DT_U8, offsetof( TitleData2, iCountZombiePanelJockey ) ),
|
||||
TD_ENTRY( "TD2.COUNT.ZOMBIE_PANEL_CHARGER", DB_TD2, DT_U8, offsetof( TitleData2, iCountZombiePanelCharger ) ),
|
||||
#endif
|
||||
|
||||
// END MARKER
|
||||
TD_ENTRY( NULL, DB_TD1, DT_U8, 0 )
|
||||
};
|
||||
|
||||
#undef TD_ENTRY
|
||||
|
||||
return tdfd;
|
||||
}
|
||||
|
||||
TitleAchievementsDescription_t const * CMatchTitle::DescribeTitleAchievements()
|
||||
{
|
||||
static TitleAchievementsDescription_t tad[] =
|
||||
{
|
||||
#include "swarm.xhelp.achtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return tad;
|
||||
}
|
||||
|
||||
TitleAvatarAwardsDescription_t const * CMatchTitle::DescribeTitleAvatarAwards()
|
||||
{
|
||||
static TitleAvatarAwardsDescription_t taad[] =
|
||||
{
|
||||
#include "swarm.xhelp.avawtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return taad;
|
||||
}
|
||||
|
||||
// Title leaderboards
|
||||
KeyValues * CMatchTitle::DescribeTitleLeaderboard( char const *szLeaderboardView )
|
||||
{
|
||||
// Check if this is a survival leaderboard
|
||||
if ( char const *szSurvivalMap = StringAfterPrefix( szLeaderboardView, "survival_" ) )
|
||||
{
|
||||
if ( IsX360() )
|
||||
{
|
||||
// Find the corresponding record in the mission script
|
||||
KeyValues *pSettings = new KeyValues( "settings" );
|
||||
KeyValues::AutoDelete autodelete_pSettings( pSettings );
|
||||
pSettings->SetString( "game/mode", "survival" );
|
||||
|
||||
KeyValues *pMissionInfo = NULL;
|
||||
KeyValues *pMapInfo = g_pMatchExtSwarm->GetMapInfoByBspName( pSettings, szSurvivalMap, &pMissionInfo );
|
||||
if ( !pMapInfo || !pMissionInfo )
|
||||
return NULL;
|
||||
|
||||
// Find the leaderboard description in the map info
|
||||
KeyValues *pLbDesc = pMapInfo->FindKey( "x360leaderboard" );
|
||||
if ( !pLbDesc )
|
||||
return NULL;
|
||||
|
||||
// Insert the required keys
|
||||
pLbDesc = pLbDesc->MakeCopy();
|
||||
|
||||
static KeyValues *s_pRatingKey = KeyValues::FromString( ":rating", // X360 leaderboards are rated
|
||||
" name besttime " // game name of the rating field is "besttime"
|
||||
" type uint64 " // type is uint64
|
||||
" rule max" // rated field must be greater than cached value so that it can be written
|
||||
);
|
||||
pLbDesc->AddSubKey( s_pRatingKey->MakeCopy() );
|
||||
pLbDesc->SetString( "besttime/type", "uint64" );
|
||||
|
||||
return pLbDesc;
|
||||
}
|
||||
|
||||
if ( IsPC() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score besttime " // :score is the leaderboard value mapped to game name "besttime"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeTimeMilliSeconds ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user