initial
This commit is contained in:
912
matchmaking/portal2/mm_title.cpp
Normal file
912
matchmaking/portal2/mm_title.cpp
Normal file
@@ -0,0 +1,912 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "portal2.spa.h"
|
||||
|
||||
#ifdef _PS3
|
||||
#include <netex/net.h>
|
||||
#include <netex/libnetctl.h>
|
||||
#endif
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
#include "matchmaking/portal2/imatchext_portal2.h"
|
||||
#include "matchmaking/mm_helpers.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 );
|
||||
}
|
||||
|
||||
#ifndef SWDS
|
||||
// Initialize Title Update version
|
||||
extern ConVar mm_tu_string;
|
||||
mm_tu_string.SetValue( "20110805" );
|
||||
#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_PORTAL_2_DISC_XBOX_360;
|
||||
#else
|
||||
return TITLEID_PORTAL_2_DEMO;
|
||||
#endif
|
||||
#elif !defined( SWDS ) && !defined( NO_STEAM )
|
||||
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
|
||||
}
|
||||
|
||||
uint64 CMatchTitle::GetTitleSettingsFlags()
|
||||
{
|
||||
return MATCHTITLE_SETTING_MULTIPLAYER
|
||||
| MATCHTITLE_SETTING_NODEDICATED
|
||||
| MATCHTITLE_PLAYERMGR_DISABLED
|
||||
| MATCHTITLE_SERVERMGR_DISABLED
|
||||
| MATCHTITLE_INVITE_ONLY_SINGLE_USER
|
||||
;
|
||||
}
|
||||
|
||||
#ifdef _PS3
|
||||
void *g_pMatchTitle_NetMemory;
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
#if defined( _PS3 ) && defined( NO_STEAM )
|
||||
MEM_ALLOC_CREDIT_( "NO_STEAM: CMatchTitle::PrepareNetStartupParams" );
|
||||
sys_net_initialize_parameter_t &snip = *( sys_net_initialize_parameter_t * ) pNetStartupParams;
|
||||
|
||||
snip.memory_size = 512 * 1024;
|
||||
snip.memory = malloc( snip.memory_size ); // alternatively this can be a global array
|
||||
|
||||
g_pMatchTitle_NetMemory = snip.memory; // bookmark the memory address for later inspection if necessary
|
||||
#endif
|
||||
}
|
||||
|
||||
int CMatchTitle::GetTotalNumPlayersSupported()
|
||||
{
|
||||
// Portal 2 is a 2-player game
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Get a guest player name
|
||||
char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
|
||||
{
|
||||
if ( vgui::IVGUILocalize *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 _GAMECONSOLE
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
//
|
||||
// Now we set the convars
|
||||
//
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
int iController = k;
|
||||
#ifdef _GAMECONSOLE
|
||||
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 _GAMECONSOLE
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
char const *szMap = pSettings->GetString( "game/map", NULL );
|
||||
if ( !szMap )
|
||||
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( szMap ) )
|
||||
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" );
|
||||
|
||||
char const *szPlayOptions = pSettings->GetString( "options/play", "" );
|
||||
if ( !Q_stricmp( "commentary", szPlayOptions ) )
|
||||
{
|
||||
pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
|
||||
}
|
||||
else if ( !Q_stricmp( "challenge", szPlayOptions ) )
|
||||
{
|
||||
pGameDllReserve->SetString( "options/play", "challenge" );
|
||||
}
|
||||
|
||||
// Run map based off the faked reservation packet
|
||||
g_pMatchExtensions->GetIServerGameDLL()->ApplyGameSettings( pGameDllReserve );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMatchTitle::RunFrame()
|
||||
{
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pIMatchSession )
|
||||
return;
|
||||
|
||||
if ( pIMatchSession->GetSessionSettings()->GetBool( "game/sv_cheats" ) )
|
||||
return; // already flagged as cheats session
|
||||
|
||||
// capture either currently set or has been set during this session
|
||||
static ConVarRef ref_sv_cheats_flagged( "sv_cheats_flagged" );
|
||||
if ( ( ref_sv_cheats_flagged.IsValid() && !ref_sv_cheats_flagged.GetBool() ) || !g_pMatchExtensions->GetIVEngineClient()->IsConnected() )
|
||||
return;
|
||||
|
||||
// Bypassing session update rules, each client can flag sv_cheats
|
||||
// separately once they see it before server session sees sv_cheats
|
||||
pIMatchSession->GetSessionSettings()->SetInt( "game/sv_cheats", 1 );
|
||||
}
|
||||
|
||||
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 _GAMECONSOLE
|
||||
iController = XBX_GetUserId( nSlot );
|
||||
#endif
|
||||
|
||||
// Portal 2 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 );
|
||||
}
|
||||
|
||||
static bool MatchSessionIsSinglePlayerOnline( char const *szGameType )
|
||||
{
|
||||
if ( XBX_GetNumGameUsers() != 1 )
|
||||
return false; // not playing with a single committed profile
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pIMatchSession )
|
||||
return false; // don't have a valid session
|
||||
KeyValues *pSettings = pIMatchSession->GetSessionSettings();
|
||||
if ( Q_stricmp( pSettings->GetString( "system/network" ), "LIVE" ) )
|
||||
return false; // session is not online
|
||||
if ( szGameType )
|
||||
{
|
||||
if ( Q_stricmp( pSettings->GetString( "game/type" ), szGameType ) )
|
||||
return false; // session is not correct game type
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MatchSessionPlayersAreFriends( int iLocalController, XUID xuidPartner )
|
||||
{
|
||||
if ( !xuidPartner )
|
||||
return false;
|
||||
#ifdef _X360
|
||||
BOOL bFriend = FALSE;
|
||||
if ( ERROR_SUCCESS != XUserAreUsersFriends( iLocalController, &xuidPartner, 1, &bFriend, NULL ) )
|
||||
return false;
|
||||
if ( !bFriend )
|
||||
return false;
|
||||
#else
|
||||
#ifndef NO_STEAM
|
||||
if ( !steamapicontext->SteamFriends()->HasFriend( xuidPartner, /*k_EFriendFlagImmediate*/ 0x04 ) )
|
||||
#endif
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static void MatchSessionUpdateSinglePlayerProgress( char const *szMap )
|
||||
{
|
||||
if ( XBX_GetNumGameUsers() != 1 )
|
||||
return;
|
||||
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pIMatchSession )
|
||||
return;
|
||||
|
||||
KeyValues *pSettings = pIMatchSession->GetSessionSettings();
|
||||
if ( Q_stricmp( pSettings->GetString( "system/network" ), "offline" ) )
|
||||
return;
|
||||
if ( Q_stricmp( pSettings->GetString( "game/mode" ), "sp" ) )
|
||||
return;
|
||||
|
||||
// Ok, we've got a single player offline session with one user
|
||||
IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
||||
if ( !pPlayer )
|
||||
return;
|
||||
|
||||
static ContextValue_t s_SP_MAP_2_PROGRESS[] = {
|
||||
#define CFG( spmapname, chapternum, subchapter ) { #spmapname, chapternum },
|
||||
#include "inc_sp_maps.inc"
|
||||
#undef CFG
|
||||
{ NULL, 0 },
|
||||
};
|
||||
uint32 uiChapterNum = s_SP_MAP_2_PROGRESS->ScanValues( szMap );
|
||||
if ( !uiChapterNum )
|
||||
return;
|
||||
|
||||
// Locate the single player progress field
|
||||
TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
|
||||
fields = TitleDataFieldsDescriptionFindByString( fields, "SP.progress" );
|
||||
if ( !fields )
|
||||
return;
|
||||
|
||||
uint32 uiCurrentlyMaxChapter = TitleDataFieldsDescriptionGetValue<uint32>( fields, pPlayer );
|
||||
if ( uiChapterNum <= uiCurrentlyMaxChapter )
|
||||
return;
|
||||
|
||||
// Update the single player progress
|
||||
TitleDataFieldsDescriptionSetValue<uint32>( fields, pPlayer, uiChapterNum );
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
static bool MatchSessionSetAchievementBasedOnComponents( T valComponent, char const *szAchievement, int numComponentFields, IPlayerLocal *pPlayerLocal )
|
||||
{
|
||||
TitleDataFieldsDescription_t const *field1 = TitleDataFieldsDescriptionFindByString(
|
||||
g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage(), CFmtStr( "%s[1]", szAchievement ) );
|
||||
if ( !field1 )
|
||||
return false;
|
||||
int iComponent = 0;
|
||||
for ( ; iComponent < numComponentFields; ++ iComponent, ++ field1 )
|
||||
{
|
||||
T valSlot = TitleDataFieldsDescriptionGetValue<T>( field1, pPlayerLocal );
|
||||
if ( valSlot == valComponent )
|
||||
return false; // already have such component
|
||||
if ( !valSlot )
|
||||
{
|
||||
TitleDataFieldsDescriptionSetValue<T>( field1, pPlayerLocal, valComponent );
|
||||
++ iComponent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( iComponent < numComponentFields )
|
||||
return false; // not enough components met yet
|
||||
|
||||
// Awesome, we are eligible for achievement
|
||||
{
|
||||
KeyValues *kvAwardAch = new KeyValues( "" );
|
||||
KeyValues::AutoDelete autodelete_kvAwardAch( kvAwardAch );
|
||||
kvAwardAch->SetInt( szAchievement, 1 );
|
||||
pPlayerLocal->UpdateAwardsData( kvAwardAch );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#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( "OnPlayerMachinesConnected", szEvent ) ||
|
||||
!Q_stricmp( "OnPlayerMachinesDisconnected", szEvent ) )
|
||||
{
|
||||
// Player counts changed on host, update aggregate fields
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return;
|
||||
KeyValues *kvPackage = new KeyValues( "Update" );
|
||||
if ( KeyValues *kvUpdate = kvPackage->FindKey( "update", true ) )
|
||||
{
|
||||
void UpdateAggregateMembersSettings( KeyValues *pFullGameSettings, KeyValues *pUpdate );
|
||||
UpdateAggregateMembersSettings( pMatchSession->GetSessionSettings(), kvUpdate );
|
||||
}
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( kvPackage ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnProfileDataSaved", szEvent ) )
|
||||
{
|
||||
// Player profile data updated, recompute skills
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return;
|
||||
|
||||
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( pEvent->GetInt( "iController" ) );
|
||||
if ( !player )
|
||||
return;
|
||||
|
||||
// Initialize member settings on a temporary copy of session settings
|
||||
KeyValues *kvPlayerUpdate = pMatchSession->GetSessionSettings()->MakeCopy();
|
||||
KeyValues::AutoDelete autodelete_kvPlayerUpdate( kvPlayerUpdate );
|
||||
|
||||
void InitializeMemberSettings( KeyValues *pSettings );
|
||||
InitializeMemberSettings( kvPlayerUpdate );
|
||||
|
||||
// Find the updated player info
|
||||
KeyValues *pPlayerData = SessionMembersFindPlayer( kvPlayerUpdate, player->GetXUID() );
|
||||
if ( !pPlayerData || !pPlayerData->FindKey( "game" ) )
|
||||
return;
|
||||
|
||||
// Send the request to the host to update our player info
|
||||
KeyValues *pRequest = new KeyValues( "Game::PlayerInfo" );
|
||||
KeyValues::AutoDelete autodelete( pRequest );
|
||||
pRequest->SetString( "run", "host" );
|
||||
pRequest->SetUint64( "xuid", player->GetXUID() );
|
||||
pRequest->AddSubKey( pPlayerData->FindKey( "game" )->MakeCopy() );
|
||||
pMatchSession->Command( pRequest );
|
||||
}
|
||||
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( szEvent, "OnPlayerAward" ) )
|
||||
{
|
||||
int iCtrlr = pEvent->GetInt( "iController" );
|
||||
IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iCtrlr );
|
||||
if ( !pPlayerLocal )
|
||||
return;
|
||||
|
||||
char const *szAward = pEvent->GetString( "award" );
|
||||
|
||||
// ACH.TEACHER implementation is based off ACH.HI_FIVE_YOUR_PARTNER
|
||||
if ( !Q_stricmp( szAward, "ACH.HI_FIVE_YOUR_PARTNER" ) )
|
||||
{
|
||||
// We are being awarded a calibration success achievement
|
||||
// check that we are in an online session, that we haven't
|
||||
// yet completed any maps and haven't gotten any other
|
||||
// achievements except for ACH.HI_FIVE_YOUR_PARTNER that we
|
||||
// have just been awarded
|
||||
if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
|
||||
return;
|
||||
TitleData1 const *td1 = ( TitleData1 const * ) pPlayerLocal->GetPlayerTitleData( TitleDataFieldsDescription_t::DB_TD1 );
|
||||
if ( !td1 )
|
||||
return; // failed to get TD1
|
||||
else
|
||||
{
|
||||
TitleData1 td1copy = *td1;
|
||||
// we don't care whether the newbie played mp_coop_lobby_2 map
|
||||
uint8 *pBits = ( uint8 * ) td1copy.coop.mapbits;
|
||||
COMPILE_TIME_ASSERT( TitleData1::CoopData_t::mp_coop_lobby_2 < 8*sizeof( uint8 ) );
|
||||
pBits[0] &=~ ( 1 << TitleData1::CoopData_t::mp_coop_lobby_2 );
|
||||
// compare our newbie mapbits with all zeroes
|
||||
uint32 allzeroes[ ARRAYSIZE( td1copy.coop.mapbits ) ];
|
||||
COMPILE_TIME_ASSERT( sizeof( allzeroes ) == sizeof( td1copy.coop.mapbits ) );
|
||||
Q_memset( allzeroes, 0, sizeof( allzeroes ) );
|
||||
if ( Q_memcmp( allzeroes, td1copy.coop.mapbits, sizeof( allzeroes ) ) )
|
||||
return; // we aren't a newbie, played some other maps already
|
||||
}
|
||||
|
||||
// We are a newbie who just received the achievement, let our potential teacher know
|
||||
int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
|
||||
g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( XBX_GetSlotByUserId( iCtrlr ) );
|
||||
KeyValues *pServerEvent = new KeyValues( "OnPlayerAward" );
|
||||
pServerEvent->SetString( "award", "ACH.HI_FIVE_YOUR_PARTNER" );
|
||||
pServerEvent->SetUint64( "xuid", pPlayerLocal->GetXUID() );
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
pServerEvent->SetUint64( "psnid", steamapicontext->SteamUser()->GetConsoleSteamID().ConvertToUint64() );
|
||||
#endif
|
||||
pServerEvent->SetInt( "newbie", 1 );
|
||||
g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( pServerEvent );
|
||||
g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
|
||||
}
|
||||
else if ( !Q_stricmp( szAward, "ACH.SHOOT_THE_MOON" ) )
|
||||
{
|
||||
// Unlock player progress towards the credits map
|
||||
MatchSessionUpdateSinglePlayerProgress( "sp_a5_credits" );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( szEvent, "Client::CmdKeyValues" ) )
|
||||
{
|
||||
KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
|
||||
if ( !pCmd )
|
||||
return;
|
||||
|
||||
int nSlot = pEvent->GetInt( "slot" );
|
||||
int iCtrlr = XBX_GetUserId( nSlot );
|
||||
IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iCtrlr );
|
||||
if ( !pPlayerLocal )
|
||||
return;
|
||||
|
||||
char const *szCmd = pCmd->GetName();
|
||||
if ( !Q_stricmp( "OnPlayerAward", szCmd ) )
|
||||
{
|
||||
//
|
||||
// ACH.TEACHER implementation
|
||||
//
|
||||
// our partner received an award
|
||||
if ( !Q_stricmp( pCmd->GetString( "award" ), "ACH.HI_FIVE_YOUR_PARTNER" ) )
|
||||
{
|
||||
if ( pCmd->GetInt( "newbie" ) != 1 )
|
||||
return;
|
||||
// Our newbie partner is being awarded a calibration success achievement
|
||||
// check that we are in an online session and that we have
|
||||
// already completed all maps
|
||||
if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
|
||||
return;
|
||||
TitleData1 const *td1 = ( TitleData1 const * ) pPlayerLocal->GetPlayerTitleData( TitleDataFieldsDescription_t::DB_TD1 );
|
||||
if ( !td1 )
|
||||
return; // failed to get TD1
|
||||
|
||||
{
|
||||
// We must have the HiFive and NewBlood achievements
|
||||
KeyValues *kvAchievementsEarned = new KeyValues( "" );
|
||||
KeyValues::AutoDelete autodelete_kvAchievementsEarned( kvAchievementsEarned );
|
||||
kvAchievementsEarned->SetInt( "ACH.HI_FIVE_YOUR_PARTNER", 0 );
|
||||
kvAchievementsEarned->SetInt( "ACH.NEW_BLOOD", 0 );
|
||||
pPlayerLocal->GetAwardsData( kvAchievementsEarned );
|
||||
if ( !kvAchievementsEarned->GetInt( "ACH.HI_FIVE_YOUR_PARTNER" ) )
|
||||
return;
|
||||
if ( !kvAchievementsEarned->GetInt( "ACH.NEW_BLOOD" ) )
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// we must have completed all coop maps
|
||||
uint8 *pBits = ( uint8 * ) td1->coop.mapbits;
|
||||
for ( int k = 0; k < TitleData1::CoopData_t::mapbits_total_basegame; ++ k )
|
||||
{
|
||||
if ( k != TitleData1::CoopData_t::mp_coop_start && k != TitleData1::CoopData_t::mp_coop_lobby_2 && k != TitleData1::CoopData_t::mp_coop_credits )
|
||||
{
|
||||
if ( !( pBits[ k/8 ] & ( 1 << (k%8) ) ) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We must be friends with this newbie
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pCmd->GetUint64( "psnid" ) ) )
|
||||
#endif
|
||||
if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pCmd->GetUint64( "xuid" ) ) )
|
||||
return;
|
||||
|
||||
// Awesome, we are eligible for ACH.TEACHER
|
||||
{
|
||||
KeyValues *kvAwardTeacher = new KeyValues( "" );
|
||||
KeyValues::AutoDelete autodelete_kvAwardTeacher( kvAwardTeacher );
|
||||
kvAwardTeacher->SetInt( "ACH.TEACHER", 1 );
|
||||
pPlayerLocal->UpdateAwardsData( kvAwardTeacher );
|
||||
}
|
||||
}
|
||||
//
|
||||
// ACH.SPREAD_THE_LOVE implementation
|
||||
//
|
||||
else if ( !Q_stricmp( pCmd->GetString( "award" ), "ACH.SPREAD_THE_LOVE" ) )
|
||||
{
|
||||
if ( pCmd->GetInt( "hugged" ) != 1 )
|
||||
return;
|
||||
if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
|
||||
return;
|
||||
// We must be friends with the person we've hugged
|
||||
XUID xuidHugged = pCmd->GetUint64( "xuid" );
|
||||
if ( xuidHugged == pPlayerLocal->GetXUID() )
|
||||
return;
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pCmd->GetUint64( "psnid" ) ) )
|
||||
#endif
|
||||
if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), xuidHugged ) )
|
||||
return;
|
||||
|
||||
// Set achievement component
|
||||
bool bAchieved = MatchSessionSetAchievementBasedOnComponents( xuidHugged, "ACH.SPREAD_THE_LOVE",
|
||||
TitleData2::kAchievement_SpreadTheLove_FriendsHuggedCount, pPlayerLocal );
|
||||
|
||||
if ( bAchieved )
|
||||
{
|
||||
KeyValues *kvAwardShirt2 = new KeyValues( "" );
|
||||
KeyValues::AutoDelete autodelete_kvAwardShirt2( kvAwardShirt2 );
|
||||
kvAwardShirt2->SetInt( "AV_SHIRT2", 1 );
|
||||
pPlayerLocal->UpdateAwardsData( kvAwardShirt2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnCoopBotTaunt", szCmd ) )
|
||||
{
|
||||
if ( !Q_stricmp( "teamhug", pCmd->GetString( "taunt" ) ) )
|
||||
{
|
||||
if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
|
||||
return;
|
||||
|
||||
// A hug has happened, let's spread the love
|
||||
int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
|
||||
g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( XBX_GetSlotByUserId( iCtrlr ) );
|
||||
KeyValues *pServerEvent = new KeyValues( "OnPlayerAward" );
|
||||
pServerEvent->SetString( "award", "ACH.SPREAD_THE_LOVE" );
|
||||
pServerEvent->SetUint64( "xuid", pPlayerLocal->GetXUID() );
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
pServerEvent->SetUint64( "psnid", steamapicontext->SteamUser()->GetConsoleSteamID().ConvertToUint64() );
|
||||
#endif
|
||||
pServerEvent->SetInt( "hugged", 1 );
|
||||
g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( pServerEvent );
|
||||
g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnSpeedRunCoopEvent", szCmd ) )
|
||||
{
|
||||
//
|
||||
// A qualifying speedrun has happened
|
||||
//
|
||||
// Determine the map context
|
||||
char const *szMapSpeedRun = pCmd->GetString( "map" );
|
||||
if ( !szMapSpeedRun || !*szMapSpeedRun )
|
||||
return;
|
||||
TitleDataFieldsDescription_t const *fieldMapComplete = TitleDataFieldsDescriptionFindByString( DescribeTitleDataStorage(), CFmtStr( "MP.complete.%s", szMapSpeedRun ) );
|
||||
if ( !fieldMapComplete )
|
||||
return;
|
||||
uint16 uiMapCompleteId = (uint16)(uint32) fieldMapComplete->m_numBytesOffset;
|
||||
|
||||
// Set achievement component
|
||||
MatchSessionSetAchievementBasedOnComponents<uint16>( uiMapCompleteId, "ACH.SPEED_RUN_COOP",
|
||||
TitleData2::kAchievement_SpeedRunCoop_QualifiedRunsCount, pPlayerLocal );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( szEvent, "OnDowloadableContentInstalled" ) )
|
||||
{
|
||||
if ( !IsX360() )
|
||||
{
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pIMatchSession )
|
||||
return;
|
||||
|
||||
IPlayerLocal *playerPrimary = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
||||
if ( !playerPrimary )
|
||||
return;
|
||||
|
||||
//
|
||||
// Find local machine in session settings
|
||||
//
|
||||
KeyValues *pSettings = pIMatchSession->GetSessionSettings();
|
||||
KeyValues *pMachine = NULL;
|
||||
KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, playerPrimary->GetXUID(), &pMachine );
|
||||
pPlayer;
|
||||
if ( !pMachine )
|
||||
return;
|
||||
|
||||
// Initialize DLC for the machine
|
||||
extern void InitializeDlcMachineSettings( KeyValues *pSettings, KeyValues *pMachine );
|
||||
InitializeDlcMachineSettings( pSettings, pMachine );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int CMatchTitle::GetEventDebugID( void )
|
||||
{
|
||||
return EVENT_DEBUG_ID_INIT;
|
||||
}
|
||||
|
||||
void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
|
||||
{
|
||||
#ifndef SWDS
|
||||
// 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;
|
||||
|
||||
// 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 );
|
||||
|
||||
Assert( szMapName && *szMapName );
|
||||
if ( szMapName && *szMapName )
|
||||
{
|
||||
kvUpdate->SetString( "update/game/map", szMapName );
|
||||
|
||||
MatchSessionUpdateSinglePlayerProgress( szMapName );
|
||||
|
||||
if ( !pSessionSettings->GetString( "game/type", NULL ) )
|
||||
{
|
||||
bool bFriends = false;
|
||||
if ( MatchSessionIsSinglePlayerOnline( NULL ) )
|
||||
{
|
||||
IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
||||
if ( pPlayerLocal )
|
||||
{
|
||||
XUID xuidLocal = pPlayerLocal->GetXUID();
|
||||
int numSessionFriends = 0, numSessionPlayers = 0;
|
||||
for ( int iMachine = 0, numMachines = pSessionSettings->GetInt( "members/numMachines" ); iMachine < numMachines; ++ iMachine )
|
||||
{
|
||||
KeyValues *pMachine = pSessionSettings->FindKey( CFmtStr( "members/machine%d", iMachine ) );
|
||||
for ( int iPlayer = 0, numPlayers = pMachine->GetInt( "numPlayers" ); iPlayer < numPlayers; ++ iPlayer )
|
||||
{
|
||||
KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", iPlayer ) );
|
||||
if ( !pPlayer )
|
||||
continue;
|
||||
|
||||
++ numSessionPlayers;
|
||||
XUID xuidPlayer = pPlayer->GetUint64( "xuid" );
|
||||
if ( xuidPlayer == xuidLocal )
|
||||
continue;
|
||||
|
||||
if ( MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), xuidPlayer ) )
|
||||
{
|
||||
++ numSessionFriends;
|
||||
}
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
else if ( MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pMachine->GetUint64( "psnid" ) ) )
|
||||
{
|
||||
++ numSessionFriends;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if ( numSessionFriends && numSessionPlayers && ( numSessionFriends + 1 == numSessionPlayers ) )
|
||||
bFriends = true;
|
||||
}
|
||||
}
|
||||
kvUpdate->SetString( "update/game/type", bFriends ? "friends" : "default" );
|
||||
}
|
||||
}
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnProfilesWriteOpportunity", "reason", "checkpoint"
|
||||
) );
|
||||
}
|
||||
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"
|
||||
) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
89
matchmaking/portal2/mm_title.h
Normal file
89
matchmaking/portal2/mm_title.h
Normal file
@@ -0,0 +1,89 @@
|
||||
//===== Copyright <20> 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();
|
||||
|
||||
// Describe title settings using a bitwise combination of flags
|
||||
virtual uint64 GetTitleSettingsFlags();
|
||||
|
||||
// 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 DLC description
|
||||
virtual TitleDlcDescription_t const * DescribeTitleDlcs();
|
||||
|
||||
// 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 );
|
||||
|
||||
// Run every frame
|
||||
virtual void RunFrame();
|
||||
|
||||
// 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
|
||||
1021
matchmaking/portal2/mm_title_gamesettingsmgr.cpp
Normal file
1021
matchmaking/portal2/mm_title_gamesettingsmgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
matchmaking/portal2/mm_title_main.cpp
Normal file
50
matchmaking/portal2/mm_title_main.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "../mm_title_main.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();
|
||||
}
|
||||
|
||||
#if defined( _X360 ) && defined( _DEBUG )
|
||||
CON_COMMAND( x_dbg_xgi, "Set X360 XGI debug output level" )
|
||||
{
|
||||
int iLevel = args.FindArgInt( "-level", -1 );
|
||||
if ( iLevel >= 0 )
|
||||
{
|
||||
XDebugSetSystemOutputLevel( HXAMAPP_XGI, iLevel );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
247
matchmaking/portal2/mm_title_richpresence.cpp
Normal file
247
matchmaking/portal2/mm_title_richpresence.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "portal2.spa.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[] = {
|
||||
#define CFG( gamemode ) { #gamemode, CONTEXT_GAME_MODE_##gamemode },
|
||||
#include "inc_gamemode.inc"
|
||||
#undef CFG
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
struct MpCoopMapRichPresence_t
|
||||
{
|
||||
char const *szMapName;
|
||||
DWORD dwCtxValue;
|
||||
int idxChapter;
|
||||
int numChapters;
|
||||
}
|
||||
g_pcv_CONTEXT_COOP_PRESENCE_TRACK[] = {
|
||||
#define CFG( mpcoopmap, ctxval, idx, num ) { #mpcoopmap, ctxval, idx, num },
|
||||
#include "inc_coop_maps.inc"
|
||||
#undef CFG
|
||||
{ NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
ContextValue_t g_pcv_CONTEXT_SP_PRESENCE_TEXT[] = {
|
||||
#define CFG( spmapname, chapternum, subchapter ) { #spmapname, CONTEXT_SP_PRESENCE_TEXT_CH##chapternum },
|
||||
#include "inc_sp_maps.inc"
|
||||
#undef CFG
|
||||
{ NULL, CONTEXT_SP_PRESENCE_TEXT_DEFAULT },
|
||||
};
|
||||
|
||||
static MpCoopMapRichPresence_t const * FindMpCoopMapRichPresence( char const *szMapName )
|
||||
{
|
||||
MpCoopMapRichPresence_t const *p = g_pcv_CONTEXT_COOP_PRESENCE_TRACK;
|
||||
for ( ; p->szMapName; ++ p )
|
||||
{
|
||||
if ( !Q_stricmp( p->szMapName, szMapName ) )
|
||||
return p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 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;
|
||||
|
||||
if ( char const *szValue = pNewSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_GAME_MODE, g_pcv_CONTEXT_GAME_MODE->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
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_REQUIRED_DLC1 - 1 + k, sizeof( val ), &val );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Determine Rich Presence Display
|
||||
//
|
||||
if ( char const *szGameModeForRichPresence = pFullSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
unsigned int dwLevelPresence = CONTEXT_PRESENCE_MAINMENU;
|
||||
|
||||
if ( !Q_stricmp( szGameModeForRichPresence, "sp" ) )
|
||||
{
|
||||
//
|
||||
// Game chapter
|
||||
//
|
||||
static char s_chLastMapNameSet[128] = {0};
|
||||
char const *szMap = pFullSettings->GetString( "Game/map" );
|
||||
if ( Q_stricmp( szMap, s_chLastMapNameSet ) )
|
||||
{
|
||||
Q_strncpy( s_chLastMapNameSet, szMap, sizeof( s_chLastMapNameSet ) );
|
||||
SetAllUsersContext( CONTEXT_SP_PRESENCE_TEXT, g_pcv_CONTEXT_SP_PRESENCE_TEXT->ScanValues( s_chLastMapNameSet ) );
|
||||
}
|
||||
|
||||
dwLevelPresence = CONTEXT_PRESENCE_SP;
|
||||
}
|
||||
else if ( !Q_stricmp( szGameModeForRichPresence, "coop" ) || !Q_stricmp( szGameModeForRichPresence, "coop_challenge" ) )
|
||||
{
|
||||
//
|
||||
// Game type: splitscreen / friends /quickmatch
|
||||
//
|
||||
DWORD dwGameType = CONTEXT_COOP_PRESENCE_TAGLINE_DEFAULT;
|
||||
if ( XBX_GetNumGameUsers() > 1 )
|
||||
dwGameType = CONTEXT_COOP_PRESENCE_TAGLINE_SPLITSCREEN;
|
||||
else if ( !Q_stricmp( "lan", pFullSettings->GetString( "system/network" ) ) )
|
||||
dwGameType = CONTEXT_COOP_PRESENCE_TAGLINE_SYSTEMLINK;
|
||||
else if ( !Q_stricmp( "friends", pFullSettings->GetString( "game/type" ) ) )
|
||||
dwGameType = CONTEXT_COOP_PRESENCE_TAGLINE_FRIEND;
|
||||
else if ( !Q_stricmp( "quickmatch", pFullSettings->GetString( "game/type" ) ) )
|
||||
dwGameType = CONTEXT_COOP_PRESENCE_TAGLINE_QUICKMATCH;
|
||||
|
||||
static DWORD s_dwLastGameTypeSet = CONTEXT_COOP_PRESENCE_TAGLINE_DEFAULT;
|
||||
if ( s_dwLastGameTypeSet != dwGameType )
|
||||
{
|
||||
s_dwLastGameTypeSet = dwGameType;
|
||||
SetAllUsersContext( CONTEXT_COOP_PRESENCE_WAITING, dwGameType );
|
||||
SetAllUsersContext( CONTEXT_COOP_PRESENCE_TAGLINE, dwGameType );
|
||||
}
|
||||
|
||||
//
|
||||
// Game track
|
||||
//
|
||||
static int nNumChapters = 0;
|
||||
static int nIdxChapter = 0;
|
||||
static char s_chLastMapNameSet[128] = {0};
|
||||
char const *szMap = pFullSettings->GetString( "Game/map" );
|
||||
if ( Q_stricmp( szMap, s_chLastMapNameSet ) )
|
||||
{
|
||||
Q_strncpy( s_chLastMapNameSet, szMap, sizeof( s_chLastMapNameSet ) );
|
||||
|
||||
// Determine the track
|
||||
MpCoopMapRichPresence_t const *pMP = FindMpCoopMapRichPresence( szMap );
|
||||
SetAllUsersContext( CONTEXT_COOP_PRESENCE_TRACK, pMP->dwCtxValue );
|
||||
nIdxChapter = pMP->idxChapter;
|
||||
SetAllUsersProperty( PROPERTY_COOP_TRACK_CHAPTER, sizeof( nIdxChapter ), &nIdxChapter );
|
||||
nNumChapters = pMP->numChapters;
|
||||
SetAllUsersProperty( PROPERTY_COOP_TRACK_NUMCHAPTERS, sizeof( nNumChapters ), &nNumChapters );
|
||||
}
|
||||
|
||||
// Presence
|
||||
if ( !Q_stricmp( "game", pFullSettings->GetString( "game/state" ) ) )
|
||||
{
|
||||
dwLevelPresence = ( nNumChapters > 0 ) ? CONTEXT_PRESENCE_COOPGAME_TRACK : CONTEXT_PRESENCE_COOPGAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwLevelPresence = CONTEXT_PRESENCE_COOPMENU;
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef _X360
|
||||
// Set the installed DLCs masks
|
||||
static int val[10]; // must be valid for the async call
|
||||
uint64 uiDlcInstalled = g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" );
|
||||
extern ConVar mm_matchmaking_dlcsquery;
|
||||
for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
|
||||
{
|
||||
val[k] = !!( uiDlcInstalled & ( 1ull << k ) );
|
||||
DevMsg( "DLC%d installed: %d\n", k, val[k] );
|
||||
SetAllUsersProperty( PROPERTY_INSTALLED_DLC1 - 1 + k, sizeof( val[k] ), &val[k] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
22
matchmaking/portal2/mm_title_richpresence.h
Normal file
22
matchmaking/portal2/mm_title_richpresence.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//===== Copyright <20> 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_GAME_MODE[];
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
290
matchmaking/portal2/mm_title_titledata.cpp
Normal file
290
matchmaking/portal2/mm_title_titledata.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "portal2.spa.h"
|
||||
#include "matchmaking/portal2/imatchext_portal2.h"
|
||||
|
||||
#include "steam/isteamuserstats.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static TitleDataFieldsDescription_t const * PrepareTitleDataStorageDescription()
|
||||
{
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ \
|
||||
TitleDataFieldsDescription_t aTDFD = { szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }; \
|
||||
s_tdfd.AddToTail( aTDFD ); \
|
||||
}
|
||||
|
||||
static CUtlVector< TitleDataFieldsDescription_t > s_tdfd;
|
||||
|
||||
// Single player progress
|
||||
TD_ENTRY( "SP.progress", DB_TD1, DT_uint32, offsetof( TitleData1, uiSinglePlayerProgressChapter ) );
|
||||
|
||||
// COOP MAPS
|
||||
#define CFG( fieldname, ctx, idx, num ) \
|
||||
TD_ENTRY( "MP.complete." #fieldname,DB_TD1, DT_BITFIELD, 8*offsetof( TitleData1, coop.mapbits ) + TitleData1::CoopData_t::fieldname )
|
||||
#define CFG_DISABLED( fieldname, ctx, idx, num )
|
||||
#include "xlast_portal2/inc_coop_maps.inc"
|
||||
#undef CFG_DISABLED
|
||||
#undef CFG
|
||||
|
||||
// COOP TAUNTS
|
||||
TD_ENTRY( "MP.taunt.own", DB_TD1, DT_0, 8*offsetof( TitleData1, coop.tauntbitsOwned ) + 0 )
|
||||
#define CFG( fieldname ) \
|
||||
TD_ENTRY( "MP.taunt.own." #fieldname,DB_TD1, DT_BITFIELD, 8*offsetof( TitleData1, coop.tauntbitsOwned ) + TitleData1::CoopData_t::taunt_##fieldname ) \
|
||||
TD_ENTRY( "MP.taunt.use." #fieldname,DB_TD1, DT_BITFIELD, 8*offsetof( TitleData1, coop.tauntbitsUsed ) + TitleData1::CoopData_t::taunt_##fieldname )
|
||||
#define CFG_DISABLED( fieldname )
|
||||
#include "xlast_portal2/inc_coop_taunts.inc"
|
||||
#undef CFG_DISABLED
|
||||
#undef CFG
|
||||
// #define CFG( fieldname ) \
|
||||
// TD_ENTRY( "MP.taunt.equipslot." #fieldname,DB_TD1, DT_uint8, offsetof( TitleData1, coop.tauntsEquipSlots[ TitleData1::CoopData_t::taunt_equipslot_##fieldname ] ) )
|
||||
// #include "xlast_portal2/inc_coop_taunts_equipslots.inc"
|
||||
// #undef CFG
|
||||
|
||||
// GAME INSTRUCTOR LESSONS
|
||||
#define CFG( fieldname ) \
|
||||
TD_ENTRY( "GI.lesson." #fieldname, DB_TD1, DT_uint8, offsetof( TitleData1, gameinstructor.lessoninfo[ TitleData1::GameInstructorData_t::lesson_##fieldname ] ) )
|
||||
#define CFG_DISABLED( fieldname )
|
||||
#include "xlast_portal2/inc_gameinstructor_lessons.inc"
|
||||
#undef CFG_DISABLED
|
||||
#undef CFG
|
||||
|
||||
// Storage for achievements components
|
||||
uint32 uiAchievementComponentBitsUsed = 0;
|
||||
#define CFG( name, compcount, ... ) \
|
||||
{ \
|
||||
COMPILE_TIME_ASSERT( (compcount == 0) || (compcount > 1) ); \
|
||||
int numAchNameChars = Q_strlen( "ACH." #name ); \
|
||||
for ( int iComponent = 0; iComponent < compcount; ++ iComponent ) \
|
||||
{ \
|
||||
char *pszComponentName = new char[ numAchNameChars + 10 ]; \
|
||||
Q_snprintf( pszComponentName, numAchNameChars + 10, "%s[%d]", "ACH." #name, iComponent+1 ); \
|
||||
TD_ENTRY( pszComponentName, DB_TD2, DT_BITFIELD, 8*offsetof( TitleData2, bitsAchievementsComponents ) + uiAchievementComponentBitsUsed + iComponent ) \
|
||||
} \
|
||||
uiAchievementComponentBitsUsed += 32 * STORAGE_COUNT_FOR_BITS( uint32, compcount ); \
|
||||
}
|
||||
#include "inc_achievements.inc"
|
||||
#undef CFG
|
||||
|
||||
#ifdef _X360
|
||||
// Awards bitfields
|
||||
#define CFG( award, ... ) \
|
||||
TD_ENTRY( "AV_TD_" #award, DB_TD2, DT_BITFIELD, 8*offsetof( TitleData2, awardbits ) + TitleData2::bitAward##award )
|
||||
#include "xlast_portal2/inc_asset_awards.inc"
|
||||
#undef CFG
|
||||
#endif
|
||||
|
||||
// Custom achievements data
|
||||
{
|
||||
char const *szAch = "ACH.SPREAD_THE_LOVE";
|
||||
int numAchNameChars = Q_strlen( szAch );
|
||||
for ( int iComponent = 0; iComponent < TitleData2::kAchievement_SpreadTheLove_FriendsHuggedCount; ++ iComponent )
|
||||
{
|
||||
char *pszComponentName = new char[ numAchNameChars + 10 ];
|
||||
Q_snprintf( pszComponentName, numAchNameChars + 10, "%s[%d]", szAch, iComponent+1 );
|
||||
TD_ENTRY( pszComponentName, DB_TD2, DT_uint64, offsetof( TitleData2, ach_SpreadTheLove_FriendsHugged[iComponent] ) );
|
||||
}
|
||||
}
|
||||
{
|
||||
char const *szAch = "ACH.SPEED_RUN_COOP";
|
||||
int numAchNameChars = Q_strlen( szAch );
|
||||
for ( int iComponent = 0; iComponent < TitleData2::kAchievement_SpeedRunCoop_QualifiedRunsCount; ++ iComponent )
|
||||
{
|
||||
char *pszComponentName = new char[ numAchNameChars + 10 ];
|
||||
Q_snprintf( pszComponentName, numAchNameChars + 10, "%s[%d]", szAch, iComponent+1 );
|
||||
TD_ENTRY( pszComponentName, DB_TD2, DT_uint16, offsetof( TitleData2, ach_SpeedRunCoop_MapsQualified[iComponent] ) );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _PS3
|
||||
// DLC ownership bits
|
||||
#define CFG( fieldname ) \
|
||||
TD_ENTRY( "DLC." #fieldname,DB_TD2, DT_BITFIELD, 8*offsetof( TitleData2, dlcbits ) + fieldname )
|
||||
CFG( 0x12 )
|
||||
CFG( 0x13 )
|
||||
CFG( 0x14 )
|
||||
#undef CFG
|
||||
#endif
|
||||
|
||||
#ifdef _GAMECONSOLE
|
||||
// system convars
|
||||
#define CFG( name, scfgType, cppType, ... ) \
|
||||
TD_ENTRY( "CFG.sys." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvSystem.name ) )
|
||||
#include "xlast_portal2/inc_gameconsole_settings_sys.inc"
|
||||
#undef CFG
|
||||
#define CFG( name ) \
|
||||
TD_ENTRY( "CFG.sys." #name, DB_TD3, DT_BITFIELD, 8*offsetof( TitleData3, cvSystem.bitfields ) + TitleData3::ConVarsSystem_t::name )
|
||||
#include "xlast_portal2/inc_gameconsole_settings_sys_bits.inc"
|
||||
#undef CFG
|
||||
// profile-specific convars
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.name ) ) \
|
||||
TD_ENTRY( "CFG.usrSS." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUserSS.name ) )
|
||||
#include "xlast_portal2/inc_gameconsole_settings_usr.inc"
|
||||
#undef CFG
|
||||
#define CFG( name ) \
|
||||
TD_ENTRY( "CFG.usr." #name, DB_TD3, DT_BITFIELD, 8*offsetof( TitleData3, cvUser.bitfields ) + TitleData3::ConVarsUser_t::name ) \
|
||||
TD_ENTRY( "CFG.usrSS." #name, DB_TD3, DT_BITFIELD, 8*offsetof( TitleData3, cvUserSS.bitfields ) + TitleData3::ConVarsUser_t::name )
|
||||
#include "xlast_portal2/inc_gameconsole_settings_usr_bits.inc"
|
||||
#undef CFG
|
||||
#endif
|
||||
|
||||
// game stats
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( "GAME." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, gamestats.name ) )
|
||||
#include "xlast_portal2/inc_gamestats.inc"
|
||||
#undef CFG
|
||||
|
||||
// END MARKER
|
||||
TD_ENTRY( NULL, DB_TD1, DT_0, 0 )
|
||||
|
||||
#undef TD_ENTRY
|
||||
|
||||
COMPILE_TIME_ASSERT( TitleData1::CoopData_t::mapbits_total >= TitleData1::CoopData_t::mapbits_last_bit_used );
|
||||
COMPILE_TIME_ASSERT( TitleData1::CoopData_t::tauntbits_total >= TitleData1::CoopData_t::tauntbits_last_bit_used );
|
||||
COMPILE_TIME_ASSERT( TitleData1::GameInstructorData_t::lessonbits_total >= TitleData1::GameInstructorData_t::lessonbits_last_bit_used );
|
||||
|
||||
return s_tdfd.Base();
|
||||
}
|
||||
|
||||
TitleDataFieldsDescription_t const * CMatchTitle::DescribeTitleDataStorage()
|
||||
{
|
||||
static TitleDataFieldsDescription_t const *s_pTDFD = PrepareTitleDataStorageDescription();
|
||||
return s_pTDFD;
|
||||
}
|
||||
|
||||
TitleAchievementsDescription_t const * CMatchTitle::DescribeTitleAchievements()
|
||||
{
|
||||
static TitleAchievementsDescription_t tad[] =
|
||||
{
|
||||
#define CFG( name, compcount, ... ) \
|
||||
{ "ACH." #name, ACHIEVEMENT_##name, compcount },
|
||||
#include "inc_achievements.inc"
|
||||
#undef CFG
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return tad;
|
||||
}
|
||||
|
||||
TitleAvatarAwardsDescription_t const * CMatchTitle::DescribeTitleAvatarAwards()
|
||||
{
|
||||
static TitleAvatarAwardsDescription_t taad[] =
|
||||
{
|
||||
#define CFG( award, ... ) { "AV_" #award, AVATARASSETAWARD_##award, "AV_TD_" #award },
|
||||
#include "inc_asset_awards.inc"
|
||||
#undef CFG
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return taad;
|
||||
}
|
||||
|
||||
TitleDlcDescription_t const * CMatchTitle::DescribeTitleDlcs()
|
||||
{
|
||||
static TitleDlcDescription_t tdlcs[] =
|
||||
{
|
||||
{ PORTAL2_DLCID_RETAIL_DLC1, 0x80000001, 0x80000001, "DLC.0x01" },
|
||||
{ PORTAL2_DLCID_COOP_BOT_SKINS, PORTAL2_DLC_APPID_COOP_BOT_SKINS, PORTAL2_DLC_PKGID_COOP_BOT_SKINS, "DLC.0x12" },
|
||||
{ PORTAL2_DLCID_COOP_BOT_HELMETS, PORTAL2_DLC_APPID_COOP_BOT_HELMETS, PORTAL2_DLC_PKGID_COOP_BOT_HELMETS, "DLC.0x13" },
|
||||
{ PORTAL2_DLCID_COOP_BOT_ANTENNA, PORTAL2_DLC_APPID_COOP_BOT_ANTENNA, PORTAL2_DLC_PKGID_COOP_BOT_ANTENNA, "DLC.0x14" },
|
||||
// END MARKER
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
return tdlcs;
|
||||
}
|
||||
|
||||
// Title leaderboards
|
||||
KeyValues * CMatchTitle::DescribeTitleLeaderboard( char const *szLeaderboardView )
|
||||
{
|
||||
// Check if this is a challenge leaderboard
|
||||
if ( char const *szChallenge = StringAfterPrefix( szLeaderboardView, "challenge_besttime_" ) )
|
||||
{
|
||||
// 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 ( !IsX360() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score besttime " // :score is the leaderboard value mapped to game name "besttime"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodAscending ); // 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;
|
||||
}
|
||||
}
|
||||
if ( char const *szChallenge = StringAfterPrefix( szLeaderboardView, "challenge_distance_" ) )
|
||||
{
|
||||
if ( !IsX360() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score distance " // :score is the leaderboard value mapped to game name "distance"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodAscending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
if ( char const *szChallenge = StringAfterPrefix( szLeaderboardView, "challenge_portals_" ) )
|
||||
{
|
||||
if ( !IsX360() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score portals " // :score is the leaderboard value mapped to game name "portals"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodAscending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // 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