summaryrefslogtreecommitdiff
path: root/game/client/abuse_report.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/abuse_report.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/abuse_report.cpp')
-rw-r--r--game/client/abuse_report.cpp720
1 files changed, 720 insertions, 0 deletions
diff --git a/game/client/abuse_report.cpp b/game/client/abuse_report.cpp
new file mode 100644
index 0000000..285a633
--- /dev/null
+++ b/game/client/abuse_report.cpp
@@ -0,0 +1,720 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Generic in-game abuse reporting
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "abuse_report.h"
+#include "abuse_report_ui.h"
+#include "filesystem.h"
+#include "imageutils.h"
+#include "econ/confirm_dialog.h"
+#include "econ/econ_notifications.h"
+
+inline bool IsLoggedOnToSteam()
+{
+ return steamapicontext != NULL && steamapicontext->SteamUser() != NULL && steamapicontext->SteamUser()->BLoggedOn();
+}
+
+const char CAbuseReportManager::k_rchScreenShotFilenameBase[] = "abuse_report";
+const char CAbuseReportManager::k_rchScreenShotFilename[] = "screenshots\\abuse_report.jpg";
+
+//-----------------------------------------------------------------------------
+class CEconNotification_AbuseReportReady : public CEconNotification
+{
+public:
+ CEconNotification_AbuseReportReady() : CEconNotification()
+ {
+ m_bHasTriggered = false;
+ m_bShowInGame = false;
+ }
+
+ ~CEconNotification_AbuseReportReady()
+ {
+ //if ( !m_bHasTriggered )
+ //{
+ // ReallyTrigger();
+ //}
+ }
+
+ virtual void MarkForDeletion()
+ {
+ m_bHasTriggered = true;
+
+ CEconNotification::MarkForDeletion();
+ }
+
+ virtual bool BShowInGameElements() const { return m_bShowInGame; }
+ virtual EType NotificationType() { return eType_Trigger; }
+ virtual void Trigger()
+ {
+ ReallyTrigger();
+ MarkForDeletion();
+ }
+
+ virtual const char *GetUnlocalizedHelpText()
+ {
+ return "#AbuseReport_Notification_Help";
+ }
+
+ static bool IsNotificationType( CEconNotification *pNotification ) { return dynamic_cast< CEconNotification_AbuseReportReady *>( pNotification ) != NULL; }
+ static bool IsInGameNotificationType( CEconNotification *pNotification )
+ {
+ CEconNotification_AbuseReportReady *n = dynamic_cast< CEconNotification_AbuseReportReady *>( pNotification );
+ return n != NULL && n->BShowInGameElements();
+ }
+
+ bool m_bShowInGame;
+
+private:
+
+ void ReallyTrigger()
+ {
+ Assert( !m_bHasTriggered );
+ m_bHasTriggered = true;
+ engine->ClientCmd_Unrestricted( "abuse_report_submit" );
+ }
+
+ bool m_bHasTriggered;
+};
+
+AbuseIncidentData_t::AbuseIncidentData_t()
+{
+ m_nScreenShotWaitFrames = 5;
+}
+
+AbuseIncidentData_t::~AbuseIncidentData_t()
+{
+}
+
+bool AbuseIncidentData_t::Poll()
+{
+ bool bReady = true;
+
+ // Poll player data
+ for ( int i = 0 ; i < m_vecPlayers.Count() ; ++i )
+ {
+
+ // Make sure sure Steam knows we want the Avatar
+ PlayerData_t *p = &m_vecPlayers[i];
+ if ( p->m_iSteamAvatarIndex < 0 )
+ {
+ if ( steamapicontext && steamapicontext->SteamUser() )
+ {
+
+ p->m_iSteamAvatarIndex = steamapicontext->SteamFriends()->GetLargeFriendAvatar( p->m_steamID );
+ if ( p->m_iSteamAvatarIndex < 0 )
+ {
+ bReady = false;
+ }
+ }
+ else
+ {
+ p->m_iSteamAvatarIndex = 0;
+ }
+ }
+ }
+
+ // Screenshot ready?
+ if ( !m_bitmapScreenshot.IsValid() && m_nScreenShotWaitFrames > 0 )
+ {
+ --m_nScreenShotWaitFrames;
+
+ // Just load the whole file into a memory buffer
+ char szFullPath[ MAX_PATH ] = "";
+ if ( !g_pFullFileSystem->RelativePathToFullPath( CAbuseReportManager::k_rchScreenShotFilename, NULL, szFullPath, ARRAYSIZE(szFullPath) ) )
+ {
+ Assert( false ); // ???
+ }
+
+ // Load it
+
+ if ( g_pFullFileSystem->FileExists( szFullPath ) )
+ {
+
+ // Load the screenshot into a local buffer
+ if ( !g_pFullFileSystem->ReadFile( CAbuseReportManager::k_rchScreenShotFilename, NULL, m_bufScreenshotFileData ) )
+ {
+ Warning( "Failed to read back %s\n", CAbuseReportManager::k_rchScreenShotFilename );
+ m_nScreenShotWaitFrames = 0;
+ }
+ else
+ {
+ ConversionErrorType nErrorCode = ImgUtl_LoadBitmap( szFullPath, m_bitmapScreenshot );
+ if ( nErrorCode != CE_SUCCESS )
+ {
+ Warning( "Abuse report screenshot %s failed to load with error code %d\n", CAbuseReportManager::k_rchScreenShotFilename, nErrorCode );
+ Assert( nErrorCode == CE_SUCCESS );
+ m_nScreenShotWaitFrames = 0;
+ }
+ else
+ {
+ // !KLUDGE! Resize to power of two dimensions, since VGUI doesn't like odd sizes
+ ImgUtl_ResizeBitmap( m_bitmapScreenshot, 1024, 1024, &m_bitmapScreenshot );
+ }
+ }
+ g_pFullFileSystem->RemoveFile( CAbuseReportManager::k_rchScreenShotFilename );
+ }
+ }
+
+ return bReady;
+}
+
+CAbuseReportManager *g_AbuseReportMgr;
+
+CAbuseReportManager::CAbuseReportManager()
+{
+ m_pIncidentData = NULL;
+ m_bTestReport = false;
+ m_eIncidentDataStatus = k_EIncidentDataStatus_None;
+ m_bReportUIPending = false;
+
+ // We're the singleton --- set global pointer
+ Assert( g_AbuseReportMgr == NULL );
+ g_AbuseReportMgr = this;
+ m_timeLastReportReadyNotification = 0.0;
+ m_adrCurrentServer.Clear();
+}
+
+CAbuseReportManager::~CAbuseReportManager()
+{
+ Assert( m_pIncidentData == NULL );
+}
+
+char const *CAbuseReportManager::Name()
+{
+ return "AbuseRepotManager";
+}
+
+bool CAbuseReportManager::Init()
+{
+ // Clean out any temporary files
+ Assert( m_pIncidentData == NULL );
+ DestroyIncidentData();
+
+ ListenForGameEvent( "teamplay_round_win" );
+ ListenForGameEvent( "tf_game_over" );
+ ListenForGameEvent( "player_death" );
+ ListenForGameEvent( "server_spawn" );
+
+ return true;
+}
+
+void CAbuseReportManager::LevelShutdownPreEntity()
+{
+
+ // Don't keep the dialog open across a level transition. Don't discard their
+ // report data, but let's kill the dialog
+ if ( g_AbuseReportDlg.Get() != NULL )
+ {
+ Warning( "Abuse report dialog open during level shutdown. Closing it.\n" );
+ g_AbuseReportDlg.Get()->Close();
+ }
+
+ // And clear the 'pending' flag
+ m_bReportUIPending = false;
+}
+
+void CAbuseReportManager::FireGameEvent( IGameEvent *event )
+{
+ //C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+
+ const char *eventname = event->GetName();
+
+ if ( !eventname || !eventname[0] )
+ return;
+
+ if (
+ !Q_strcmp( "teamplay_round_win", eventname )
+ || !Q_strcmp( "tf_game_over", eventname )
+ ) {
+ // Periodically remind them that they have a report ready to file
+ CheckCreateReportReadyNotification( 60.0 * 5.0, true, 10.0f );
+ }
+ else if ( !Q_strcmp( "player_death", eventname ) )
+ {
+ // In some maps, the round just never ends.
+ // So make sure we do remind them every now and then about this.
+ // Just not too often
+ CheckCreateReportReadyNotification( 60.0 * 20.0, true, 5.0f );
+ }
+ else if ( !Q_strcmp( "server_spawn", eventname ) )
+ {
+ m_adrCurrentServer.Clear();
+ m_adrCurrentServer.SetFromString( event->GetString( "address", "" ), false );
+ m_adrCurrentServer.SetPort( event->GetInt( "port", 0 ) );
+
+ m_steamIDCurrentServer = CSteamID();
+ if ( steamapicontext && steamapicontext->SteamUser() && GetUniverse() != k_EUniverseInvalid )
+ {
+ m_steamIDCurrentServer.SetFromString( event->GetString( "steamid", "" ), GetUniverse() );
+ }
+ }
+}
+
+void CAbuseReportManager::Shutdown()
+{
+ // Close the dialog, if any
+ LevelShutdownPreEntity();
+
+ DestroyIncidentData();
+
+ // Clear global pointer
+ Assert( g_AbuseReportMgr == this );
+ if ( g_AbuseReportMgr == this )
+ {
+ g_AbuseReportMgr = NULL;
+ }
+}
+
+void CAbuseReportManager::Update( float frametime )
+{
+
+ // if a dialog is already displayed, make sure we don't try to activate another
+ if ( g_AbuseReportDlg.Get() != NULL )
+ {
+ m_bReportUIPending = false;
+ }
+
+ // Poll report data, if any
+ if ( m_pIncidentData != NULL )
+ {
+ if ( m_eIncidentDataStatus == k_EIncidentDataStatus_Preparing )
+ {
+ if ( m_pIncidentData->Poll() )
+ {
+ m_eIncidentDataStatus = k_EIncidentDataStatus_Ready;
+ CheckCreateReportReadyNotification( 1.0f, true, 7.0f );
+ }
+ }
+ else
+ {
+ Assert( m_eIncidentDataStatus == k_EIncidentDataStatus_Ready );
+ }
+
+ if ( m_eIncidentDataStatus == k_EIncidentDataStatus_Ready && m_bReportUIPending )
+ {
+ m_bReportUIPending = false;
+ ActivateSubmitReportUI();
+ }
+ }
+ else
+ {
+ m_bReportUIPending = false;
+ }
+
+ // Re-create notification constantly in the menu.
+ // While in game, we will only popup notifications
+ // periodically at round end or player death
+ CheckCreateReportReadyNotification( 10.0, false, 999.0f );
+}
+
+void CAbuseReportManager::SubmitReportUIRequested()
+{
+ if ( g_AbuseReportDlg.Get() != NULL )
+ {
+ Assert( g_AbuseReportDlg.Get() == NULL );
+ return;
+ }
+
+ // If no report data already, then create some
+ if ( m_pIncidentData == NULL )
+ {
+ QueueReport();
+ if ( m_pIncidentData == NULL )
+ {
+ // Failed
+ return;
+ }
+ }
+
+ // Set flag to bring up the reporting UI at earliest opportunity,
+ // once all data has been fetched asynchronously
+ m_bReportUIPending = true;
+}
+
+bool CAbuseReportManager::CreateAndPopulateIncident()
+{
+ Assert( m_pIncidentData == NULL );
+
+ // by default, just create the base class version
+ m_pIncidentData = new AbuseIncidentData_t;
+
+ // And populate it
+ return PopulateIncident();
+}
+
+bool CAbuseReportManager::PopulateIncident()
+{
+ if ( m_pIncidentData == NULL )
+ {
+ Assert( m_pIncidentData );
+ return false;
+ }
+
+ // Queue a screenshot
+ CUtlString cmd;
+ cmd.Format( "__screenshot_internal \"%s\"", k_rchScreenShotFilenameBase );
+ engine->ClientCmd_Unrestricted( cmd );
+
+ // Set status as preparing
+ m_eIncidentDataStatus = k_EIncidentDataStatus_Preparing;
+
+ m_pIncidentData->m_bCanReportGameServer = false;
+
+ m_pIncidentData->m_adrGameServer.Clear();
+ if (
+ m_adrCurrentServer.IsValid()
+ && !m_adrCurrentServer.IsLocalhost()
+ && m_steamIDCurrentServer.IsValid()
+ && ( !m_adrCurrentServer.IsReservedAdr() || m_steamIDCurrentServer.GetEUniverse() != k_EUniversePublic )
+ )
+ {
+ m_pIncidentData->m_adrGameServer = m_adrCurrentServer;
+ m_pIncidentData->m_steamIDGameServer = m_steamIDCurrentServer;
+ m_pIncidentData->m_bCanReportGameServer = true;
+ }
+
+ m_pIncidentData->m_matWorldToClip = engine->WorldToScreenMatrix();
+
+ // Add in players
+ for (int i = 1 ; i <= gpGlobals->maxClients ; ++i )
+ {
+ CBasePlayer *player = UTIL_PlayerByIndex( i );
+
+ #ifndef _DEBUG
+ // Skip local players
+ if ( player != NULL && player->IsLocalPlayer() )
+ {
+ continue;
+ }
+ #endif
+
+ // Get player info from the engine. This works even if they haven't spawned yet.
+ player_info_t pi;
+ if ( !engine->GetPlayerInfo( i, &pi ) )
+ {
+ continue;
+ }
+
+ if ( pi.fakeplayer )
+ {
+ continue;
+ }
+ if ( pi.friendsID == 0 )
+ {
+ continue;
+ }
+ CSteamID steamID( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
+ if ( !steamID.IsValid() )
+ {
+ Assert( steamID.IsValid() );
+ continue;
+ }
+
+ int arrayIndex = m_pIncidentData->m_vecPlayers.AddToTail();
+ AbuseIncidentData_t::PlayerData_t *p = &m_pIncidentData->m_vecPlayers[ arrayIndex ];
+
+ p->m_iClientIndex = i;
+ p->m_steamID = steamID;
+ p->m_sPersona = pi.name;
+ p->m_bHasEntity = false;
+ p->m_bRenderBoundsValid = false;
+ p->m_screenBoundsMin.x = p->m_screenBoundsMin.y = 1.0f;
+ p->m_screenBoundsMax.x = p->m_screenBoundsMax.y = 0.0f;
+
+ if ( player==NULL )
+ {
+ continue;
+ }
+
+ p->m_bHasEntity = true;
+ player->GetRenderBounds( p->m_vecRenderBoundsMin, p->m_vecRenderBoundsMax );
+ p->m_matModelToWorld.CopyFrom3x4( player->RenderableToWorldTransform() );
+ MatrixMultiply( m_pIncidentData->m_matWorldToClip, p->m_matModelToWorld, p->m_matModelToClip );
+
+ // Gather up screen extents
+ p->m_bRenderBoundsValid = false;
+ for ( int j = 0 ; j < 8 ; ++j )
+ {
+
+ // Get corner point in model space
+ Vector4D modelCorner(
+ ( j & 1 ) ? p->m_vecRenderBoundsMax.x : p->m_vecRenderBoundsMin.x,
+ ( j & 2 ) ? p->m_vecRenderBoundsMax.y : p->m_vecRenderBoundsMin.y,
+ ( j & 4 ) ? p->m_vecRenderBoundsMax.z : p->m_vecRenderBoundsMin.z,
+ 1.0f
+ );
+
+ // Transform to clip space
+ Vector4D clipCorner;
+ Vector4DMultiply( p->m_matModelToClip, modelCorner, clipCorner );
+
+ //Msg( "%6.3f, %6.3f, %6.3f, %6.3f\n", clipCorner[0], clipCorner[1], clipCorner[2], clipCorner[3] );
+
+ // If all points behind near clip plane, don't try to
+ // figure out screen space bounds
+ if ( clipCorner[3] > .1f )
+ {
+ p->m_bRenderBoundsValid = true;
+ }
+
+ // Push w forward to "near clip plane"
+ float w = MAX( clipCorner[3], .1f );
+
+ // Divide by w to project, and convert normalized device coordinates
+ // where the view volume is (-1...1), to normalized screen coords, where
+ // they are from 0...1
+ float x = ( clipCorner[0] / w + 1.0f ) / 2.0f;
+ float y = ( -clipCorner[1] / w + 1.0f ) / 2.0f;
+ p->m_screenBoundsMin.x = MIN( p->m_screenBoundsMin.x, x );
+ p->m_screenBoundsMax.x = MAX( p->m_screenBoundsMax.x, x );
+ p->m_screenBoundsMin.y = MIN( p->m_screenBoundsMin.y, y );
+ p->m_screenBoundsMax.y = MAX( p->m_screenBoundsMax.y, y );
+ }
+
+ // Clip projected rect to the screen
+ if ( p->m_bRenderBoundsValid )
+ {
+ p->m_screenBoundsMin.x = MAX( p->m_screenBoundsMin.x, 0.0f );
+ p->m_screenBoundsMax.x = MIN( p->m_screenBoundsMax.x, 1.0f );
+ p->m_screenBoundsMin.y = MAX( p->m_screenBoundsMin.y, 0.0f );
+ p->m_screenBoundsMax.y = MIN( p->m_screenBoundsMax.y, 1.0f );
+
+ p->m_bRenderBoundsValid =
+ p->m_screenBoundsMin.x + .01f < p->m_screenBoundsMax.x
+ && p->m_screenBoundsMin.y + .01f < p->m_screenBoundsMax.y;
+ }
+
+ // Sanity check that we agree on what their steam ID is!
+ if ( player->GetSteamID( &steamID ) )
+ {
+ Assert( p->m_steamID == steamID );
+ }
+ }
+
+ // Test harness: add in a handful of fake players
+ #ifdef _DEBUG
+ if ( m_bTestReport )
+ {
+ int arrayIndex = m_pIncidentData->m_vecPlayers.AddToTail();
+ AbuseIncidentData_t::PlayerData_t *p = &m_pIncidentData->m_vecPlayers[ arrayIndex ];
+
+ p->m_iClientIndex = -1;
+ p->m_sPersona = "Lippencott";
+ p->m_steamID.SetFromUint64( 148618791998333672 );
+
+ arrayIndex = m_pIncidentData->m_vecPlayers.AddToTail();
+ p = &m_pIncidentData->m_vecPlayers[ arrayIndex ];
+
+ p->m_iClientIndex = -1;
+ p->m_sPersona = "EricS";
+ p->m_steamID.SetFromUint64( 148618791998195668 );
+
+ arrayIndex = m_pIncidentData->m_vecPlayers.AddToTail();
+ p = &m_pIncidentData->m_vecPlayers[ arrayIndex ];
+
+ p->m_iClientIndex = -1;
+ p->m_sPersona = "Sarenya";
+ p->m_steamID.SetFromUint64( 148618791998429832 );
+
+ arrayIndex = m_pIncidentData->m_vecPlayers.AddToTail();
+ p = &m_pIncidentData->m_vecPlayers[ arrayIndex ];
+
+
+ p->m_iClientIndex = -1;
+ p->m_sPersona = "fletch";
+ p->m_steamID.SetFromUint64( 148618791998436114 );
+ {
+ AbuseIncidentData_t::PlayerImage_t img;
+ img.m_eType = AbuseIncidentData_t::k_PlayerImageType_UGC;
+ img.m_hUGCHandle = 6978249415967519;
+ p->m_vecImages.AddToTail( img );
+ }
+
+ if ( !m_pIncidentData->m_bCanReportGameServer)
+ {
+ m_pIncidentData->m_adrGameServer.SetFromString( "123.45.67.89:27015", false );
+ m_pIncidentData->m_steamIDGameServer = CSteamID( 12345, 0, GetUniverse(), k_EAccountTypeAnonGameServer );
+ m_pIncidentData->m_bCanReportGameServer = true;
+ }
+ }
+ #endif
+
+ // Make sure there is at least one other person we could file a report against!
+ if ( m_pIncidentData->m_vecPlayers.Count() < 1 )
+ {
+ Warning( "No players to accuse of abuse, cannot file report\n" );
+ return false;
+ }
+
+ return true;
+}
+
+void CAbuseReportManager::DestroyIncidentData()
+{
+ if ( m_pIncidentData != NULL )
+ {
+ delete m_pIncidentData;
+ m_pIncidentData = NULL;
+ }
+ m_eIncidentDataStatus = k_EIncidentDataStatus_None;
+
+ // Get rid of any existing screenshot file, both locally
+ // and in the cloud. We don't want this to count against
+ // our quota
+ if ( steamapicontext && steamapicontext->SteamRemoteStorage() && steamapicontext->SteamRemoteStorage()->FileExists( k_rchScreenShotFilename ) )
+ {
+ steamapicontext->SteamRemoteStorage()->FileDelete( k_rchScreenShotFilename );
+ }
+
+ if ( g_pFullFileSystem->FileExists( k_rchScreenShotFilename ) ) // !KLUDGE! To prevent warning if the file doesn't exist!
+ {
+ g_pFullFileSystem->RemoveFile( k_rchScreenShotFilename );
+ }
+
+ m_timeLastReportReadyNotification = 0.0;
+
+ // Make sure we don't have any notifications queued
+ NotificationQueue_Remove( &CEconNotification_AbuseReportReady::IsNotificationType );
+}
+
+void CAbuseReportManager::QueueReport()
+{
+ // Dialog is already active?
+ if ( g_AbuseReportDlg.Get() != NULL )
+ {
+ Warning( "Cannot capture another incident report. Submission dialog is active.\n" );
+ return;
+ }
+
+ // Destroy any existing data
+ DestroyIncidentData();
+
+ // Make sure we're logged on to Steam
+ if ( !IsLoggedOnToSteam() )
+ {
+ g_AbuseReportMgr->ShowNoSteamErrorMessage();
+ return;
+ }
+
+ if ( CreateAndPopulateIncident() )
+ {
+ Msg( "Captured data for abuse report.\n");
+ }
+ else
+ {
+ Warning( "Failed to captured data for abuse report.\n");
+ DestroyIncidentData();
+ }
+}
+
+void CAbuseReportManager::ShowNoSteamErrorMessage()
+{
+ ShowMessageBox( "#AbuseReport_NoSteamTitle", "#AbuseReport_NoSteamMessage", "#GameUI_OK" );
+}
+
+void CAbuseReportManager::CheckCreateReportReadyNotification( float flMinSecondsSinceLastNotification, bool bInGame, float flLifetime )
+{
+ // We have to have some data ready
+ if ( m_pIncidentData == NULL || m_eIncidentDataStatus != k_EIncidentDataStatus_Ready )
+ {
+ return;
+ }
+
+ // Don't pester them if they are already trying to do something about it
+ if ( g_AbuseReportDlg.Get() != NULL || m_bReportUIPending )
+ {
+ return;
+ }
+
+ // Already notified them too recently?
+ if ( m_timeLastReportReadyNotification != 0.0 && Plat_FloatTime() < m_timeLastReportReadyNotification + flMinSecondsSinceLastNotification )
+ {
+ return;
+ }
+
+ // Already a notification in the queue?
+ if ( bInGame )
+ {
+ if ( NotificationQueue_Count( &CEconNotification_AbuseReportReady::IsInGameNotificationType ) > 0 )
+ {
+ return;
+ }
+ }
+ else
+ {
+ if ( NotificationQueue_Count( &CEconNotification_AbuseReportReady::IsNotificationType ) > 0 )
+ {
+ return;
+ }
+ }
+
+ CreateReportReadyNotification( bInGame, flLifetime );
+}
+
+void CAbuseReportManager::CreateReportReadyNotification( bool bInGame, float flLifetime )
+{
+ NotificationQueue_Remove( &CEconNotification_AbuseReportReady::IsNotificationType );
+ CEconNotification_AbuseReportReady *pNotification = new CEconNotification_AbuseReportReady();
+ pNotification->SetText( "AbuseReport_Notification" );
+ pNotification->SetLifetime( flLifetime );
+ pNotification->m_bShowInGame = bInGame;
+ NotificationQueue_Add( pNotification );
+
+ m_timeLastReportReadyNotification = Plat_FloatTime();
+}
+
+CON_COMMAND_F( abuse_report_queue, "Capture data for abuse report and queue for submission. Use abose_report_submit to activate UI to submit the report", FCVAR_DONTRECORD )
+{
+ if ( !g_AbuseReportMgr )
+ {
+ Warning( "abuse_report_queue: No abuse report manager, cannot create report.\n" );
+ return;
+ }
+
+ g_AbuseReportMgr->QueueReport();
+}
+
+CON_COMMAND_F( abuse_report_submit, "Activate UI to submit queued report. Use abuse_report_queue to capture data for the report the report", FCVAR_DONTRECORD )
+{
+ if ( !g_AbuseReportMgr )
+ {
+ Warning( "abuse_report_submit: No abuse report manager, cannot submit report.\n" );
+ return;
+ }
+
+ // Make sure we're logged on to Steam
+ if ( !IsLoggedOnToSteam() )
+ {
+ g_AbuseReportMgr->ShowNoSteamErrorMessage();
+ return;
+ }
+
+ if ( g_AbuseReportDlg.Get() != NULL )
+ {
+ // Dialog is already active
+ return;
+ }
+ g_AbuseReportMgr->SubmitReportUIRequested();
+}
+
+// Test harness
+#ifdef _DEBUG
+
+CON_COMMAND_F( abuse_report_test, "Make a test abuse incident and activate UI", FCVAR_DONTRECORD )
+{
+ if ( !g_AbuseReportMgr )
+ {
+ Assert( g_AbuseReportMgr );
+ return;
+ }
+ g_AbuseReportMgr->m_bTestReport = true;
+ g_AbuseReportMgr->QueueReport();
+ g_AbuseReportMgr->m_bTestReport = false;
+ engine->ClientCmd_Unrestricted( "abuse_report_submit" );
+}
+
+#endif