Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

912 lines
30 KiB

//===== Copyright © 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
}