339 lines
12 KiB
C++
339 lines
12 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Displays HUD element to show we are having connectivity trouble
|
|
//
|
|
//=====================================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "hud.h"
|
|
#include "hudelement.h"
|
|
#include "hud_element_helper.h"
|
|
#include "scaleformui/scaleformui.h"
|
|
#include "sfhud_autodisconnect.h"
|
|
#include "hud_macros.h"
|
|
#include "view.h"
|
|
|
|
#include "inetchannelinfo.h"
|
|
|
|
#include "c_cs_playerresource.h"
|
|
#include "c_cs_player.h"
|
|
#include "cs_gamerules.h"
|
|
#include "cs_client_gamestats.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
DECLARE_HUDELEMENT( SFHudAutodisconnect );
|
|
|
|
SFUI_BEGIN_GAME_API_DEF
|
|
SFUI_END_GAME_API_DEF( SFHudAutodisconnect, Autodisconnect );
|
|
|
|
SFHudAutodisconnect::SFHudAutodisconnect( const char *value ) : SFHudFlashInterface( value ),
|
|
m_sfuiControlBg( NULL ), m_sfuiControlTopLabel( NULL ), m_sfuiControlBottomLabel( NULL ), m_sfuiControlTimerLabel( NULL ), m_sfuiControlTimerIcon( NULL )
|
|
{
|
|
// This HUD element should never be hidden, so do not call SetHiddenBits
|
|
}
|
|
|
|
SFHudAutodisconnect::~SFHudAutodisconnect()
|
|
{
|
|
}
|
|
|
|
void SFHudAutodisconnect::ShowPanel( bool bShow )
|
|
{
|
|
if ( m_FlashAPI )
|
|
{
|
|
WITH_SLOT_LOCKED
|
|
{
|
|
if ( bShow )
|
|
m_pScaleformUI->Value_InvokeWithoutReturn( m_FlashAPI, "ShowPanel", NULL, 0 );
|
|
else
|
|
m_pScaleformUI->Value_InvokeWithoutReturn( m_FlashAPI, "HidePanel", NULL, 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
ConVar cl_connection_trouble_show( "cl_connection_trouble_show", "0", FCVAR_RELEASE, "Show connection trouble HUD warnings" );
|
|
DEVELOPMENT_ONLY_CONVAR( cl_connection_trouble_force, 0 );
|
|
static bool Helper_ShouldInformPlayerAboutConnectionLossChoke()
|
|
{
|
|
static ConVarRef cl_connection_trouble_info( "cl_connection_trouble_info" );
|
|
if ( !cl_connection_trouble_info.IsValid() )
|
|
return false;
|
|
|
|
// No error - nothing to show
|
|
if ( !cl_connection_trouble_info.GetString()[ 0 ] )
|
|
return false;
|
|
|
|
// Not a transient error - always show
|
|
if ( cl_connection_trouble_info.GetString()[ 0 ] != '@' )
|
|
return true;
|
|
|
|
// UI debugging feature
|
|
if ( cl_connection_trouble_force.GetInt() == 1 ) // Allow to always show for testing
|
|
return true;
|
|
|
|
// Don't care when playing demos or GOTV or on listen server
|
|
if ( g_bEngineIsHLTV || engine->IsClientLocalToActiveServer() )
|
|
return false;
|
|
|
|
// Not when loading
|
|
if ( engine->IsDrawingLoadingImage() )
|
|
return false;
|
|
|
|
// No local player - don't show
|
|
C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
|
|
if ( !pLocalPlayer )
|
|
return false;
|
|
|
|
// Show only when on active team
|
|
switch ( pLocalPlayer->GetTeamNumber() )
|
|
{
|
|
case TEAM_TERRORIST:
|
|
case TEAM_CT:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if ( !pLocalPlayer->IsAlive() )
|
|
return false;
|
|
|
|
// Need game rules
|
|
if ( !CSGameRules() )
|
|
return false;
|
|
|
|
// If the round is already won, then don't show warnings (things will start resetting and cause a spike)
|
|
if ( CSGameRules()->IsRoundOver() )
|
|
return false;
|
|
|
|
// Don't show warnings during freeze period or warmup (that's when players are joining)
|
|
if ( CSGameRules()->IsFreezePeriod() || CSGameRules()->IsWarmupPeriod() )
|
|
return false;
|
|
|
|
// Don't show warning for 3 seconds after freezetime ended (need to accumulate choke/loss)
|
|
if ( gpGlobals->curtime < CSGameRules()->GetRoundStartTime() + 3.0f )
|
|
return false;
|
|
|
|
// Don't show warnings if local player just spawned or respawned (AR / DM / etc.)
|
|
if ( pLocalPlayer->m_flLastSpawnTimeIndex > gpGlobals->curtime - 3.0f )
|
|
return false;
|
|
|
|
//
|
|
// OGS recording
|
|
//
|
|
static double s_dTimeLastOgsRecordWritten = 0;
|
|
static double s_dTimeTrackingPeakStarted = 0;
|
|
static float s_flTrackedPeakValue = 0;
|
|
static CSClientCsgoGameEventType_t s_chTypeTrackingPeak = k_CSClientCsgoGameEventType_ConnectionProblem_Generic;
|
|
double dTimeNow = Plat_FloatTime();
|
|
if ( dTimeNow > s_dTimeLastOgsRecordWritten + 60 )
|
|
{
|
|
bool bRecordNow = false;
|
|
CSClientCsgoGameEventType_t chType = k_CSClientCsgoGameEventType_ConnectionProblem_Generic;
|
|
float flCurrentValue = 0;
|
|
if ( INetChannelInfo *pChannelInfo = engine->GetNetChannelInfo() )
|
|
{
|
|
float flPacketLoss = pChannelInfo->GetAvgLoss( FLOW_INCOMING );
|
|
float flPacketChoke = pChannelInfo->GetAvgChoke( FLOW_INCOMING );
|
|
if ( flPacketLoss > 0 )
|
|
{
|
|
chType = k_CSClientCsgoGameEventType_ConnectionProblem_Loss;
|
|
flCurrentValue = flPacketLoss;
|
|
}
|
|
else if ( flPacketChoke > 0 )
|
|
{
|
|
chType = k_CSClientCsgoGameEventType_ConnectionProblem_Choke;
|
|
flCurrentValue = flPacketChoke;
|
|
}
|
|
}
|
|
|
|
if ( ( chType != s_chTypeTrackingPeak ) || ( dTimeNow > s_dTimeTrackingPeakStarted + 0.75 ) )
|
|
{ // Start tracking this problem type (or weren't tracking for some time?)
|
|
s_chTypeTrackingPeak = chType;
|
|
s_dTimeTrackingPeakStarted = dTimeNow;
|
|
s_flTrackedPeakValue = 0;
|
|
}
|
|
else if ( dTimeNow > s_dTimeTrackingPeakStarted + 0.25 )
|
|
{ // Have been tracking for 0.25 seconds: time to record
|
|
bRecordNow = true;
|
|
}
|
|
|
|
// Track max value over the period
|
|
s_flTrackedPeakValue = MAX( s_flTrackedPeakValue, flCurrentValue );
|
|
|
|
if ( bRecordNow )
|
|
{
|
|
s_dTimeLastOgsRecordWritten = dTimeNow;
|
|
uint64 ullData = int(s_flTrackedPeakValue * 1000);
|
|
g_CSClientGameStats.AddClientCSGOGameEvent( chType, pLocalPlayer->GetAbsOrigin(), pLocalPlayer->EyeAngles(), ullData );
|
|
DevMsg( "%s (peak at %.1f%%)\n", cl_connection_trouble_info.GetString(), s_flTrackedPeakValue*100 );
|
|
|
|
// Reset the data
|
|
s_chTypeTrackingPeak = k_CSClientCsgoGameEventType_ConnectionProblem_Generic;
|
|
s_flTrackedPeakValue = 0;
|
|
}
|
|
}
|
|
|
|
if ( !cl_connection_trouble_show.GetBool() ) // Allow to always hide transient errors (data collection only)
|
|
return false;
|
|
|
|
// Does not hide with the rest of the HUD
|
|
return true;
|
|
}
|
|
|
|
void SFHudAutodisconnect::ProcessInput( void )
|
|
{
|
|
static ConVarRef cl_connection_trouble_info( "cl_connection_trouble_info" );
|
|
if ( FlashAPIIsValid() && m_bActive && cl_connection_trouble_info.IsValid() && cl_connection_trouble_info.GetString()[0] )
|
|
{
|
|
//
|
|
// See cl_main.cpp CL_Move
|
|
// bool hasProblem = cl.m_NetChannel->IsTimingOut() && !demoplayer->IsPlayingBack() && cl.IsActive();
|
|
//
|
|
|
|
// Check for disconnect?
|
|
float TimeoutValue = -1.0f, Percentage = -1.0f;
|
|
|
|
if ( 1 == sscanf( cl_connection_trouble_info.GetString(), "disconnect(%f)", &TimeoutValue ) )
|
|
{
|
|
if ( TimeoutValue < 0 )
|
|
TimeoutValue = 0;
|
|
|
|
char cTimerStr[ 128 ];
|
|
V_snprintf( cTimerStr, sizeof(cTimerStr), "%02d:%02d", Floor2Int( TimeoutValue / 60.f ), ( Floor2Int(TimeoutValue) % 60 ) );
|
|
|
|
WITH_SLOT_LOCKED
|
|
{
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetText( m_sfuiControlTopLabel, "#SFUI_CONNWARNING_HEADER" );
|
|
if ( m_sfuiControlBottomLabel ) m_pScaleformUI->Value_SetText( m_sfuiControlBottomLabel, "#SFUI_CONNWARNING_BODY" );
|
|
if ( m_sfuiControlTimerLabel ) m_pScaleformUI->Value_SetText( m_sfuiControlTimerLabel, cTimerStr );
|
|
|
|
if ( m_sfuiControlBg ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBg, true );
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTopLabel, true );
|
|
if ( m_sfuiControlBottomLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBottomLabel, true );
|
|
if ( m_sfuiControlTimerLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerLabel, true );
|
|
if ( m_sfuiControlTimerIcon ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerIcon, true );
|
|
}
|
|
}
|
|
else if ( 2 == sscanf( cl_connection_trouble_info.GetString(), "@%f:loss(%f)", &TimeoutValue, &Percentage ) )
|
|
{
|
|
WITH_SLOT_LOCKED
|
|
{
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetText( m_sfuiControlTopLabel, "#SFUI_CONNWARNING_Bandwidth_PacketLoss" );
|
|
|
|
if ( m_sfuiControlBg ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBg, false );
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTopLabel, true );
|
|
if ( m_sfuiControlBottomLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBottomLabel, false );
|
|
if ( m_sfuiControlTimerLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerLabel, false );
|
|
if ( m_sfuiControlTimerIcon ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerIcon, false );
|
|
}
|
|
}
|
|
else if ( 2 == sscanf( cl_connection_trouble_info.GetString(), "@%f:choke(%f)", &TimeoutValue, &Percentage ) )
|
|
{
|
|
WITH_SLOT_LOCKED
|
|
{
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetText( m_sfuiControlTopLabel, "#SFUI_CONNWARNING_Bandwidth_Choking" );
|
|
|
|
if ( m_sfuiControlBg ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBg, false );
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTopLabel, true );
|
|
if ( m_sfuiControlBottomLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBottomLabel, false );
|
|
if ( m_sfuiControlTimerLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerLabel, false );
|
|
if ( m_sfuiControlTimerIcon ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerIcon, false );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WITH_SLOT_LOCKED
|
|
{
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetText( m_sfuiControlTopLabel, "#SFUI_CONNWARNING_HEADER" );
|
|
|
|
if ( m_sfuiControlBg ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBg, false );
|
|
if ( m_sfuiControlTopLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTopLabel, true );
|
|
if ( m_sfuiControlBottomLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlBottomLabel, false );
|
|
if ( m_sfuiControlTimerLabel ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerLabel, false );
|
|
if ( m_sfuiControlTimerIcon ) m_pScaleformUI->Value_SetVisible( m_sfuiControlTimerIcon, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SFHudAutodisconnect::FlashReady( void )
|
|
{
|
|
// Grab a pointer to the timeout text box
|
|
if ( SFVALUE panelRoot = m_pScaleformUI->Value_GetMember( m_FlashAPI, "Panel" ) )
|
|
{
|
|
if ( SFVALUE panelAnim = m_pScaleformUI->Value_GetMember( panelRoot, "PanelAnim" ) )
|
|
{
|
|
m_sfuiControlBg = m_pScaleformUI->Value_GetMember( panelAnim, "AutoDisconnectBg" );
|
|
|
|
if ( SFVALUE text = m_pScaleformUI->Value_GetMember( panelAnim, "Text" ) )
|
|
{
|
|
m_sfuiControlTopLabel = m_pScaleformUI->Value_GetMember( text, "AutoDisconnectText1" );
|
|
m_sfuiControlBottomLabel = m_pScaleformUI->Value_GetMember( text, "AutoDisconnectText2" );
|
|
m_sfuiControlTimerLabel = m_pScaleformUI->Value_GetMember( text, "TimerText" );
|
|
m_sfuiControlTimerIcon = m_pScaleformUI->Value_GetMember( text, "TimerIcon" );
|
|
|
|
SafeReleaseSFVALUE( text );
|
|
}
|
|
|
|
SafeReleaseSFVALUE( panelAnim );
|
|
}
|
|
|
|
SafeReleaseSFVALUE( panelRoot );
|
|
}
|
|
}
|
|
|
|
bool SFHudAutodisconnect::PreUnloadFlash( void )
|
|
{
|
|
SafeReleaseSFVALUE( m_sfuiControlBg );
|
|
SafeReleaseSFVALUE( m_sfuiControlTopLabel );
|
|
SafeReleaseSFVALUE( m_sfuiControlBottomLabel );
|
|
SafeReleaseSFVALUE( m_sfuiControlTimerLabel );
|
|
SafeReleaseSFVALUE( m_sfuiControlTimerIcon );
|
|
|
|
return true;
|
|
}
|
|
|
|
void SFHudAutodisconnect::LevelInit( void )
|
|
{
|
|
if ( !FlashAPIIsValid() )
|
|
{
|
|
// We assume we are offline-only for splitscreen multiplayer, so we allow this to be displayed in the split screen slot like
|
|
// the rest of the HUD is. If we support splitscreen online down the road, this should be moved into the fullscreen slot.
|
|
SFUI_REQUEST_ELEMENT( SF_SS_SLOT( GET_ACTIVE_SPLITSCREEN_SLOT() ), g_pScaleformUI, SFHudAutodisconnect, this, Autodisconnect );
|
|
}
|
|
|
|
// When initially loaded, hide any previous message
|
|
ShowPanel( false );
|
|
m_bActive = false;
|
|
}
|
|
|
|
void SFHudAutodisconnect::LevelShutdown( void )
|
|
{
|
|
if ( FlashAPIIsValid() )
|
|
{
|
|
RemoveFlashElement();
|
|
}
|
|
}
|
|
|
|
void SFHudAutodisconnect::Reset( void )
|
|
{
|
|
ShowPanel( false );
|
|
}
|
|
|
|
bool SFHudAutodisconnect::ShouldDraw( void )
|
|
{
|
|
return Helper_ShouldInformPlayerAboutConnectionLossChoke();
|
|
}
|
|
|
|
void SFHudAutodisconnect::SetActive( bool bActive )
|
|
{
|
|
if ( m_bActive != bActive )
|
|
{
|
|
ShowPanel( bActive );
|
|
}
|
|
|
|
CHudElement::SetActive( bActive );
|
|
}
|
|
|