This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View 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
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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
}

View 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

View 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;
}