//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: steam state machine that handles authenticating steam users // //=============================================================================// #ifdef _WIN32 #if !defined( _X360 ) #include "winlite.h" #include // INADDR_ANY defn #endif #elif POSIX #include #endif #include #include "cl_steamauth.h" #include "interface.h" #include "filesystem_engine.h" #include "tier0/icommandline.h" #include "tier0/vprof.h" #include "host.h" #include "cmd.h" #include "common.h" #ifndef SWDS #include "vgui_baseui_interface.h" #endif #pragma warning( disable: 4355 ) // disables ' 'this' : used in base member initializer list' //----------------------------------------------------------------------------- // Purpose: singleton accessor //----------------------------------------------------------------------------- static CSteam3Client s_Steam3Client; CSteam3Client &Steam3Client() { return s_Steam3Client; } //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CSteam3Client::CSteam3Client() #if !defined(NO_STEAM) : m_CallbackClientGameServerDeny( this, &CSteam3Client::OnClientGameServerDeny ), m_CallbackGameServerChangeRequested( this, &CSteam3Client::OnGameServerChangeRequested ), m_CallbackGameOverlayActivated( this, &CSteam3Client::OnGameOverlayActivated ), m_CallbackPersonaStateChanged( this, &CSteam3Client::OnPersonaUpdated ), m_CallbackLowBattery( this, &CSteam3Client::OnLowBattery ) #endif { m_bActive = false; m_bGSSecure = false; m_hAuthTicket = k_HAuthTicketInvalid; m_unIP = 0; m_usPort = 0; m_nTicketSize = 0; } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CSteam3Client::~CSteam3Client() { Shutdown(); } //----------------------------------------------------------------------------- // Purpose: Unload the steam3 engine //----------------------------------------------------------------------------- void CSteam3Client::Shutdown() { if ( !m_bActive ) return; m_bActive = false; #if !defined( NO_STEAM ) SteamAPI_Shutdown(); Clear(); // clear our interface pointers now they are invalid #endif } //----------------------------------------------------------------------------- // Purpose: Initialize the steam3 connection //----------------------------------------------------------------------------- void CSteam3Client::Activate() { if ( m_bActive ) return; m_bActive = true; m_bGSSecure = false; #if !defined( NO_STEAM ) SteamAPI_InitSafe(); // ignore failure, that will fall out later when they don't get a valid logon cookie SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers Init(); // Steam API context init #endif } //----------------------------------------------------------------------------- // Purpose: Get the steam3 logon cookie to use //----------------------------------------------------------------------------- void CSteam3Client::GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket, uint32 unIP, uint16 usPort, uint64 unGSSteamID, bool bSecure ) { #ifdef NO_STEAM m_bGSSecure = bSecure; #else CSteamID steamIDGS( unGSSteamID ); // Assume failure *pcbTicket = 0; // We must have interface pointers if ( !SteamUser() ) { Warning( "No SteamUser interface. Cannot perform steam authentication\n" ); return; } // Make sure we have a valid Steam ID CSteamID steamID = SteamUser()->GetSteamID(); if ( !steamID.IsValid() ) { Warning( "Our steam ID %s is not valid. Steam must be running and you must be logged in\n", steamID.Render() ); return; } // Get a new ticket, if we don't already have one for this server if ( m_hAuthTicket == k_HAuthTicketInvalid || m_unIP != unIP || m_usPort != usPort || m_bGSSecure != bSecure || m_steamIDGS != steamIDGS || m_nTicketSize <= 0 ) { // Cancel any previously issues ticket if ( m_hAuthTicket != k_HAuthTicketInvalid ) SteamUser()->CancelAuthTicket( m_hAuthTicket ); // Shove the GS ID in the first bits *(uint64*)m_arbTicketData = SteamUser()->GetSteamID().ConvertToUint64(); // Ask Steam for a ticket m_nTicketSize = 0; m_hAuthTicket = SteamUser()->GetAuthSessionTicket( m_arbTicketData+sizeof(uint64), sizeof(m_arbTicketData)-sizeof(uint64), &m_nTicketSize ); if ( m_hAuthTicket == k_HAuthTicketInvalid || m_nTicketSize <= 0 ) { // Failed! Assert( m_hAuthTicket != k_HAuthTicketInvalid ); Assert( m_nTicketSize > 0 ); m_hAuthTicket = k_HAuthTicketInvalid; m_nTicketSize = 0; Warning( "ISteamUser::GetAuthSessionTicket failed to return a valid ticket\n" ); } else { // Got valid ticket. Remember its properties m_nTicketSize += sizeof(uint64); m_unIP = unIP; m_usPort = usPort; m_bGSSecure = bSecure; m_steamIDGS = steamIDGS; } } // Give them back the ticket data, if we were able to get one, and it will fit *pcbTicket = 0; if ( m_nTicketSize > 0 ) { if ( cbMaxTicket >= (int)m_nTicketSize ) { memcpy( pTicket, m_arbTicketData, m_nTicketSize ); *pcbTicket = m_nTicketSize; } else { Assert( cbMaxTicket >= (int)m_nTicketSize ); } } // Tell the steam backend about the server we are playing on - so that it can broadcast this to our friends and they can join. // This may be something that you should NOT do if you are on a listen server - or you know that the game is not joinable // for some reason. #ifdef _DEBUG Msg( "Sending AdvertiseGame %s %s (%s)\n", steamIDGS.Render(), netadr_t(unIP, usPort).ToString(), m_bGSSecure ? "secure" : "insecure" ); #endif SteamUser()->AdvertiseGame( steamIDGS, unIP, usPort ); #endif } //----------------------------------------------------------------------------- // Purpose: Tell steam that we are leaving a server //----------------------------------------------------------------------------- void CSteam3Client::CancelAuthTicket() { m_bGSSecure = false; if ( !SteamUser() ) return; #if !defined( NO_STEAM ) if ( m_hAuthTicket != k_HAuthTicketInvalid ) SteamUser()->CancelAuthTicket( m_hAuthTicket ); #ifdef _DEBUG Msg( "Clearing auth ticket, sending void AdvertiseGame\n" ); #endif CSteamID steamIDGS; // invalid steamID means not playing anywhere SteamUser()->AdvertiseGame( steamIDGS, 0, 0 ); m_hAuthTicket = k_HAuthTicketInvalid; #endif } //----------------------------------------------------------------------------- // Purpose: Process any callbacks we may have //----------------------------------------------------------------------------- void CSteam3Client::RunFrame() { #if !defined( NO_STEAM ) VPROF_BUDGET( "CSteam3Client::RunFrame", VPROF_BUDGETGROUP_STEAM ); SteamAPI_RunCallbacks(); #endif } #if !defined(NO_STEAM) //----------------------------------------------------------------------------- // Purpose: Disconnect the user from their current server //----------------------------------------------------------------------------- void CSteam3Client::OnClientGameServerDeny( ClientGameServerDeny_t *pClientGameServerDeny ) { if ( pClientGameServerDeny->m_uAppID == GetSteamAppID() ) { const char *pszReason = "Unknown"; switch ( pClientGameServerDeny->m_uReason ) { case ( k_EDenyInvalidVersion ) : pszReason = "Invalid version"; break; case ( k_EDenyGeneric ) : pszReason = "Kicked"; break; case ( k_EDenyNotLoggedOn ) : pszReason = "Not logged on"; break; case ( k_EDenyNoLicense ) : pszReason = "No license"; break; case ( k_EDenyCheater ) : pszReason = "VAC banned "; break; case ( k_EDenyLoggedInElseWhere ) : pszReason = "Dropped from server"; break; case ( k_EDenyUnknownText ) : pszReason = "Unknown"; break; case ( k_EDenyIncompatibleAnticheat ) : pszReason = "Incompatible Anti Cheat"; break; case ( k_EDenyMemoryCorruption ) : pszReason = "Memory corruption"; break; case ( k_EDenyIncompatibleSoftware ) : pszReason = "Incompatible software"; break; case ( k_EDenySteamConnectionLost ) : pszReason = "Steam connection lost"; break; case ( k_EDenySteamConnectionError ) : pszReason = "Steam connection error"; break; case ( k_EDenySteamResponseTimedOut ) : pszReason = "Response timed out"; break; case ( k_EDenySteamValidationStalled ) : pszReason = "Verification failed"; break; } Warning( "Disconnect: %s\n", pszReason ); Host_Disconnect( true ); } } extern ConVar password; //----------------------------------------------------------------------------- // Purpose: Disconnect the user from their current server //----------------------------------------------------------------------------- void CSteam3Client::OnGameServerChangeRequested( GameServerChangeRequested_t *pGameServerChangeRequested ) { password.SetValue( pGameServerChangeRequested->m_rgchPassword ); Msg( "Connecting to %s\n", pGameServerChangeRequested->m_rgchServer ); Cbuf_AddText( va( "connect %s steam\n", pGameServerChangeRequested->m_rgchServer ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CSteam3Client::OnGameOverlayActivated( GameOverlayActivated_t *pGameOverlayActivated ) { #ifndef SWDS if ( Host_IsSinglePlayerGame() ) { if ( !EngineVGui()->IsGameUIVisible() && !EngineVGui()->IsConsoleVisible() ) { if ( pGameOverlayActivated->m_bActive ) Cbuf_AddText( "setpause" ); else Cbuf_AddText( "unpause" ); } } #endif } extern void UpdateNameFromSteamID( IConVar *pConVar, CSteamID *pSteamID ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CSteam3Client::OnPersonaUpdated( PersonaStateChange_t *pPersonaStateChanged ) { if ( pPersonaStateChanged->m_nChangeFlags & k_EPersonaChangeName ) { if ( SteamUtils() && SteamFriends() && SteamUser() ) { CSteamID steamID = SteamUser()->GetSteamID(); IConVar *pConVar = g_pCVar->FindVar( "name" ); UpdateNameFromSteamID( pConVar, &steamID ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CSteam3Client::OnLowBattery( LowBatteryPower_t *pLowBat ) { // on the 9min, 5 min and 1 min warnings tell the engine to fire off a save switch( pLowBat->m_nMinutesBatteryLeft ) { case 9: case 5: case 1: Cbuf_AddText( "save LowBattery_AutoSave" ); break; default: break; } } #endif