summaryrefslogtreecommitdiff
path: root/engine/sv_steamauth.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 /engine/sv_steamauth.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'engine/sv_steamauth.cpp')
-rw-r--r--engine/sv_steamauth.cpp1069
1 files changed, 1069 insertions, 0 deletions
diff --git a/engine/sv_steamauth.cpp b/engine/sv_steamauth.cpp
new file mode 100644
index 0000000..144ae77
--- /dev/null
+++ b/engine/sv_steamauth.cpp
@@ -0,0 +1,1069 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: steam state machine that handles authenticating steam users
+//
+//===========================================================================//
+
+#ifdef _WIN32
+#if !defined( _X360 )
+#include "winlite.h"
+#include <winsock2.h> // INADDR_ANY defn
+#endif
+#elif POSIX
+#include <netinet/in.h>
+#endif
+
+#include "sv_steamauth.h"
+#include "sv_filter.h"
+#include "inetchannel.h"
+#include "netadr.h"
+#include "server.h"
+#include "proto_oob.h"
+#include "host.h"
+#include "tier0/vcrmode.h"
+#include "sv_plugin.h"
+#include "sv_log.h"
+#include "filesystem_engine.h"
+#include "filesystem_init.h"
+#include "tier0/icommandline.h"
+#include "steam/steam_gameserver.h"
+#include "hltvserver.h"
+#include "sys_dll.h"
+#if defined( REPLAY_ENABLED )
+#include "replayserver.h"
+#endif
+
+extern ConVar sv_lan;
+extern ConVar sv_visiblemaxplayers;
+extern ConVar sv_region;
+extern ConVar tv_enable;
+
+static void sv_setsteamblockingcheck_f( IConVar *pConVar, const char *pOldString, float flOldValue );
+
+ConVar sv_steamblockingcheck( "sv_steamblockingcheck", "0", 0,
+ "Check each new player for Steam blocking compatibility, 1 = message only, 2 >= drop if any member of owning clan blocks,"
+ "3 >= drop if any player has blocked, 4 >= drop if player has blocked anyone on server", sv_setsteamblockingcheck_f );
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#pragma warning( disable: 4355 ) // disables ' 'this' : used in base member initializer list'
+
+ConVar sv_master_share_game_socket( "sv_master_share_game_socket", "1", 0,
+ "Use the game's socket to communicate to the master server. "
+ "If this is 0, then it will create a socket on -steamport + 1 "
+ "to communicate to the master server on." );
+
+static char s_szTempMsgBuf[16000];
+
+static void MsgAndLog( const char *fmt, ... )
+{
+ va_list ap;
+ va_start(ap, fmt);
+ V_vsprintf_safe( s_szTempMsgBuf, fmt, ap );
+
+ // Does Log always print to the console?
+ //if ( !engine->IsDedicatedServer() )
+ // Msg("%s", s_szTempMsgBuf );
+ Log("%s", s_szTempMsgBuf );
+}
+
+static void WarningAndLog( const char *fmt, ... )
+{
+ va_list ap;
+ va_start(ap, fmt);
+ V_vsprintf_safe( s_szTempMsgBuf, fmt, ap );
+
+ // Does Log always print to the console?
+ Warning("%s", s_szTempMsgBuf );
+ Log("%s", s_szTempMsgBuf );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: singleton accessor
+//-----------------------------------------------------------------------------
+static CSteam3Server s_Steam3Server;
+CSteam3Server &Steam3Server()
+{
+ return s_Steam3Server;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CSteam3Server::CSteam3Server()
+#if !defined(NO_STEAM)
+:
+ m_CallbackLogonSuccess( this, &CSteam3Server::OnLogonSuccess ),
+ m_CallbackLogonFailure( this, &CSteam3Server::OnLogonFailure ),
+ m_CallbackLoggedOff( this, &CSteam3Server::OnLoggedOff ),
+ m_CallbackValidateAuthTicketResponse( this, &CSteam3Server::OnValidateAuthTicketResponse ),
+ m_CallbackPlayerCompatibilityResponse( this, &CSteam3Server::OnComputeNewPlayerCompatibilityResponse ),
+ m_CallbackGSPolicyResponse( this, &CSteam3Server::OnGSPolicyResponse )
+#endif
+{
+ m_bHasActivePlayers = false;
+ m_bLogOnResult = false;
+ m_eServerMode = eServerModeInvalid;
+ m_eServerType = eServerTypeNormal;
+ m_bWantsSecure = false; // default to insecure currently, this may change
+ m_bInitialized = false;
+ m_bWantsPersistentAccountLogon = false;
+ m_bLogOnFinished = false;
+ m_bMasterServerUpdaterSharingGameSocket = false;
+ m_steamIDLanOnly.InstancedSet( 0,0, k_EUniversePublic, k_EAccountTypeInvalid );
+ m_SteamIDGS.InstancedSet( 1, 0, k_EUniverseInvalid, k_EAccountTypeInvalid );
+ m_QueryPort = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: detect current server mode based on cvars & settings
+//-----------------------------------------------------------------------------
+EServerMode CSteam3Server::GetCurrentServerMode()
+{
+ if ( sv_lan.GetBool() )
+ {
+ return eServerModeNoAuthentication;
+ }
+ else if ( CommandLine()->FindParm( "-insecure" ) )
+ {
+ return eServerModeAuthentication;
+ }
+ else
+ {
+ return eServerModeAuthenticationAndSecure;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CSteam3Server::~CSteam3Server()
+{
+ Shutdown();
+}
+
+
+void CSteam3Server::Activate( EServerType serverType )
+{
+ // we are active, check if sv_lan changed or we're trying to change server type
+ if ( GetCurrentServerMode() == m_eServerMode && m_eServerType == serverType )
+ {
+ // we are active and LANmode/servertype didnt change. done.
+ return;
+ }
+
+ if ( BIsActive() )
+ {
+ // shut down before we change server mode
+ Shutdown();
+ }
+
+ m_unIP = INADDR_ANY;
+ m_usPort = 26900;
+
+ if ( CommandLine()->FindParm( "-steamport" ) )
+ {
+ m_usPort = CommandLine()->ParmValue( "-steamport", 26900 );
+ }
+
+ ConVarRef ipname( "ip" );
+ if ( ipname.IsValid() )
+ {
+ netadr_t ipaddr;
+ NET_StringToAdr( ipname.GetString(), &ipaddr );
+ if ( !ipaddr.IsLoopback() && !ipaddr.IsLocalhost() )
+ {
+ m_unIP = ipaddr.GetIPHostByteOrder();
+ }
+ }
+
+ m_eServerMode = GetCurrentServerMode();
+ m_eServerType = serverType;
+
+ char gamedir[MAX_OSPATH];
+ Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );
+
+ // Figure out the game port. If we're doing a SrcTV relay, then ignore the NS_SERVER port and don't tell Steam that we have a game server.
+ uint16 usGamePort = 0;
+ if ( serverType == eServerTypeNormal )
+ {
+ usGamePort = NET_GetUDPPort( NS_SERVER );
+ }
+
+ uint16 usMasterServerUpdaterPort;
+ if ( sv_master_share_game_socket.GetBool() )
+ {
+ m_bMasterServerUpdaterSharingGameSocket = true;
+ usMasterServerUpdaterPort = MASTERSERVERUPDATERPORT_USEGAMESOCKETSHARE;
+ if ( serverType == eServerTypeTVRelay )
+ m_QueryPort = NET_GetUDPPort( NS_HLTV );
+ else
+ m_QueryPort = usGamePort;
+ }
+ else
+ {
+ m_bMasterServerUpdaterSharingGameSocket = false;
+ usMasterServerUpdaterPort = m_usPort;
+ m_QueryPort = m_usPort;
+ }
+#ifndef _X360
+
+ switch ( m_eServerMode )
+ {
+ case eServerModeNoAuthentication:
+ MsgAndLog( "Initializing Steam libraries for LAN server\n" );
+ break;
+ case eServerModeAuthentication:
+ MsgAndLog( "Initializing Steam libraries for INSECURE Internet server. Authentication and VAC not requested.\n" );
+ break;
+ case eServerModeAuthenticationAndSecure:
+ MsgAndLog( "Initializing Steam libraries for secure Internet server\n" );
+ break;
+ default:
+ WarningAndLog( "Bogus eServermode %d!\n", m_eServerMode );
+ Assert( !"Bogus server mode?!" );
+ break;
+ }
+
+ SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers
+ if ( CommandLine()->FindParm("-hushsteam") || !SteamGameServer_InitSafe(
+ m_unIP,
+ m_usPort+1, // Steam lives on -steamport + 1, master server updater lives on -steamport.
+ usGamePort,
+ usMasterServerUpdaterPort,
+ m_eServerMode,
+ GetSteamInfIDVersionInfo().szVersionString ) )
+ {
+steam_no_good:
+#if !defined( NO_STEAM )
+ WarningAndLog( "*********************************************************\n" );
+ WarningAndLog( "*\tUnable to load Steam support library.*\n" );
+ WarningAndLog( "*\tThis server will operate in LAN mode only.*\n" );
+ WarningAndLog( "*********************************************************\n" );
+#endif
+ m_eServerMode = eServerModeNoAuthentication;
+ sv_lan.SetValue( true );
+ return;
+ }
+ Init(); // Steam API context init
+ if ( SteamGameServer() == NULL )
+ {
+ Assert( false );
+ goto steam_no_good;
+ }
+
+ // Note that SteamGameServer_InitSafe() calls SteamAPI_SetBreakpadAppID() for you, which is what we don't want if we wish
+ // to report crashes under a different AppId. Reset it back to our crashing one now.
+ if ( sv.IsDedicated() )
+ {
+ SteamAPI_SetBreakpadAppID( GetSteamInfIDVersionInfo().ServerAppID );
+ }
+
+ // Set some stuff that should NOT change while the server is
+ // running
+ SteamGameServer()->SetProduct( GetSteamInfIDVersionInfo().szProductString );
+ SteamGameServer()->SetGameDescription( serverGameDLL->GetGameDescription() );
+ SteamGameServer()->SetDedicatedServer( sv.IsDedicated() );
+ SteamGameServer()->SetModDir( gamedir );
+
+ // Use anonymous logon, or persistent?
+ if ( m_sAccountToken.IsEmpty() )
+ {
+ m_bWantsPersistentAccountLogon = false;
+ MsgAndLog( "No account token specified; logging into anonymous game server account. (Use sv_setsteamaccount to login to a persistent account.)\n" );
+ SteamGameServer()->LogOnAnonymous();
+ }
+ else
+ {
+ m_bWantsPersistentAccountLogon = true;
+ MsgAndLog( "Logging into Steam game server account\n" );
+
+ // TODO: Change this to use just the token when the SDK is updated
+ SteamGameServer()->LogOn( m_sAccountToken );
+ }
+
+#endif
+
+ SendUpdatedServerDetails();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: game server stopped, shutdown Steam game server session
+//-----------------------------------------------------------------------------
+void CSteam3Server::Shutdown()
+{
+ if ( !BIsActive() )
+ return;
+
+ SteamGameServer_Shutdown();
+ m_bHasActivePlayers = false;
+ m_bLogOnResult = false;
+ m_SteamIDGS = k_steamIDNotInitYetGS;
+ m_eServerMode = eServerModeInvalid;
+
+ Clear(); // Steam API context shutdown
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the userid's are the same
+//-----------------------------------------------------------------------------
+bool CSteam3Server::CompareUserID( const USERID_t & id1, const USERID_t & id2 )
+{
+ if ( id1.idtype != id2.idtype )
+ return false;
+
+ switch ( id1.idtype )
+ {
+ case IDTYPE_STEAM:
+ case IDTYPE_VALVE:
+ {
+ return (id1.steamid == id2.steamid );
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if this userid is already on this server
+//-----------------------------------------------------------------------------
+bool CSteam3Server::CheckForDuplicateSteamID( const CBaseClient *client )
+{
+ // in LAN mode we allow reuse of SteamIDs
+ if ( BLanOnly() )
+ return false;
+
+ // Compare connecting client's ID to other IDs on the server
+ for ( int i=0 ; i< sv.GetClientCount() ; i++ )
+ {
+ const IClient *cl = sv.GetClient( i );
+
+ // Not connected, no SteamID yet
+ if ( !cl->IsConnected() || cl->IsFakeClient() )
+ continue;
+
+ if ( cl->GetNetworkID().idtype != IDTYPE_STEAM )
+ continue;
+
+ // don't compare this client against himself in the list
+ if ( client == cl )
+ continue;
+
+ if ( !CompareUserID( client->GetNetworkID(), cl->GetNetworkID() ) )
+ continue;
+
+ // SteamID is reused
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when secure policy is set
+//-----------------------------------------------------------------------------
+const CSteamID &CSteam3Server::GetGSSteamID()
+{
+ return m_SteamIDGS;
+}
+
+
+#if !defined(NO_STEAM)
+//-----------------------------------------------------------------------------
+// Purpose: Called when secure policy is set
+//-----------------------------------------------------------------------------
+void CSteam3Server::OnGSPolicyResponse( GSPolicyResponse_t *pPolicyResponse )
+{
+ if ( !BIsActive() )
+ return;
+
+ if ( SteamGameServer() && SteamGameServer()->BSecure() )
+ {
+ MsgAndLog( "VAC secure mode is activated.\n" );
+ }
+ else
+ {
+ MsgAndLog( "VAC secure mode disabled.\n" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSteam3Server::OnLogonSuccess( SteamServersConnected_t *pLogonSuccess )
+{
+ if ( !BIsActive() )
+ return;
+
+ if ( !m_bLogOnResult )
+ {
+ m_bLogOnResult = true;
+ }
+
+ if ( !BLanOnly() )
+ {
+ MsgAndLog( "Connection to Steam servers successful.\n" );
+ if ( SteamGameServer() )
+ {
+ uint32 ip = SteamGameServer()->GetPublicIP();
+ MsgAndLog( " Public IP is %d.%d.%d.%d.\n", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255 );
+ }
+ }
+
+ if ( SteamGameServer() )
+ {
+ m_SteamIDGS = SteamGameServer()->GetSteamID();
+ if ( m_SteamIDGS.BAnonGameServerAccount() )
+ {
+ MsgAndLog( "Assigned anonymous gameserver Steam ID %s.\n", m_SteamIDGS.Render() );
+ }
+ else if ( m_SteamIDGS.BPersistentGameServerAccount() )
+ {
+ MsgAndLog( "Assigned persistent gameserver Steam ID %s.\n", m_SteamIDGS.Render() );
+ }
+ else
+ {
+ WarningAndLog( "Assigned Steam ID %s, which is of an unexpected type!\n", m_SteamIDGS.Render() );
+ Assert( !"Unexpected steam ID type!" );
+ }
+ }
+ else
+ {
+ m_SteamIDGS = k_steamIDNotInitYetGS;
+ }
+
+ // send updated server details
+ // OnLogonSuccess() gets called each time we logon, so if we get dropped this gets called
+ // again and we get need to retell the AM our details
+ SendUpdatedServerDetails();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: callback on unable to connect to the steam3 backend
+// Input : eResult -
+//-----------------------------------------------------------------------------
+void CSteam3Server::OnLogonFailure( SteamServerConnectFailure_t *pLogonFailure )
+{
+ if ( !BIsActive() )
+ return;
+
+ //bool bRetrying = false;
+ if ( !m_bLogOnResult )
+ {
+ if ( pLogonFailure->m_eResult == k_EResultServiceUnavailable )
+ {
+ if ( !BLanOnly() )
+ {
+ MsgAndLog( "Connection to Steam servers successful (SU).\n" );
+ }
+ }
+ else
+ {
+ // we tried to be in secure mode but failed
+ // force into insecure mode
+ // eventually change this to set sv_lan as well
+ if ( !BLanOnly() )
+ {
+ WarningAndLog( "Could not establish connection to Steam servers. (Result = %d)\n", pLogonFailure->m_eResult );
+
+ // If this was a permanent failure, switch to anonymous
+ // TODO: Requires SDK update
+ /*if ( m_bWantsPersistentAccountLogon && ( pLogonFailure->m_eResult == k_EResultInvalidParam || pLogonFailure->m_eResult == k_EResultAccountNotFound ) )
+ {
+ WarningAndLog( "Invalid game server account token. Retrying Steam connection with anonymous logon\n" );
+
+ m_bWantsPersistentAccountLogon = false;
+ bRetrying = true;
+ SteamGameServer()->LogOnAnonymous();
+ }*/
+ }
+ }
+ }
+
+ m_bLogOnResult = true;
+ //m_bLogOnResult = !bRetrying;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : eResult -
+//-----------------------------------------------------------------------------
+void CSteam3Server::OnLoggedOff( SteamServersDisconnected_t *pLoggedOff )
+{
+ if ( !BLanOnly() )
+ {
+ WarningAndLog( "Connection to Steam servers lost. (Result = %d)\n", pLoggedOff->m_eResult );
+ }
+}
+
+void CSteam3Server::OnComputeNewPlayerCompatibilityResponse( ComputeNewPlayerCompatibilityResult_t *pCompatibilityResult )
+{
+ CBaseClient *client = ClientFindFromSteamID( pCompatibilityResult->m_SteamIDCandidate );
+ if ( !client )
+ return;
+ if ( sv_steamblockingcheck.GetInt() )
+ {
+ if ( sv_steamblockingcheck.GetInt() >= 2 )
+ {
+ if ( pCompatibilityResult->m_cClanPlayersThatDontLikeCandidate > 0 )
+ {
+ client->Disconnect( "Another player on this server ( member of owning clan ) does not want to play with this player." );
+ return;
+ }
+ }
+ if ( sv_steamblockingcheck.GetInt() >= 3 )
+ {
+ if ( pCompatibilityResult->m_cPlayersThatDontLikeCandidate > 0 )
+ {
+ client->Disconnect( "Another player on this server does not want to play with this player." );
+ return;
+ }
+ }
+ if ( sv_steamblockingcheck.GetInt() >= 4 )
+ {
+ if ( pCompatibilityResult->m_cPlayersThatCandidateDoesntLike > 0 )
+ {
+ client->Disconnect( "Existing player on this server is on this players block list." );
+ return;
+ }
+ }
+
+ if ( pCompatibilityResult->m_cClanPlayersThatDontLikeCandidate > 0 ||
+ pCompatibilityResult->m_cPlayersThatDontLikeCandidate > 0 ||
+ pCompatibilityResult->m_cPlayersThatCandidateDoesntLike > 0 )
+ {
+ MsgAndLog( "Player %s is blocked by %d players and %d clan members and has blocked %d players on server\n", client->GetClientName(),
+ pCompatibilityResult->m_cPlayersThatDontLikeCandidate,
+ pCompatibilityResult->m_cClanPlayersThatDontLikeCandidate,
+ pCompatibilityResult->m_cPlayersThatCandidateDoesntLike );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSteam3Server::OnValidateAuthTicketResponse( ValidateAuthTicketResponse_t *pValidateAuthTicketResponse )
+{
+ //Msg("Steam backend:Got approval for %x\n", pGSClientApprove->m_SteamID.ConvertToUint64() );
+ // We got the approval message from the back end.
+ // Note that if we dont get it, we default to approved anyway
+ // dont need to send anything back
+
+ if ( !BIsActive() )
+ return;
+
+ CBaseClient *client = ClientFindFromSteamID( pValidateAuthTicketResponse->m_SteamID );
+ if ( !client )
+ return;
+
+ if ( pValidateAuthTicketResponse->m_eAuthSessionResponse != k_EAuthSessionResponseOK )
+ {
+ OnValidateAuthTicketResponseHelper( client, pValidateAuthTicketResponse->m_eAuthSessionResponse );
+ return;
+ }
+
+ if ( Filter_IsUserBanned( client->GetNetworkID() ) )
+ {
+ sv.RejectConnection( client->GetNetChannel()->GetRemoteAddress(), client->GetClientChallenge(), "#GameUI_ServerRejectBanned" );
+ client->Disconnect( va( "STEAM UserID %s is banned", client->GetNetworkIDString() ) );
+ }
+ else if ( CheckForDuplicateSteamID( client ) )
+ {
+ client->Disconnect( "STEAM UserID %s is already\nin use on this server", client->GetNetworkIDString() );
+ }
+ else
+ {
+ char msg[ 512 ];
+ sprintf( msg, "\"%s<%i><%s><>\" STEAM USERID validated\n", client->GetClientName(), client->GetUserID(), client->GetNetworkIDString() );
+
+ DevMsg( "%s", msg );
+ g_Log.Printf( "%s", msg );
+
+ g_pServerPluginHandler->NetworkIDValidated( client->GetClientName(), client->GetNetworkIDString() );
+
+ // Tell IServerGameClients if its version is high enough.
+ if ( g_iServerGameClientsVersion >= 4 )
+ {
+ serverGameClients->NetworkIDValidated( client->GetClientName(), client->GetNetworkIDString() );
+ }
+ }
+
+ if ( sv_steamblockingcheck.GetInt() >= 1 )
+ {
+ SteamGameServer()->ComputeNewPlayerCompatibility( pValidateAuthTicketResponse->m_SteamID );
+ }
+ client->SetFullyAuthenticated();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper for the two places that deny a user connect
+// Input : steamID - id to kick
+// eDenyReason - reason
+// pchOptionalText - some kicks also have a string with them
+//-----------------------------------------------------------------------------
+void CSteam3Server::OnValidateAuthTicketResponseHelper( CBaseClient *cl, EAuthSessionResponse eAuthSessionResponse )
+{
+ INetChannel *netchan = cl->GetNetChannel();
+
+ // If the client is timing out, the Steam failure is probably related (e.g. game crashed). Let's just print that the client timed out.
+ if ( netchan && netchan->IsTimingOut() )
+ {
+ cl->Disconnect( CLIENTNAME_TIMED_OUT, cl->GetClientName() );
+ return;
+ }
+
+ // Emit a more detailed diagnostic.
+ WarningAndLog( "STEAMAUTH: Client %s received failure code %d\n", cl->GetClientName(), (int)eAuthSessionResponse );
+
+ switch ( eAuthSessionResponse )
+ {
+ case k_EAuthSessionResponseUserNotConnectedToSteam:
+ if ( !BLanOnly() )
+ cl->Disconnect( INVALID_STEAM_LOGON_NOT_CONNECTED );
+ break;
+ case k_EAuthSessionResponseLoggedInElseWhere:
+ if ( !BLanOnly() )
+ cl->Disconnect( INVALID_STEAM_LOGGED_IN_ELSEWHERE );
+ break;
+ case k_EAuthSessionResponseNoLicenseOrExpired:
+ cl->Disconnect( "This Steam account does not own this game. \nPlease login to the correct Steam account" );
+ break;
+ case k_EAuthSessionResponseVACBanned:
+ if ( !BLanOnly() )
+ cl->Disconnect( INVALID_STEAM_VACBANSTATE );
+ break;
+ case k_EAuthSessionResponseAuthTicketCanceled:
+ if ( !BLanOnly() )
+ cl->Disconnect( INVALID_STEAM_LOGON_TICKET_CANCELED );
+ break;
+ case k_EAuthSessionResponseAuthTicketInvalidAlreadyUsed:
+ case k_EAuthSessionResponseAuthTicketInvalid:
+ if ( !BLanOnly() )
+ cl->Disconnect( INVALID_STEAM_TICKET );
+ break;
+ case k_EAuthSessionResponseVACCheckTimedOut:
+ cl->Disconnect( "An issue with your computer is blocking the VAC system. You cannot play on secure servers.\n\nhttps://support.steampowered.com/kb_article.php?ref=2117-ILZV-2837" );
+ break;
+ default:
+ cl->Disconnect( "Client dropped by server" );
+ break;
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : steamIDFind -
+// Output : IClient
+//-----------------------------------------------------------------------------
+CBaseClient *CSteam3Server::ClientFindFromSteamID( CSteamID & steamIDFind )
+{
+ for ( int i=0 ; i< sv.GetClientCount() ; i++ )
+ {
+ CBaseClient *cl = (CBaseClient *)sv.GetClient( i );
+
+ // Not connected, no SteamID yet
+ if ( !cl->IsConnected() || cl->IsFakeClient() )
+ continue;
+
+ if ( cl->GetNetworkID().idtype != IDTYPE_STEAM )
+ continue;
+
+ USERID_t id = cl->GetNetworkID();
+ if (id.steamid == steamIDFind )
+ {
+ return cl;
+ }
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: tell Steam that a new user connected
+//-----------------------------------------------------------------------------
+bool CSteam3Server::NotifyClientConnect( CBaseClient *client, uint32 unUserID, netadr_t & adr, const void *pvCookie, uint32 ucbCookie )
+{
+ if ( !BIsActive() )
+ return true;
+
+ if ( !client || client->IsFakeClient() )
+ return false;
+
+ // Make sure their ticket is long enough
+ if ( ucbCookie <= sizeof(uint64) )
+ {
+ WarningAndLog("Client UserID %x connected with invalid ticket size %d\n", unUserID, ucbCookie );
+ return false;
+ }
+
+ // steamID is prepended to the ticket
+ CUtlBuffer buffer( pvCookie, ucbCookie, CUtlBuffer::READ_ONLY );
+ uint64 ulSteamID = buffer.GetInt64();
+
+ CSteamID steamID( ulSteamID );
+ if ( steamID.GetEUniverse() != SteamGameServer()->GetSteamID().GetEUniverse() )
+ {
+ WarningAndLog("Client %d %s connected to universe %d, but game server %s is running in universe %d\n", unUserID, steamID.Render(),
+ steamID.GetEUniverse(), SteamGameServer()->GetSteamID().Render(), SteamGameServer()->GetSteamID().GetEUniverse() );
+ return false;
+ }
+ if ( !steamID.IsValid() || !steamID.BIndividualAccount() )
+ {
+ WarningAndLog("Client %d connected from %s with invalid Steam ID %s\n", unUserID, adr.ToString(), steamID.Render() );
+ return false;
+ }
+
+ // skip the steamID
+ pvCookie = (uint8 *)pvCookie + sizeof( uint64 );
+ ucbCookie -= sizeof( uint64 );
+ EBeginAuthSessionResult eResult = SteamGameServer()->BeginAuthSession( pvCookie, ucbCookie, steamID );
+ switch ( eResult )
+ {
+ case k_EBeginAuthSessionResultOK:
+ //Msg("S3: BeginAuthSession request for %x was good.\n", steamID.ConvertToUint64( ) );
+ break;
+ case k_EBeginAuthSessionResultInvalidTicket:
+ WarningAndLog("S3: Client connected with invalid ticket: UserID: %x\n", unUserID );
+ return false;
+ case k_EBeginAuthSessionResultDuplicateRequest:
+ WarningAndLog("S3: Duplicate client connection: UserID: %x SteamID %x\n", unUserID, steamID.ConvertToUint64( ) );
+ return false;
+ case k_EBeginAuthSessionResultInvalidVersion:
+ WarningAndLog("S3: Client connected with invalid ticket ( old version ): UserID: %x\n", unUserID );
+ return false;
+ case k_EBeginAuthSessionResultGameMismatch:
+ // This error would be very useful to present to the client.
+ WarningAndLog("S3: Client connected with ticket for the wrong game: UserID: %x\n", unUserID );
+ return false;
+ case k_EBeginAuthSessionResultExpiredTicket:
+ WarningAndLog("S3: Client connected with expired ticket: UserID: %x\n", unUserID );
+ return false;
+ default:
+ WarningAndLog("S3: Client failed auth session for unknown reason. UserID: %x\n", unUserID );
+ return false;
+ }
+
+ // first checks ok, we know now the SteamID
+ client->SetSteamID( steamID );
+
+ SendUpdatedServerDetails();
+
+ return true;
+}
+
+bool CSteam3Server::NotifyLocalClientConnect( CBaseClient *client )
+{
+ CSteamID steamID;
+
+ if ( SteamGameServer() )
+ {
+ steamID = SteamGameServer()->CreateUnauthenticatedUserConnection();
+ }
+
+ client->SetSteamID( steamID );
+
+ SendUpdatedServerDetails();
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *client -
+//-----------------------------------------------------------------------------
+void CSteam3Server::NotifyClientDisconnect( CBaseClient *client )
+{
+ if ( !client || !BIsActive() || !client->IsConnected() || !client->m_SteamID.IsValid() )
+ return;
+
+ // Check if the client has a local (anonymous) steam account. This is the
+ // case for bots. Currently it's also the case for people who connect
+ // directly to the SourceTV port.
+ if ( client->m_SteamID.GetEAccountType() == k_EAccountTypeAnonGameServer )
+ {
+ SteamGameServer()->SendUserDisconnect( client->m_SteamID );
+
+ // Clear the steam ID, as it was a dummy one that should not be used again
+ client->m_SteamID = CSteamID();
+ }
+ else
+ {
+
+ // All bots should have an anonymous account ID
+ Assert( !client->IsFakeClient() );
+
+ USERID_t id = client->GetNetworkID();
+ if ( id.idtype != IDTYPE_STEAM )
+ return;
+
+ // Msg("S3: Sending client disconnect for %x\n", steamIDClient.ConvertToUint64( ) );
+ SteamGameServer()->EndAuthSession( client->m_SteamID );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSteam3Server::NotifyOfLevelChange()
+{
+ // we're changing levels, so we may not respond for a while
+ if ( m_bHasActivePlayers )
+ {
+ m_bHasActivePlayers = false;
+ SendUpdatedServerDetails();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSteam3Server::NotifyOfServerNameChange()
+{
+ SendUpdatedServerDetails();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSteam3Server::RunFrame()
+{
+ bool bHasPlayers = ( sv.GetNumClients() > 0 );
+
+ if ( m_bHasActivePlayers != bHasPlayers )
+ {
+ m_bHasActivePlayers = bHasPlayers;
+ SendUpdatedServerDetails();
+ }
+
+ static double s_fLastRunCallback = 0.0f;
+ double fCurtime = Plat_FloatTime();
+ if ( fCurtime - s_fLastRunCallback > 0.1f )
+ {
+ s_fLastRunCallback = fCurtime;
+ SteamGameServer_RunCallbacks();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: lets the steam3 servers know our full details
+// Input : bChangingLevels - true if we're going to heartbeat slowly for a while
+//-----------------------------------------------------------------------------
+void CSteam3Server::SendUpdatedServerDetails()
+{
+ if ( !BIsActive() || SteamGameServer() == NULL )
+ return;
+
+ // Fetch counts that include the dummy slots for SourceTV and reply.
+ int nNumClients = sv.GetNumClients();
+ int nMaxClients = sv.GetMaxClients();
+ int nFakeClients = sv.GetNumFakeClients();
+
+ // Now remove any dummy slots reserved for the Source TV or replay
+ // listeners. The fact that these are "players" should be a Source-specific
+ // implementation artifact, and this kludge --- I mean ELEGANT SOLUTION ---
+ // should not be propagated to the Steam layer. Steam should be able to report
+ // exactly what we give it to the master server, etc.
+ for ( int i = 0 ; i < sv.GetClientCount() ; ++i )
+ {
+ CBaseClient *cl = (CBaseClient *)sv.GetClient( i );
+ if ( !cl->IsConnected() )
+ continue;
+
+ bool bHideClient = false;
+ if ( cl->IsReplay() || cl->IsHLTV() )
+ {
+ Assert( cl->IsFakeClient() );
+ bHideClient = true;
+ }
+
+ if ( cl->IsFakeClient() && !cl->ShouldReportThisFakeClient() )
+ {
+ bHideClient = true;
+ }
+
+ if ( bHideClient )
+ {
+ --nNumClients;
+ --nMaxClients;
+ --nFakeClients;
+
+ // And make sure we don't have any local player authentication
+ // records within steam for this guy.
+ if ( cl->m_SteamID.IsValid() )
+ {
+ Assert( cl->m_SteamID.BAnonGameServerAccount() );
+ SteamGameServer()->SendUserDisconnect( cl->m_SteamID );
+ cl->m_SteamID = CSteamID();
+ }
+ }
+ }
+
+ // Apply convar to force reported max player count LAST
+ if ( sv_visiblemaxplayers.GetInt() > 0 && sv_visiblemaxplayers.GetInt() < nMaxClients )
+ nMaxClients = sv_visiblemaxplayers.GetInt();
+
+ SteamGameServer()->SetMaxPlayerCount( nMaxClients );
+ SteamGameServer()->SetBotPlayerCount( nFakeClients );
+ SteamGameServer()->SetPasswordProtected( sv.GetPassword() != NULL );
+ SteamGameServer()->SetRegion( sv_region.GetString() );
+ SteamGameServer()->SetServerName( sv.GetName() );
+ if ( hltv && hltv->IsTVRelay() )
+ {
+ // If we're a relay we can't use the local server data for these
+ SteamGameServer()->SetMapName( hltv->GetMapName() );
+ SteamGameServer()->SetMaxPlayerCount( hltv->GetMaxClients() );
+ SteamGameServer()->SetBotPlayerCount( 0 );
+ }
+ else
+ {
+ const char *pszMap = NULL;
+ if ( g_iServerGameDLLVersion >= 9 )
+ pszMap = serverGameDLL->GetServerBrowserMapOverride();
+ if ( pszMap == NULL || *pszMap == '\0' )
+ pszMap = sv.GetMapName();
+ SteamGameServer()->SetMapName( pszMap );
+ }
+ if ( hltv && hltv->IsActive() )
+ {
+ // This is also the case when we're a relay, in which case we never set a game port, so we'll only have a spectator port
+ SteamGameServer()->SetSpectatorPort( NET_GetUDPPort( NS_HLTV ) );
+ SteamGameServer()->SetSpectatorServerName( hltv->GetName() );
+ }
+ else
+ {
+ SteamGameServer()->SetSpectatorPort( 0 );
+ }
+
+ UpdateGroupSteamID( false );
+
+ // Form the game data to send
+
+ CUtlString sGameData;
+
+ // Start with whatever the game has
+ if ( g_iServerGameDLLVersion >= 9 )
+ sGameData = serverGameDLL->GetServerBrowserGameData();
+
+ // Add the value of our steam blocking flag
+ char rgchTag[32];
+ V_sprintf_safe( rgchTag, "steamblocking:%d", sv_steamblockingcheck.GetInt() );
+ if ( !sGameData.IsEmpty() )
+ {
+ sGameData.Append( "," );
+ }
+ sGameData.Append( rgchTag );
+
+ SteamGameServer()->SetGameData( sGameData );
+
+// Msg( "CSteam3Server::SendUpdatedServerDetails: nNumClients=%d, nMaxClients=%d, nFakeClients=%d:\n", nNumClients, nMaxClients, nFakeClients );
+// for ( int i = 0 ; i < sv.GetClientCount() ; ++i )
+// {
+// IClient *c = sv.GetClient( i );
+// Msg(" %d: %s, connected=%d, replay=%d, fake=%d\n", i, c->GetClientName(), c->IsConnected() ? 1 : 0, c->IsReplay() ? 1 : 0, c->IsFakeClient() ? 1 : 0 );
+// }
+}
+
+bool CSteam3Server::IsMasterServerUpdaterSharingGameSocket()
+{
+ return m_bMasterServerUpdaterSharingGameSocket;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Heartbeat_f()
+{
+
+ if( Steam3Server().SteamGameServer() )
+ {
+ Steam3Server().SteamGameServer()->ForceHeartbeat();
+ }
+}
+
+static ConCommand heartbeat( "heartbeat", Heartbeat_f, "Force heartbeat of master servers", 0 );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Select Steam gameserver account to login to
+//-----------------------------------------------------------------------------
+void sv_setsteamaccount_f( const CCommand &args )
+{
+ if ( Steam3Server().SteamGameServer() && Steam3Server().SteamGameServer()->BLoggedOn() )
+ {
+ Warning( "Warning: Game server already logged into steam. You need to use the sv_setsteamaccount command earlier.\n");
+ return;
+ }
+
+ if ( sv_lan.GetBool() )
+ {
+ Warning( "Warning: sv_setsteamaccount is not applicable in LAN mode.\n");
+ }
+
+ if ( args.ArgC() != 2 )
+ {
+ Warning( "Usage: sv_setsteamaccount <login_token>\n");
+ return;
+ }
+
+ Steam3Server().SetAccount( args[1] );
+}
+
+static ConCommand sv_setsteamaccount( "sv_setsteamaccount", sv_setsteamaccount_f, "token\nSet game server account token to use for logging in to a persistent game server account", 0 );
+
+
+static void sv_setsteamgroup_f( IConVar *pConVar, const char *pOldString, float flOldValue );
+
+ConVar sv_steamgroup( "sv_steamgroup", "", FCVAR_NOTIFY, "The ID of the steam group that this server belongs to. You can find your group's ID on the admin profile page in the steam community.", sv_setsteamgroup_f );
+
+
+void CSteam3Server::UpdateGroupSteamID( bool bForce )
+{
+ if ( sv_steamgroup.GetInt() == 0 && !bForce )
+ return;
+ uint unAccountID = Q_atoi( sv_steamgroup.GetString() );
+ m_SteamIDGroupForBlocking.Set( unAccountID, m_SteamIDGS.GetEUniverse(), k_EAccountTypeClan );
+ if ( SteamGameServer() )
+ SteamGameServer()->AssociateWithClan( m_SteamIDGroupForBlocking );
+}
+
+static void sv_setsteamgroup_f( IConVar *pConVar, const char *pOldString, float flOldValue )
+{
+ if ( sv_lan.GetBool() )
+ {
+ Warning( "Warning: sv_steamgroup is not applicable in LAN mode.\n");
+ }
+ Steam3Server().UpdateGroupSteamID( true );
+}
+
+static void sv_setsteamblockingcheck_f( IConVar *pConVar, const char *pOldString, float flOldValue )
+{
+ if ( sv_lan.GetBool() )
+ {
+ Warning( "Warning: sv_steamblockingcheck is not applicable in LAN mode.\n");
+ }
+}