initial
This commit is contained in:
510
matchmaking/cstrike15/mm_title.cpp
Normal file
510
matchmaking/cstrike15/mm_title.cpp
Normal file
@@ -0,0 +1,510 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "csgo.spa.h"
|
||||
|
||||
#ifdef _PS3
|
||||
#include <netex/net.h>
|
||||
#include <netex/libnetctl.h>
|
||||
#endif
|
||||
|
||||
#include "fmtstr.h"
|
||||
#include "gametypes/igametypes.h"
|
||||
#include "netmessages_signon.h"
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern IGameTypes *g_pGameTypes;
|
||||
|
||||
|
||||
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 );
|
||||
mgr->AddListener( this, "player_connect", false );
|
||||
mgr->AddListener( this, "player_disconnect", false );
|
||||
}
|
||||
|
||||
#ifdef _GAMECONSOLE
|
||||
// Initialize Title Update version
|
||||
extern ConVar mm_tu_string;
|
||||
mm_tu_string.SetValue( "00000000" );
|
||||
#endif
|
||||
|
||||
g_pGameTypes->Initialize();
|
||||
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CMatchTitle::Shutdown()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->RemoveListener( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
uint64 CMatchTitle::GetTitleID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return TITLEID_COUNTER_STRIKE__GO;
|
||||
#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
|
||||
}
|
||||
|
||||
#ifdef _PS3
|
||||
|
||||
uint64 CMatchTitle::GetTitleSettingsFlags()
|
||||
{
|
||||
return MATCHTITLE_SETTING_MULTIPLAYER
|
||||
| MATCHTITLE_VOICE_INGAME
|
||||
| MATCHTITLE_FORCE_PSN_NAMES
|
||||
| MATCHTITLE_PLAYERMGR_DISABLED
|
||||
| MATCHTITLE_SERVERMGR_DISABLED;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
uint64 CMatchTitle::GetTitleSettingsFlags()
|
||||
{
|
||||
return MATCHTITLE_SETTING_MULTIPLAYER
|
||||
| MATCHTITLE_VOICE_INGAME
|
||||
| MATCHTITLE_PLAYERMGR_ALLFRIENDS
|
||||
#if !defined( CSTRIKE15 )
|
||||
| MATCHTITLE_SETTING_NODEDICATED
|
||||
| MATCHTITLE_PLAYERMGR_DISABLED
|
||||
| MATCHTITLE_SERVERMGR_DISABLED
|
||||
| MATCHTITLE_INVITE_ONLY_SINGLE_USER
|
||||
#else
|
||||
| MATCHTITLE_PLAYERMGR_FRIENDREQS
|
||||
#endif // !CSTRIKE15
|
||||
;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS3
|
||||
void *g_pMatchTitle_NetMemory;
|
||||
#endif
|
||||
|
||||
void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
|
||||
{
|
||||
#ifdef _X360
|
||||
XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
|
||||
|
||||
xnsp.cfgQosDataLimitDiv4 = 64; // 256 bytes
|
||||
|
||||
// Increase outstanding QoS responses significantly
|
||||
// See: https://forums.xboxlive.com/AnswerPage.aspx?qid=493b207b-66b9-42bc-b23d-ddc306e09749&tgt=1
|
||||
xnsp.cfgQosSrvMaxSimultaneousResponses = 255;
|
||||
|
||||
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()
|
||||
{
|
||||
#ifdef _GAMECONSOLE
|
||||
// [jason] Rounding this up to 16, since this was the value used on cstrike15 xbla. Among other things, this
|
||||
// controls how many remote talker voice channels are reserved for each client in XHV
|
||||
return 16;
|
||||
#else
|
||||
return 64; // On PC this is not limited, return max number dedicated servers can ever run with
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get a guest player name
|
||||
char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
|
||||
{
|
||||
if ( vgui::ILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
|
||||
{
|
||||
if ( wchar_t* wStringTableEntry = pLocalize->Find( "#SFUI_LocalPlayer" ) )
|
||||
{
|
||||
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" );
|
||||
|
||||
if ( !Q_stricmp( "commentary", pSettings->GetString( "options/play", "" ) ) )
|
||||
pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
|
||||
|
||||
// Run map based off the faked reservation packet
|
||||
g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForKeyValues( pGameDllReserve );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static KeyValues * GetCurrentMatchSessionSettings()
|
||||
{
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// <Vitaliy> July-2012
|
||||
// CS:GO hack: training map has been implemented storing progress in convars instead of
|
||||
// properly storing it in game profile data, force storing the convars into profile
|
||||
// when the training session finishes here
|
||||
// Please, do not use this approach in future code.
|
||||
//
|
||||
bool g_bPlayingTrainingMap = false;
|
||||
|
||||
void CMatchTitle::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
|
||||
!Q_stricmp( "OnPlayerUpdated", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
|
||||
}
|
||||
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 ) )
|
||||
{
|
||||
extern void UpdateAggregateMembersSettings( KeyValues *pFullGameSettings, KeyValues *pUpdate );
|
||||
UpdateAggregateMembersSettings( pMatchSession->GetSessionSettings(), kvUpdate );
|
||||
}
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( kvPackage ) );
|
||||
}
|
||||
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 );
|
||||
if ( IMatchSession *pSession = g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
if ( !Q_stricmp( "training", pSession->GetSessionSettings()->GetString( "game/type" ) ) )
|
||||
g_bPlayingTrainingMap = true;
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
|
||||
{
|
||||
if ( g_bPlayingTrainingMap )
|
||||
{
|
||||
g_bPlayingTrainingMap = false;
|
||||
g_pMatchExtensions->GetIVEngineClient()->ClientCmd_Unrestricted( CFmtStr( "host_writeconfig_ss %d", XBX_GetPrimaryUserId() ) );
|
||||
}
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( szEvent, "Client::CmdKeyValues" ) )
|
||||
{
|
||||
KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
|
||||
if ( !pCmd )
|
||||
return;
|
||||
char const *szCmd = pCmd->GetName();
|
||||
if ( !Q_stricmp( "ExtendedServerInfo", szCmd ) )
|
||||
{
|
||||
KeyValuesDumpAsDevMsg( pCmd, 2, 1 );
|
||||
g_pGameTypes->SetAndParseExtendedServerInfo( pCmd );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
|
||||
{
|
||||
int iOldState = pEvent->GetInt( "old", 0 );
|
||||
int iNewState = pEvent->GetInt( "new", 0 );
|
||||
|
||||
if (
|
||||
( iOldState >= SIGNONSTATE_CONNECTED && // disconnect
|
||||
iNewState < SIGNONSTATE_CONNECTED ) ||
|
||||
( iOldState < SIGNONSTATE_FULL && // full connect
|
||||
iNewState >= SIGNONSTATE_FULL )
|
||||
)
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineDisconnectReason", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int CMatchTitle::GetEventDebugID( void )
|
||||
{
|
||||
return EVENT_DEBUG_ID_INIT;
|
||||
}
|
||||
|
||||
void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
|
||||
{
|
||||
// Parse the game event
|
||||
char const *szGameEvent = pIGameEvent->GetName();
|
||||
if ( !szGameEvent || !*szGameEvent )
|
||||
return;
|
||||
|
||||
if ( !Q_stricmp( "round_start", szGameEvent ) ||
|
||||
!Q_stricmp( "round_end", szGameEvent ) ||
|
||||
!Q_stricmp( "game_newmap", szGameEvent ) ||
|
||||
!Q_stricmp( "player_connect", szGameEvent ) ||
|
||||
!Q_stricmp( "player_disconnect", szGameEvent ) )
|
||||
{ // Update rich presence
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
if ( !Q_stricmp( "round_start", szGameEvent ) )
|
||||
{
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
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 );
|
||||
|
||||
char const *szWorkshopMap = g_pMatchExtensions->GetIVEngineClient()->GetLevelNameShort();
|
||||
if ( StringHasPrefix( szWorkshopMap, "workshop" ) )
|
||||
{
|
||||
size_t nLenMapName = Q_strlen( szMapName );
|
||||
size_t nShortMapName = Q_strlen( szWorkshopMap );
|
||||
if ( ( nShortMapName >= nLenMapName ) &&
|
||||
!Q_stricmp( szWorkshopMap + nShortMapName - nLenMapName, szMapName ) )
|
||||
// Use the name of the workshop map
|
||||
kvUpdate->SetString( "update/game/map", szWorkshopMap );
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
89
matchmaking/cstrike15/mm_title.h
Normal file
89
matchmaking/cstrike15/mm_title.h
Normal file
@@ -0,0 +1,89 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_H
|
||||
#define MM_TITLE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "../mm_framework.h"
|
||||
|
||||
class CMatchTitle :
|
||||
public IMatchTitle,
|
||||
public IMatchEventsSink,
|
||||
public IGameEventListener2
|
||||
{
|
||||
// Methods exposed for the framework
|
||||
public:
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Methods of IMatchTitle
|
||||
public:
|
||||
// Title ID
|
||||
virtual uint64 GetTitleID();
|
||||
|
||||
// Service ID for XLSP
|
||||
virtual uint64 GetTitleServiceID();
|
||||
|
||||
// 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();
|
||||
|
||||
// Run every frame
|
||||
virtual void RunFrame() {}
|
||||
|
||||
// Title leaderboards
|
||||
virtual KeyValues * DescribeTitleLeaderboard( char const *szLeaderboardView );
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
virtual void PrepareClientForConnect( KeyValues *pSettings );
|
||||
|
||||
// Start up a listen server with the given settings
|
||||
virtual bool StartServerMap( KeyValues *pSettings );
|
||||
|
||||
// Methods of IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
// Methods of IGameEventListener2
|
||||
public:
|
||||
// FireEvent is called by EventManager if event just occured
|
||||
// KeyValue memory will be freed by manager if not needed anymore
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
virtual int GetEventDebugID( void );
|
||||
|
||||
public:
|
||||
CMatchTitle();
|
||||
~CMatchTitle();
|
||||
};
|
||||
|
||||
// Match title singleton
|
||||
extern CMatchTitle *g_pMatchTitle;
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
96
matchmaking/cstrike15/mm_title_contextvalues.h
Normal file
96
matchmaking/cstrike15/mm_title_contextvalues.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
#ifndef _MM_TITLE_CONTEXTVALUES_H
|
||||
#define _MM_TITLE_CONTEXTVALUES_H
|
||||
#pragma once
|
||||
|
||||
#include "csgo.spa.h"
|
||||
|
||||
static ContextValue_t g_MapGroupContexts[] =
|
||||
{
|
||||
{ "mg_bomb", CONTEXT_CSS_MAP_GROUP_BOMB_MG },
|
||||
{ "mg_hostage", CONTEXT_CSS_MAP_GROUP_HOSTAGE_MG },
|
||||
{ "mg_demolition", CONTEXT_CSS_MAP_GROUP_DEMOLITION_MG },
|
||||
{ "mg_armsrace", CONTEXT_CSS_MAP_GROUP_ARMSRACE_MG },
|
||||
{ "mg_de_aztec", CONTEXT_CSS_MAP_GROUP_AZTEC_MG },
|
||||
{ "mg_ar_baggage", CONTEXT_CSS_MAP_GROUP_BAGGAGE_MG },
|
||||
{ "mg_de_bank", CONTEXT_CSS_MAP_GROUP_BANK_MG },
|
||||
{ "mg_de_dust", CONTEXT_CSS_MAP_GROUP_DUST_MG },
|
||||
{ "mg_de_dust2", CONTEXT_CSS_MAP_GROUP_DUST2_MG },
|
||||
{ "mg_de_inferno", CONTEXT_CSS_MAP_GROUP_INFERNO_MG },
|
||||
{ "mg_cs_italy", CONTEXT_CSS_MAP_GROUP_ITALY_MG },
|
||||
{ "mg_de_lake", CONTEXT_CSS_MAP_GROUP_LAKE_MG },
|
||||
{ "mg_de_nuke", CONTEXT_CSS_MAP_GROUP_NUKE_MG },
|
||||
{ "mg_cs_office", CONTEXT_CSS_MAP_GROUP_OFFICE_MG },
|
||||
{ "mg_de_safehouse", CONTEXT_CSS_MAP_GROUP_SAFEHOUSE_MG },
|
||||
{ "mg_ar_shoots", CONTEXT_CSS_MAP_GROUP_SHOOTS_MG },
|
||||
{ "mg_de_shorttrain", CONTEXT_CSS_MAP_GROUP_SHORTTRAIN_MG },
|
||||
{ "mg_de_stmarc", CONTEXT_CSS_MAP_GROUP_STMARC_MG },
|
||||
{ "mg_de_sugarcane", CONTEXT_CSS_MAP_GROUP_SUGARCANE_MG },
|
||||
{ "mg_de_train", CONTEXT_CSS_MAP_GROUP_TRAIN_MG },
|
||||
{ "mg_training", CONTEXT_CSS_MAP_GROUP_TRAINING_MG },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_LevelContexts[] =
|
||||
{
|
||||
{ "cs_italy", CONTEXT_CSS_LEVEL_ITALY },
|
||||
{ "cs_office", CONTEXT_CSS_LEVEL_OFFICE },
|
||||
{ "de_aztec", CONTEXT_CSS_LEVEL_AZTEC },
|
||||
{ "de_dust", CONTEXT_CSS_LEVEL_DUST },
|
||||
{ "de_dust2", CONTEXT_CSS_LEVEL_DUST2 },
|
||||
{ "de_inferno", CONTEXT_CSS_LEVEL_INFERNO },
|
||||
{ "de_nuke", CONTEXT_CSS_LEVEL_NUKE },
|
||||
{ "ar_baggage", CONTEXT_CSS_LEVEL_BAGGAGE },
|
||||
{ "ar_shoots", CONTEXT_CSS_LEVEL_SHOOTS },
|
||||
{ "de_lake", CONTEXT_CSS_LEVEL_LAKE },
|
||||
{ "de_bank", CONTEXT_CSS_LEVEL_BANK },
|
||||
{ "de_safehouse", CONTEXT_CSS_LEVEL_SAFEHOUSE },
|
||||
{ "de_sugarcane", CONTEXT_CSS_LEVEL_SUGARCANE },
|
||||
{ "de_stmarc", CONTEXT_CSS_LEVEL_STMARC },
|
||||
{ "de_shorttrain", CONTEXT_CSS_LEVEL_SHORTTRAIN },
|
||||
{ "de_train", CONTEXT_CSS_LEVEL_TRAIN },
|
||||
{ "training1", CONTEXT_CSS_LEVEL_TRAINING },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_GameModeContexts[] =
|
||||
{
|
||||
{ "casual", CONTEXT_CSS_GAME_MODE_CASUAL },
|
||||
{ "competitive", CONTEXT_CSS_GAME_MODE_COMPETITIVE },
|
||||
{ "gungameprogressive", CONTEXT_CSS_GAME_MODE_GUNGAMEPROGRESSIVE },
|
||||
{ "gungametrbomb", CONTEXT_CSS_GAME_MODE_GUNGAMEBOMB },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_GameModeAsNumberContexts[] =
|
||||
{
|
||||
{ "casual", 0 },
|
||||
{ "competitive", 1 },
|
||||
{ "competitive_unranked", 2 },
|
||||
{ "pro", 4 },
|
||||
{ "gungameprogressive", 10 },
|
||||
{ "gungameselect", 20 },
|
||||
{ "gungametrbomb", 30 },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_GameTypeContexts[] =
|
||||
{
|
||||
{ "classic", CONTEXT_CSS_GAME_TYPE_CLASSIC },
|
||||
{ "gungame", CONTEXT_CSS_GAME_TYPE_GUNGAME },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_PrivacyContexts[] =
|
||||
{
|
||||
{ "public", CONTEXT_CSS_PRIVACY_PUBLIC },
|
||||
{ "private", CONTEXT_CSS_PRIVACY_INVITE_ONLY },
|
||||
{ "friends", CONTEXT_CSS_PRIVACY_FRIENDS },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
#endif // _MM_TITLE_CONTEXTVALUES_H
|
||||
1645
matchmaking/cstrike15/mm_title_gamesettingsmgr.cpp
Normal file
1645
matchmaking/cstrike15/mm_title_gamesettingsmgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
matchmaking/cstrike15/mm_title_main.cpp
Normal file
50
matchmaking/cstrike15/mm_title_main.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//===== Copyright © 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
|
||||
|
||||
330
matchmaking/cstrike15/mm_title_richpresence.cpp
Normal file
330
matchmaking/cstrike15/mm_title_richpresence.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "mm_title_contextvalues.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
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 ( dwContextId == X_CONTEXT_PRESENCE ) DevMsg( "Set presence to %d for user %d\n", dwValue, iCtrlr );
|
||||
|
||||
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 )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_GAME_MODE, CONTEXT_GAME_MODE_CSS_GAME_MODE_MULTIPLAYER, false );
|
||||
|
||||
// matchmaking version
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
extern ConVar mm_title_debug_version;
|
||||
val = mm_title_debug_version.GetInt();
|
||||
SetAllUsersProperty( PROPERTY_CSS_MATCH_VERSION, sizeof( val ), &val );
|
||||
DevMsg( "PrepareForSessionCreate: matchmaking version %d\n", val );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings )
|
||||
{
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
||||
( void ) g_pMatchExtensions->GetIBaseClientDLL()->GetRichPresenceStatusString();
|
||||
#endif
|
||||
#ifdef _X360
|
||||
if ( !pFullSettings )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_MAINMENU ); // main menu
|
||||
return;
|
||||
}
|
||||
|
||||
// Also set players information during initial rich presence update
|
||||
if ( !pUpdatedSettings && pFullSettings )
|
||||
{
|
||||
MM_Title_RichPresence_PlayersChanged( pFullSettings );
|
||||
|
||||
// Open slots
|
||||
int numSlots = pFullSettings->GetInt( "members/numSlots", XBX_GetNumGameUsers() );
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = numSlots;
|
||||
SetAllUsersProperty( PROPERTY_CSS_OPEN_SLOTS, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// Team slots
|
||||
int numTeamSlots = MAX( pFullSettings->GetInt( "members/numTSlotsFree", 0 ), pFullSettings->GetInt( "members/numCTSlotsFree", 0 ) );
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = numTeamSlots;
|
||||
SetAllUsersProperty( PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// Skill fields
|
||||
{
|
||||
static int val = 0; // must be valid for the async call
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_EXPERIENCE, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL0, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL1, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL2, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL3, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL4, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// Listen/dedicated server resolver
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = 0;
|
||||
extern ConVar mm_title_debug_dccheck;
|
||||
if ( mm_title_debug_dccheck.GetInt() )
|
||||
val = ( ( mm_title_debug_dccheck.GetInt() > 0 ) ? 0 : 1 );
|
||||
SetAllUsersProperty( PROPERTY_CSS_SEARCH_LISTEN_SERVER, sizeof( val ), &val );
|
||||
}
|
||||
}
|
||||
|
||||
// pUpdatedSettings = NULL when the session is created and all contexts need to be set
|
||||
KeyValues *pNewSettings = pUpdatedSettings ? pUpdatedSettings : pFullSettings;
|
||||
|
||||
// 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 );
|
||||
// }
|
||||
// }
|
||||
|
||||
// Actual game type (classic, gungame)
|
||||
if ( char const *szGameType = pNewSettings->GetString( "game/type", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_GAME_TYPE, g_GameTypeContexts->ScanValues( szGameType ) );
|
||||
}
|
||||
|
||||
// Game state
|
||||
if ( char const *szGameState = pNewSettings->GetString( "game/state", NULL ) )
|
||||
{
|
||||
if ( !V_stricmp( pFullSettings->GetString( "system/network" ), "offline" ) )
|
||||
SetAllUsersContext( CONTEXT_GAME_STATE, CONTEXT_GAME_STATE_SINGLE_PLAYER );
|
||||
else
|
||||
SetAllUsersContext( CONTEXT_GAME_STATE, ( !V_stricmp( "game", szGameState ) ) ? CONTEXT_GAME_STATE_MULTIPLAYER : CONTEXT_GAME_STATE_IN_MENUS );
|
||||
}
|
||||
|
||||
// Actual game mode (casual, competitive, pro, etc)
|
||||
if ( char const *szValue = pNewSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_GAME_MODE, g_GameModeContexts->ScanValues( szValue ) );
|
||||
|
||||
static int val; // must be valid for the async call
|
||||
val = g_GameModeAsNumberContexts->ScanValues( szValue );
|
||||
SetAllUsersProperty( PROPERTY_CSS_GAME_MODE_AS_NUMBER, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// MapGroup being used
|
||||
if ( char const *szMapGroupName = pNewSettings->GetString( "game/mapgroupname", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_MAP_GROUP, g_MapGroupContexts->ScanValues( szMapGroupName ) );
|
||||
}
|
||||
|
||||
// Privacy
|
||||
if ( char const *szPrivacy = pNewSettings->GetString( "system/access", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_PRIVACY, g_PrivacyContexts->ScanValues( szPrivacy ) );
|
||||
}
|
||||
|
||||
// Listen server
|
||||
if ( char const *szListenServer = pNewSettings->GetString( "server/server", NULL ) )
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = ( !V_stricmp( "listen", szListenServer ) ? 1 : 0 );
|
||||
extern ConVar mm_title_debug_dccheck;
|
||||
if ( mm_title_debug_dccheck.GetInt() )
|
||||
val = ( ( mm_title_debug_dccheck.GetInt() > 0 ) ? 0 : 1 );
|
||||
SetAllUsersProperty( PROPERTY_CSS_SEARCH_LISTEN_SERVER, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
//
|
||||
// Determine Rich Presence Display
|
||||
//
|
||||
if ( char const *szGameModeForRichPresence = pFullSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
// Online/Offline
|
||||
if ( char const *szNetwork = pFullSettings->GetString( "system/network", NULL ) )
|
||||
{
|
||||
DWORD dwLevelPresence = CONTEXT_PRESENCE_MAINMENU;
|
||||
|
||||
if ( V_stricmp( "offline", szNetwork ) == 0 )
|
||||
{
|
||||
dwLevelPresence = CONTEXT_PRESENCE_SINGLEPLAYER;
|
||||
}
|
||||
else if ( !V_stricmp( "lobby", pFullSettings->GetString( "game/state" ) ) )
|
||||
{
|
||||
dwLevelPresence = CONTEXT_PRESENCE_LOBBY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Privacy
|
||||
if ( char const *szPrivacy = pFullSettings->GetString( "system/access", NULL ) )
|
||||
{
|
||||
DWORD dwPrivacy = g_PrivacyContexts->ScanValues( szPrivacy );
|
||||
if ( dwPrivacy == CONTEXT_CSS_PRIVACY_PUBLIC )
|
||||
{
|
||||
// Public match
|
||||
|
||||
// See if there are any free slots
|
||||
int numSlots = pFullSettings->GetInt( "members/numSlots", 0 );
|
||||
int numPlayers = pFullSettings->GetInt( "members/numPlayers", 0 );
|
||||
|
||||
dwLevelPresence = (numSlots > numPlayers) ? CONTEXT_PRESENCE_MULTIPLAYER : CONTEXT_PRESENCE_MULTIPLAYER_NO_SLOTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Private/invite only match
|
||||
dwLevelPresence = CONTEXT_PRESENCE_MULTIPLAYER_PRIVATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetAllUsersContext( X_CONTEXT_PRESENCE, dwLevelPresence );
|
||||
}
|
||||
|
||||
// Update the map being used
|
||||
DWORD dwMapRichPresence = pFullSettings->GetInt( "game/mapRichPresence", 0xFFFF );
|
||||
if ( dwMapRichPresence == 0xFFFF )
|
||||
{
|
||||
// We didn't have a richpresence context set in GameModes.txt so look it up based on the name
|
||||
if ( char const *szMapName = pFullSettings->GetString( "game/map", NULL ) )
|
||||
{
|
||||
dwMapRichPresence = g_LevelContexts->ScanValues( szMapName );
|
||||
}
|
||||
}
|
||||
if ( dwMapRichPresence != 0xFFFF )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_LEVEL, dwMapRichPresence );
|
||||
}
|
||||
}
|
||||
#endif // _X360
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings )
|
||||
{
|
||||
//#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
|
||||
}
|
||||
|
||||
// Called by the client to notify matchmaking that it should update matchmaking properties based
|
||||
// on player distribution among the teams.
|
||||
void MM_Title_RichPresence_UpdateTeamPropertiesCSGO( KeyValues *pCurrentSettings, KeyValues *pTeamProperties )
|
||||
{
|
||||
#ifdef _X360
|
||||
int numSlots = pCurrentSettings->GetInt( "members/numSlots", 0 );
|
||||
int numPlayers = pTeamProperties->GetInt( "members/numPlayers", 0 );
|
||||
int numSpectators = pTeamProperties->GetInt( "members/numSpectators", 0 );
|
||||
int numExtraSpectatorSlots = pCurrentSettings->GetInt( "members/numExtraSpectatorSlots", 0 );
|
||||
int numFreeTSlots = pCurrentSettings->GetInt( "members/numTSlotsFree", 0 );
|
||||
int numFreeCTSlots = pCurrentSettings->GetInt( "members/numCTSlotsFree", 0 );
|
||||
|
||||
// Spectator overflow is computed in case we end up in a situation in which there are more
|
||||
// spectators than spectator slots. In that case, the extra spectators are counted against
|
||||
// the active player slots.
|
||||
int spectatorOverflow = numSpectators - numExtraSpectatorSlots;
|
||||
|
||||
|
||||
static int nPROPERTY_CSS_OPEN_SLOTS;
|
||||
nPROPERTY_CSS_OPEN_SLOTS = numSlots - numPlayers;
|
||||
if ( spectatorOverflow > 0 )
|
||||
{
|
||||
nPROPERTY_CSS_OPEN_SLOTS = numSlots - ( numPlayers - numExtraSpectatorSlots + spectatorOverflow );
|
||||
}
|
||||
SetAllUsersProperty( PROPERTY_CSS_OPEN_SLOTS, sizeof( nPROPERTY_CSS_OPEN_SLOTS ), &nPROPERTY_CSS_OPEN_SLOTS );
|
||||
|
||||
|
||||
|
||||
// post the average skill rank so matchmaking will work
|
||||
int avgRank = pTeamProperties->GetInt( "members/timeout", DEFAULT_NEW_PLAYER_ELO_RANK );
|
||||
static int nPROPERTY_CSS_AGGREGATE_SKILL0;
|
||||
nPROPERTY_CSS_AGGREGATE_SKILL0 = avgRank;
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL0, sizeof( nPROPERTY_CSS_AGGREGATE_SKILL0 ), &nPROPERTY_CSS_AGGREGATE_SKILL0 );
|
||||
|
||||
|
||||
// Post the maximum number of free slots on either team, for team matchmaking only
|
||||
static int nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS;
|
||||
nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS = max( numFreeTSlots, numFreeCTSlots );
|
||||
SetAllUsersProperty( PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS, sizeof( nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS ), &nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS );
|
||||
|
||||
#elif defined( _PS3 )
|
||||
// This is hacky: we stuff the rank into system data so that if lobby migrates
|
||||
// to a new owner and the metadata wasn't correctly set by the previous owner
|
||||
// then the new owner will set it
|
||||
if ( IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
KeyValues *kvSystemData = pMatchSession->GetSessionSystemData();
|
||||
if ( !kvSystemData )
|
||||
return;
|
||||
|
||||
int avgRank = pTeamProperties->GetInt( "members/timeout", DEFAULT_NEW_PLAYER_ELO_RANK );
|
||||
kvSystemData->SetInt( "timeout", avgRank );
|
||||
|
||||
int numOpenSlots = 10 - pTeamProperties->GetInt( "members/numPlayers" );
|
||||
if ( numOpenSlots < 0 )
|
||||
numOpenSlots = 0;
|
||||
|
||||
kvSystemData->SetInt( "numOpenSlots", numOpenSlots );
|
||||
|
||||
char const *szSessionType = kvSystemData->GetString( "type", NULL );
|
||||
|
||||
DevMsg( "Session timeout value=%d (%s)\n", avgRank, szSessionType ? szSessionType : "offline" );
|
||||
DevMsg( "Session numOpenSlots=%d (%s)\n", numOpenSlots, szSessionType ? szSessionType : "offline" );
|
||||
if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
|
||||
return; // don't run on clients for now, maybe later when we become owner
|
||||
|
||||
steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:timeout", CFmtStr( "%u", avgRank ) );
|
||||
steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:numOpenSlots", CFmtStr( "%u", numOpenSlots ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
21
matchmaking/cstrike15/mm_title_richpresence.h
Normal file
21
matchmaking/cstrike15/mm_title_richpresence.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_RICHPRESENCE_H
|
||||
#define MM_TITLE_RICHPRESENCE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_title.h"
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings );
|
||||
void MM_Title_RichPresence_UpdateTeamPropertiesCSGO( KeyValues *pCurrentSettings, KeyValues *pTeamProperties );
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings );
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings );
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
557
matchmaking/cstrike15/mm_title_titledata.cpp
Normal file
557
matchmaking/cstrike15/mm_title_titledata.cpp
Normal file
@@ -0,0 +1,557 @@
|
||||
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "matchmaking/cstrike15/imatchext_cstrike15.h"
|
||||
#include "inputsystem/iinputsystem.h"
|
||||
#include "platforminputdevice.h"
|
||||
#include "netmessages_signon.h"
|
||||
|
||||
#ifndef NO_STEAM
|
||||
#include "steam/isteamuserstats.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static ConVar cl_titledataversionblock1( "cl_titledataversionblock1", "14", FCVAR_DEVELOPMENTONLY, "stats for console title data block1 i/o version." );
|
||||
static ConVar cl_titledataversionblock2( "cl_titledataversionblock2", "8", FCVAR_DEVELOPMENTONLY, "stats for console title data block2 i/o version." );
|
||||
static ConVar cl_titledataversionblock3( "cl_titledataversionblock3", "48", FCVAR_DEVELOPMENTONLY, "stats for console title data block3 i/o version." );
|
||||
|
||||
static TitleDataFieldsDescription_t const * PrepareTitleDataStorageDescription()
|
||||
{
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ \
|
||||
TitleDataFieldsDescription_t aTDFD = { szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }; \
|
||||
s_tdfd.AddToTail( aTDFD ); \
|
||||
if ( numBytesOffset >= XPROFILE_SETTING_MAX_SIZE ) \
|
||||
Warning( "\nnumBytesOffset %d is > XPROFILE_SETTING_MAX_SIZE in TD_ENTRY for %s\n\n", numBytesOffset, szName ); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ \
|
||||
TitleDataFieldsDescription_t aTDFD = { szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }; \
|
||||
s_tdfd.AddToTail( aTDFD ); \
|
||||
}
|
||||
|
||||
#endif // _X360
|
||||
|
||||
static CUtlVector< TitleDataFieldsDescription_t > s_tdfd;
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
// versioning info for the title data blocks
|
||||
char *pTitleDataBlock[3];
|
||||
pTitleDataBlock[0] = new char[30];
|
||||
pTitleDataBlock[1] = new char[30];
|
||||
pTitleDataBlock[2] = new char[30];
|
||||
Q_snprintf( pTitleDataBlock[0], 30, "TITLEDATA.BLOCK1.VERSION" );
|
||||
Q_snprintf( pTitleDataBlock[1], 30, "TITLEDATA.BLOCK2.VERSION" );
|
||||
Q_snprintf( pTitleDataBlock[2], 30, "TITLEDATA.BLOCK3.VERSION" );
|
||||
TD_ENTRY( pTitleDataBlock[0], DB_TD1, DT_uint16, offsetof( TitleData1, versionNumber ) );
|
||||
TD_ENTRY( pTitleDataBlock[1], DB_TD2, DT_uint16, offsetof( TitleData2, versionNumber ) );
|
||||
TD_ENTRY( pTitleDataBlock[2], DB_TD3, DT_uint16, offsetof( TitleData3, versionNumber ) );
|
||||
|
||||
// stats
|
||||
#define CFG( cppType, name ) \
|
||||
TD_ENTRY( "STATS.usr." #name, DB_TD1, DT_##cppType, offsetof( TitleData1, usrStats.name ) )
|
||||
#include "xlast_csgo/inc_stats_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
// loadouts
|
||||
//we are using an array so we can pack this info as tight as possible to fit into the 1K block
|
||||
#define CFG( loadoutnum, equipmentnum ) \
|
||||
int numLoadouts = loadoutnum; \
|
||||
int numEquipmentSlots = equipmentnum;
|
||||
#include "xlast_csgo/inc_loadouts_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
int loadoutDataIndex = 0;
|
||||
for ( int team=0; team<2; ++team )
|
||||
{
|
||||
char teamName[10];
|
||||
if ( team == 0 )
|
||||
Q_snprintf( teamName, 10, "CT" );
|
||||
else
|
||||
Q_snprintf( teamName, 10, "T" );
|
||||
for (int i=0; i<numLoadouts; ++i)
|
||||
{
|
||||
for (int j=0; j<numEquipmentSlots; ++j)
|
||||
{
|
||||
char *loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.EQUIP%.1d.ID", teamName, i, j );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.EQUIP%.1d.QUANTITY", teamName, i, j );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
}
|
||||
char *loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.PRIMARY", teamName, i );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.SECONDARY", teamName, i );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.FLAGS", teamName, i );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
}
|
||||
}
|
||||
|
||||
// medals
|
||||
#define CFG( buffer ) \
|
||||
for ( int i=0; i< buffer; ++i ) \
|
||||
{ \
|
||||
char *pAwardedName = new char[ 20 ]; \
|
||||
Q_snprintf( pAwardedName, 20, "MEDALS.AWARDED%.3d", i ); \
|
||||
TD_ENTRY( pAwardedName, DB_TD2, DT_uint8, offsetof( TitleData2, CSMedalsAwarded[i] ) ) \
|
||||
char *pMedalInfoName = new char[ 25 ]; \
|
||||
Q_snprintf( pMedalInfoName, 25, "MEDALS.MEDALINFO%.3d", i ); \
|
||||
TD_ENTRY( pMedalInfoName, DB_TD2, DT_uint32, offsetof( TitleData2, CSMedalsMedalInfo[i] ) ) \
|
||||
}
|
||||
#include "xlast_csgo/inc_medals_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
#endif // _X360
|
||||
|
||||
#if defined( _GAMECONSOLE )
|
||||
|
||||
// system convars
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "CFG.sys." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvSystem.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_settings_sys.inc"
|
||||
#undef CFG
|
||||
|
||||
// profile-specific convars
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.name ) ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "CFG.usrSS." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUserSS.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_settings_usr.inc"
|
||||
#include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
|
||||
// joystick bindings
|
||||
#define ACTION( name )
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.name ) )
|
||||
#include "xlast_csgo/inc_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
#if defined( _PS3 )
|
||||
|
||||
// PS3 also has keyboard bindings.
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.name ) )
|
||||
#include "xlast_csgo/inc_ps3_key_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
// For PS3 we have two additional sets of button bindings one for Sharp Shooter, the other for Move.
|
||||
// We also have a few device specific convar settings.
|
||||
|
||||
// PS Move specific bindings and settings
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_MOVE_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.PSMove.name ) )
|
||||
#include "xlast_csgo/inc_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_MOVE_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.PSMove.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
// Sharp Shooter specific bindings and settings
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_SHARP_SHOOTER_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.SharpShooter.name ) )
|
||||
#include "xlast_csgo/inc_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_SHARP_SHOOTER_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.SharpShooter.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#undef ACTION
|
||||
|
||||
// Player Rankings by mode, controller, w/ optional history
|
||||
int rankIndex = 0;
|
||||
int numControllers = PlatformInputDevice::GetInputDeviceCountforPlatform();
|
||||
|
||||
for ( int m = 0; m < ELOTitleData::NUM_GAME_MODES_ELO_RANKED; m++ )
|
||||
{
|
||||
for ( int c = 1; c <= numControllers; c++ )
|
||||
{
|
||||
char *pRankingName = new char[ 30 ];
|
||||
V_snprintf( pRankingName, 30, TITLE_DATA_PREFIX "ELO.MODE%d.CTR%d", m, c );
|
||||
|
||||
TD_ENTRY( pRankingName, DB_TD3, DT_ELO, offsetof( TitleData3, playerRankingsData[ rankIndex ] ) );
|
||||
|
||||
rankIndex++;
|
||||
}
|
||||
|
||||
// Record the bracket and some info for calculating it. Only legal controllers are game console controllers.
|
||||
char *pBracketInfoName = new char[ 30 ];
|
||||
V_snprintf( pBracketInfoName, 30, TITLE_DATA_PREFIX"ELO.MODE%d.BRACKETINFO", m );
|
||||
TD_ENTRY( pBracketInfoName, DB_TD3, DT_uint16, offsetof( TitleData3, EloBracketInfo[ m ] ) );
|
||||
}
|
||||
|
||||
#endif // _GAMECONSOLE
|
||||
|
||||
#if defined ( _X360 )
|
||||
// matchmaking data
|
||||
#define CFG( cppType, name ) \
|
||||
TD_ENTRY( "MMDATA.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, usrMMData.name ) )
|
||||
#include "xlast_csgo/inc_mmdata_usr.inc"
|
||||
#undef CFG
|
||||
#endif // #if defined ( _X360 )
|
||||
|
||||
// END MARKER
|
||||
TD_ENTRY( (const char*) NULL, DB_TD3, DT_0, 0 )
|
||||
|
||||
#undef TD_ENTRY
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
COMPILE_TIME_ASSERT( sizeof( TitleData1 ) < XPROFILE_SETTING_MAX_SIZE );
|
||||
COMPILE_TIME_ASSERT( sizeof( TitleData2 ) < XPROFILE_SETTING_MAX_SIZE );
|
||||
COMPILE_TIME_ASSERT( sizeof( TitleData3 ) < XPROFILE_SETTING_MAX_SIZE );
|
||||
|
||||
#endif
|
||||
|
||||
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[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.achtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return tad;
|
||||
}
|
||||
|
||||
TitleAvatarAwardsDescription_t const * CMatchTitle::DescribeTitleAvatarAwards()
|
||||
{
|
||||
static TitleAvatarAwardsDescription_t taad[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.avawtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return taad;
|
||||
}
|
||||
|
||||
TitleDlcDescription_t const * CMatchTitle::DescribeTitleDlcs()
|
||||
{
|
||||
static TitleDlcDescription_t tdlcs[] =
|
||||
{
|
||||
//{ 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 )
|
||||
{
|
||||
#if !defined( NO_STEAM )
|
||||
if ( StringAfterPrefix( szLeaderboardView, "WINS_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score wins_ratio " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score total_wins"
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score total_losses "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score win_as_ct "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score win_as_t "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload4 { "
|
||||
" :score loss_as_ct "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload5 { "
|
||||
" :score loss_as_t "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetString( ":scoreformula", "( payload0 / max( payload0 + payload1, 1 ) ) * ( min( payload0 + payload1, 20 ) / 20 ) * 10000000" );
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // 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_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "CS_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score average_contribution " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score mvp_awards"
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score rounds_played "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score total_contribution "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score kills "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload4 { "
|
||||
" :score deaths "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload5 { "
|
||||
" :score damage "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetString( ":scoreformula", "( payload2 / max( payload1, 1 ) )" );
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // 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_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "KD_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score kd_ratio " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score kills"
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score deaths "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score rounds_played "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score shots_fired "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload4 { "
|
||||
" :score head_shots "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload5 { "
|
||||
" :score shots_hit "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetString( ":scoreformula", "( payload0 / max( payload1, 1 ) ) * ( min( payload2, 20 ) / 20 ) * 10000000" );
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // 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_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "STARS_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score numstars " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :scoresum 1 "
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score bombs_planted "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score bombs_detonated "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score bombs_defused "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score hostages_rescued "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // 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;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "GP_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score num_rounds " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :scoresum 1 "
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score time_played "
|
||||
" :format uint64 "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score time_played_ct "
|
||||
" :format uint64 "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score time_played_t "
|
||||
" :format uint64 "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score total_medals "
|
||||
" :format int "
|
||||
" :upload last " // the last value written is the authoritative value of total achievement medals unlocked
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // 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;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !NO_STEAM
|
||||
|
||||
/*
|
||||
// Check if this is a survival leaderboard
|
||||
if ( char const *szSurvivalMap = StringAfterPrefix( szLeaderboardView, "survival_" ) )
|
||||
{
|
||||
if ( IsX360() )
|
||||
{
|
||||
// Find the corresponding record in the mission script
|
||||
KeyValues *pSettings = new KeyValues( "settings" );
|
||||
KeyValues::AutoDelete autodelete_pSettings( pSettings );
|
||||
pSettings->SetString( "game/mode", "survival" );
|
||||
|
||||
KeyValues *pMissionInfo = NULL;
|
||||
KeyValues *pMapInfo = g_pMatchExtL4D->GetMapInfoByBspName( pSettings, szSurvivalMap, &pMissionInfo );
|
||||
if ( !pMapInfo || !pMissionInfo )
|
||||
return NULL;
|
||||
|
||||
// Find the leaderboard description in the map info
|
||||
KeyValues *pLbDesc = pMapInfo->FindKey( "x360leaderboard" );
|
||||
if ( !pLbDesc )
|
||||
return NULL;
|
||||
|
||||
// Insert the required keys
|
||||
pLbDesc = pLbDesc->MakeCopy();
|
||||
|
||||
static KeyValues *s_pRatingKey = KeyValues::FromString( ":rating", // X360 leaderboards are rated
|
||||
" name besttime " // game name of the rating field is "besttime"
|
||||
" type uint64 " // type is uint64
|
||||
" rule max" // rated field must be greater than cached value so that it can be written
|
||||
);
|
||||
pLbDesc->AddSubKey( s_pRatingKey->MakeCopy() );
|
||||
pLbDesc->SetString( "besttime/type", "uint64" );
|
||||
|
||||
return pLbDesc;
|
||||
}
|
||||
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score besttime " // :score is the leaderboard value mapped to game name "besttime"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeTimeMilliSeconds ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user