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