summaryrefslogtreecommitdiff
path: root/game/client/tf/tf_presence.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/tf_presence.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/tf/tf_presence.cpp')
-rw-r--r--game/client/tf/tf_presence.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/game/client/tf/tf_presence.cpp b/game/client/tf/tf_presence.cpp
new file mode 100644
index 0000000..6c741f8
--- /dev/null
+++ b/game/client/tf/tf_presence.cpp
@@ -0,0 +1,531 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Rich Presence support.
+// HACK: This file has also become the client wing of matchmaking. Matchmaking should
+// be re-factored to make a more complete client/server/engine interface
+//
+//=====================================================================================//
+
+#include "cbase.h"
+#include "tf_presence.h"
+#include "c_team_objectiveresource.h"
+#include "tf_gamerules.h"
+#include "c_tf_team.h"
+#include "c_tf_playerresource.h"
+#include "engine/imatchmaking.h"
+#include "ixboxsystem.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Global singleton
+static CTF_Presence s_presence;
+
+struct s_MapName
+{
+ const char *pDiskName;
+ const char *pDisplayName;
+};
+
+// This array must match the define order in hl2orange.spa.h
+static s_MapName s_Scenarios[] = {
+ { "ctf_2fort", "2Fort" },
+ { "cp_dustbowl", "Dustbowl" },
+ { "cp_granary", "Granary" },
+ { "cp_well", "Well" },
+ { "cp_gravelpit", "Gravel Pit" },
+ { "tc_hydro", "Hydro" },
+ { "cloak", "Cloak (CTF)" },
+ { "cp_cloak", "Cloak (CP)" },
+};
+
+struct s_PresenceTranslation
+{
+ uint id;
+ const char *pString;
+};
+
+// Only presence IDs can be searched by id number, because they're guaranteed to be unique
+static s_PresenceTranslation s_PresenceIds[] = {
+ { CONTEXT_SCENARIO, "CONTEXT_SCENARIO" },
+ { PROPERTY_CAPS_OWNED, "PROPERTY_CAPS_OWNED" },
+ { PROPERTY_CAPS_TOTAL, "PROPERTY_CAPS_TOTAL" },
+ { PROPERTY_FLAG_CAPTURE_LIMIT, "PROPERTY_FLAG_CAPTURE_LIMIT" },
+ { PROPERTY_NUMBER_OF_ROUNDS, "PROPERTY_NUMBER_OF_ROUNDS" },
+ { PROPERTY_WIN_LIMIT, "PROPERTY_WIN_LIMIT" },
+ { PROPERTY_GAME_SIZE, "PROPERTY_GAME_SIZE" },
+ { PROPERTY_AUTOBALANCE, "PROPERTY_AUTOBALANCE" },
+ { PROPERTY_PRIVATE_SLOTS, "PROPERTY_PRIVATE_SLOTS" },
+ { PROPERTY_MAX_GAME_TIME, "PROPERTY_MAX_GAME_TIME" },
+ { PROPERTY_NUMBER_OF_TEAMS, "PROPERTY_NUMBER_OF_TEAMS" },
+ { PROPERTY_TEAM, "PROPERTY_TEAM" },
+#if defined( _X360 )
+ { X_CONTEXT_GAME_MODE, "CONTEXT_GAME_MODE" },
+ { X_CONTEXT_GAME_TYPE, "CONTEXT_GAME_TYPE" },
+#endif
+};
+
+// Presence values cannot be searched by id number, because they are not unique
+static s_PresenceTranslation s_PresenceValues[] = {
+ { SESSION_MATCH_QUERY_PLAYER_MATCH, "SESSION_MATCH_QUERY_PLAYER_MATCH" },
+ { CONTEXT_GAME_MODE_MULTIPLAYER, "CONTEXT_GAME_MODE_MULTIPLAYER" },
+ { CONTEXT_SCENARIO_CTF_2FORT, "CONTEXT_SCENARIO_CTF_2FORT" },
+ { CONTEXT_SCENARIO_CP_DUSTBOWL, "CONTEXT_SCENARIO_CP_DUSTBOWL" },
+ { CONTEXT_SCENARIO_CP_GRANARY, "CONTEXT_SCENARIO_CP_GRANARY" },
+ { CONTEXT_SCENARIO_CP_WELL, "CONTEXT_SCENARIO_CP_WELL" },
+ { CONTEXT_SCENARIO_CP_GRAVELPIT, "CONTEXT_SCENARIO_CP_GRAVELPIT" },
+ { CONTEXT_SCENARIO_TC_HYDRO, "CONTEXT_SCENARIO_TC_HYDRO" },
+ { CONTEXT_SCENARIO_CTF_CLOAK, "CONTEXT_SCENARIO_CTF_CLOAK" },
+ { CONTEXT_SCENARIO_CP_CLOAK, "CONTEXT_SCENARIO_CP_CLOAK" },
+#if defined( _X360 )
+ { XSESSION_CREATE_LIVE_MULTIPLAYER_STANDARD, "SESSION_CREATE_LIVE_MULTIPLAYER_STANDARD" },
+ { XSESSION_CREATE_LIVE_MULTIPLAYER_RANKED, "SESSION_CREATE_LIVE_MULTIPLAYER_RANKED" },
+ { XSESSION_CREATE_SYSTEMLINK, "SESSION_CREATE_SYSTEMLINK" },
+ { X_CONTEXT_GAME_TYPE_STANDARD, "CONTEXT_GAME_TYPE_STANDARD" },
+ { X_CONTEXT_GAME_TYPE_RANKED, "CONTEXT_GAME_TYPE_RANKED" },
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Convert a map name to a defined ID.
+//-----------------------------------------------------------------------------
+static unsigned int GetMapID( const char *pMapName )
+{
+ for ( int i = 0; i < ARRAYSIZE( s_Scenarios ); ++i )
+ {
+ if ( !Q_stricmp( s_Scenarios[i].pDiskName, pMapName ) )
+ {
+ return i;
+ }
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Convert a session property string to a display string for gameUI.
+//-----------------------------------------------------------------------------
+void CTF_Presence::GetPropertyDisplayString( uint id, uint value, char *pOutput, int nBytes )
+{
+ const char *pDisplayString = "";
+
+ switch( id )
+ {
+#if defined( _X360 )
+ case X_CONTEXT_GAME_TYPE:
+ switch( value )
+ {
+ case X_CONTEXT_GAME_TYPE_STANDARD:
+ pDisplayString = "#TF_Unranked";
+ break;
+
+ case X_CONTEXT_GAME_TYPE_RANKED:
+ pDisplayString = "#TF_Ranked";
+ break;
+ }
+ break;
+#endif
+ case CONTEXT_SCENARIO:
+ pDisplayString = s_Scenarios[value].pDisplayName;
+ break;
+
+ case PROPERTY_FLAG_CAPTURE_LIMIT:
+ case PROPERTY_NUMBER_OF_ROUNDS:
+ case PROPERTY_WIN_LIMIT:
+ Q_snprintf( pOutput, nBytes, "%d", value );
+ return;
+
+ case PROPERTY_MAX_GAME_TIME:
+ if ( value >= NO_TIME_LIMIT )
+ {
+ Q_strncpy( pOutput, "#TF_MaxTimeNoLimit", nBytes );
+ }
+ else
+ {
+ Q_snprintf( pOutput, nBytes, "%d:00", value );
+ }
+ return;
+
+ case PROPERTY_TEAM:
+ switch ( value )
+ {
+ case 0:
+ pDisplayString = "blue";
+ break;
+
+ case 1:
+ pDisplayString = "red";
+ break;
+
+ case 2:
+ pDisplayString = "spectator";
+ break;
+ }
+ break;
+
+ default:
+ pDisplayString = "Unknown";
+ break;
+ }
+
+ Q_strncpy( pOutput, pDisplayString, nBytes );
+}
+
+//-----------------------------------------------------------------------------
+// Convert a presence ID to a string.
+//-----------------------------------------------------------------------------
+const char *CTF_Presence::GetPropertyIdString( const uint id )
+{
+ for ( int i = 0; i < ARRAYSIZE( s_PresenceIds ); ++i )
+ {
+ if ( s_PresenceIds[i].id == id )
+ {
+ return s_PresenceIds[i].pString;
+ }
+ }
+ return "Unknown";
+}
+
+//-----------------------------------------------------------------------------
+// Convert a session property string to an ID.
+//-----------------------------------------------------------------------------
+uint CTF_Presence::GetPresenceID( const char *pIDName )
+{
+ for ( int i = 0; i < ARRAYSIZE( s_PresenceIds ); ++i )
+ {
+ if ( !Q_stricmp( s_PresenceIds[i].pString, pIDName ) )
+ {
+ return s_PresenceIds[i].id;
+ }
+ }
+
+ for ( int i = 0; i < ARRAYSIZE( s_PresenceValues ); ++i )
+ {
+ if ( !Q_stricmp( s_PresenceValues[i].pString, pIDName ) )
+ {
+ return s_PresenceValues[i].id;
+ }
+ }
+
+ Warning( "Presence ID not found for %s\n", pIDName );
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Level init
+//-----------------------------------------------------------------------------
+void CTF_Presence::LevelInitPreEntity( void )
+{
+ m_bIsInCommentary = false;
+ const char *pMapName = MapName();
+ if ( pMapName )
+ {
+ UserSetContext( XBX_GetPrimaryUserId(), CONTEXT_SCENARIO, GetMapID( pMapName ), true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Init
+//-----------------------------------------------------------------------------
+bool CTF_Presence::Init()
+{
+ presence = &s_presence;
+
+ ListenForGameEvent( "controlpoint_initialized" );
+ ListenForGameEvent( "controlpoint_updateowner" );
+ ListenForGameEvent( "teamplay_round_start" );
+ ListenForGameEvent( "ctf_flag_captured" );
+ ListenForGameEvent( "playing_commentary" );
+
+ return CBasePresence::Init();
+}
+
+//-----------------------------------------------------------------------------
+// Get game session properties from matchmaking.
+//-----------------------------------------------------------------------------
+void CTF_Presence::SetupGameProperties( CUtlVector< XUSER_CONTEXT > &contexts, CUtlVector< XUSER_PROPERTY > &properties )
+{
+ // Session properties have been set for this game. Use our knowledge of
+ // the properties that have been defined for this game to set rules, cvars, etc.
+ char buffer[MAX_PATH];
+
+#if 0 // defined( _X360 ) // absolutely nothing happens in this loop, so I disabled it. It was breaking the compiler in LTCG mode. -egr
+ int count = contexts.Count();
+ for ( int i = 0; i < count; ++i )
+ {
+ XUSER_CONTEXT &ctx = contexts[i];
+ switch( ctx.dwContextId )
+ {
+ case X_CONTEXT_GAME_TYPE:
+ if ( ctx.dwValue == X_CONTEXT_GAME_TYPE_RANKED )
+ {
+ }
+ else if ( ctx.dwValue == X_CONTEXT_GAME_TYPE_STANDARD )
+ {
+ }
+ break;
+ }
+ }
+#endif
+
+ for ( int i = 0; i < properties.Count(); ++i )
+ {
+ XUSER_PROPERTY &prop = properties[i];
+ switch( prop.dwPropertyId )
+ {
+ case PROPERTY_FLAG_CAPTURE_LIMIT:
+ Q_snprintf( buffer, sizeof( buffer ), "tf_flag_caps_per_round %d", prop.value.nData );
+ engine->ClientCmd( buffer );
+ break;
+
+ case PROPERTY_NUMBER_OF_ROUNDS:
+ Q_snprintf( buffer, sizeof( buffer ), "mp_maxrounds %d", prop.value.nData );
+ engine->ClientCmd( buffer );
+ break;
+
+ case PROPERTY_WIN_LIMIT:
+ Q_snprintf( buffer, sizeof( buffer ), "mp_winlimit %d", prop.value.nData );
+ engine->ClientCmd( buffer );
+ break;
+
+ case PROPERTY_GAME_SIZE:
+ Q_snprintf( buffer, sizeof( buffer ), "maxplayers %d", prop.value.nData );
+ engine->ClientCmd( buffer );
+ break;
+
+ case PROPERTY_AUTOBALANCE:
+ Q_snprintf( buffer, sizeof( buffer ), "mp_autoteambalance %d", prop.value.nData );
+ engine->ClientCmd( buffer );
+ break;
+
+ case PROPERTY_MAX_GAME_TIME:
+ Q_snprintf( buffer, sizeof( buffer ), "mp_timelimit %d", prop.value.nData );
+ engine->ClientCmd( buffer );
+ break;
+
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Respond to TF game events.
+//-----------------------------------------------------------------------------
+void CTF_Presence::FireGameEvent( IGameEvent *event )
+{
+ const char *eventname = event->GetName();
+
+ if ( !Q_stricmp( "teamplay_round_start", eventname ) )
+ {
+ // Set presence for this map
+ // TODO: Set appropriate presence mode based on game type
+#if defined( _X360 )
+ if ( TFGameRules() && !m_bIsInCommentary )
+ {
+ if ( TFGameRules()->GetGameType() == TF_GAMETYPE_CP )
+ {
+ UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_TF_CP, true );
+ }
+ else if ( TFGameRules()->GetGameType() == TF_GAMETYPE_CTF )
+ {
+ // ctf games start tied
+ int zeroscore = 0;
+ UserSetProperty( XBX_GetPrimaryUserId(), PROPERTY_PLAYER_TEAM_SCORE, sizeof(int), &zeroscore, true );
+ UserSetProperty( XBX_GetPrimaryUserId(), PROPERTY_OPPONENT_TEAM_SCORE, sizeof(int), &zeroscore, true );
+ UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_TF_CTF_TIED, true );
+ }
+ }
+#endif
+ }
+ else if ( !Q_stricmp( "controlpoint_initialized", eventname ) )
+ {
+ int nPoints = ObjectiveResource()->GetNumControlPoints();
+ int nOwned = ObjectiveResource()->GetNumControlPointsOwned();
+
+ UserSetProperty( XBX_GetPrimaryUserId(), PROPERTY_CAPS_TOTAL, sizeof(int), &nPoints, true );
+ UserSetProperty( XBX_GetPrimaryUserId(), PROPERTY_CAPS_OWNED, sizeof(int), &nOwned, true );
+ }
+ else if ( !Q_stricmp( "controlpoint_updateowner", eventname ) )
+ {
+ int nOwned = ObjectiveResource()->GetNumControlPointsOwned();
+ UserSetProperty( XBX_GetPrimaryUserId(), PROPERTY_CAPS_OWNED, sizeof(int), &nOwned, true );
+ }
+ else if ( !Q_stricmp( "ctf_flag_captured", eventname ) )
+ {
+ C_TFTeam *pLocalTeam = GetGlobalTFTeam( GetLocalPlayerTeam() );
+
+ if ( pLocalTeam )
+ {
+ int iOtherScore = 0;
+ int iTeamScore = 0;
+ int iCappingTeam = event->GetInt( "capping_team" );
+ int iCappingTeamScore = event->GetInt( "capping_team_score" );
+
+ // If the local player is on the team that just captured
+ if ( iCappingTeam == pLocalTeam->GetTeamNumber() )
+ {
+ // the newly capped score is our current score
+ iTeamScore = iCappingTeamScore;
+ }
+ else // Other team capped
+ {
+ // Start other team score at the newly capped score set by the game event.
+ // It can be higher than any we have locally recorded because of networking lag.
+ iOtherScore = iCappingTeamScore;
+ iTeamScore = pLocalTeam->GetFlagCaptures();
+ }
+
+ // highest score of any other team is the opposing score
+ for ( int i = 0; i < g_Teams.Count(); i++ )
+ {
+ C_TFTeam* pCurTeam = ( dynamic_cast< C_TFTeam* >( g_Teams[i] ) );
+ if ( pCurTeam )
+ {
+ if ( GetLocalPlayerTeam() == pCurTeam->GetTeamNumber() )
+ continue;
+
+ int iCurScore = pCurTeam->GetFlagCaptures();
+
+ if ( iCurScore > iOtherScore )
+ {
+ iOtherScore = iCurScore;
+ }
+ }
+ }
+
+ UserSetProperty( XBX_GetPrimaryUserId(), PROPERTY_PLAYER_TEAM_SCORE, sizeof(int), &iTeamScore, true );
+ UserSetProperty( XBX_GetPrimaryUserId(), PROPERTY_OPPONENT_TEAM_SCORE, sizeof(int), &iOtherScore, true );
+#if defined ( _X360 )
+ if ( !m_bIsInCommentary )
+ {
+ if ( iTeamScore > iOtherScore )
+ {
+ UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_TF_CTF_WINNING, true );
+ }
+ else if ( iOtherScore > iTeamScore )
+ {
+ UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_TF_CTF_LOSING, true );
+ }
+ else
+ {
+ UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_TF_CTF_TIED, true );
+ }
+ }
+#endif
+ }
+ }
+ else if ( !Q_stricmp( "playing_commentary", eventname ) )
+ {
+ m_bIsInCommentary = true;
+#if defined ( _X360 )
+ UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_COMMENTARY, true );
+ UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_GAME_MODE, CONTEXT_GAME_MODE_SINGLEPLAYER, true );
+#endif
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Upload player stats to Live.
+//-----------------------------------------------------------------------------
+void CTF_Presence::UploadStats()
+{
+#if defined( _X360 )
+ if ( m_bReportingStats )
+ {
+ m_ViewProperties[0].dwViewId = X_STATS_VIEW_SKILL;
+ m_ViewProperties[1].dwViewId = m_bArbitrated ? STATS_VIEW_PLAYER_MAX_RANKED : STATS_VIEW_PLAYER_MAX_UNRANKED;
+ m_ViewProperties[2].dwViewId = STATS_VIEW_PLAYER_MAX_UNRANKED;
+
+ CUtlVector< XUSER_PROPERTY > skillStats;
+
+ if ( !g_TF_PR )
+ return;
+
+ XUID localId = matchmaking->PlayerIdToXuid( GetLocalPlayerIndex() );
+
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ XUID id = matchmaking->PlayerIdToXuid( i );
+ if ( id == 0 )
+ continue;
+
+ // For non-ranked sessions, only the local player's stats are written
+ if ( !m_bArbitrated && id != localId )
+ continue;
+
+ skillStats.RemoveAll();
+ int viewCt = 1;
+
+ if ( id != 0 )
+ {
+ Msg( "XUID: %d\n", id );
+ XUSER_PROPERTY prop;
+
+ int nScore = g_TF_PR->GetTotalScore( i );
+
+ // Write the player's skill stats
+ prop.dwPropertyId = X_PROPERTY_RELATIVE_SCORE;
+ prop.value.type = XUSER_DATA_TYPE_INT32;
+ prop.value.nData = nScore;
+ skillStats.AddToTail( prop );
+
+ prop.dwPropertyId = X_PROPERTY_SESSION_TEAM;
+ prop.value.type = XUSER_DATA_TYPE_INT32;
+ prop.value.nData = i;
+ skillStats.AddToTail( prop );
+
+ m_ViewProperties[0].dwNumProperties = skillStats.Count();
+ m_ViewProperties[0].pProperties = skillStats.Base();
+
+ Msg( "Skill:\n" );
+ Msg( "Relative Score: %d\n" , skillStats[0].value.nData );
+ Msg( "Team: %d\n" , skillStats[1].value.nData );
+
+ if ( id != localId )
+ {
+ // Write the remote player's points scored
+ prop.dwPropertyId = PROPERTY_POINTS_SCORED;
+ prop.value.type = XUSER_DATA_TYPE_INT64;
+ prop.value.nData = nScore;
+
+ m_ViewProperties[1].dwNumProperties = 1;
+ m_ViewProperties[1].pProperties = &prop;
+
+ viewCt = 2;
+
+ Msg( "Points Scored: %d\n" , prop.value.nData );
+ }
+ else
+ {
+ // Write the local player's points scored
+ prop.dwPropertyId = PROPERTY_POINTS_SCORED;
+ prop.value.type = XUSER_DATA_TYPE_INT64;
+ prop.value.nData = nScore;
+ m_ViewProperties[1].dwNumProperties = 1;
+ m_ViewProperties[1].pProperties = &prop;
+
+ // Include the local player's array of personal stats
+ m_ViewProperties[2].dwNumProperties = m_PlayerStats.Count();
+ m_ViewProperties[2].pProperties = m_PlayerStats.Base();
+
+ viewCt = 3;
+
+ Msg( "Points Scored: %d\n" , prop.value.nData );
+ Msg( "Unranked stat count: %d\n", m_ViewProperties[2].dwNumProperties );
+ }
+ }
+
+ DWORD ret = xboxsystem->WriteStats( m_hSession, id , viewCt, m_ViewProperties, false );
+ if ( ret != ERROR_SUCCESS )
+ {
+ Warning( "Write stats failed with error %d\n", ret );
+ }
+ }
+
+ m_PlayerStats.RemoveAll();
+ m_bReportingStats = false;
+ }
+#endif
+}
+