Files
2022-10-26 12:25:11 +08:00

255 lines
6.1 KiB
C++

#include "StdAfx.h"
#include "HeartbeatChecker.h"
#include "HeartbeatManager.h"
HeartbeatChecker::HeartbeatChecker()
{
}
HeartbeatChecker::~HeartbeatChecker()
{
}
VOID HeartbeatChecker::Init()
{
m_dwStatus = this->CLEAR_FIELD;
m_dwSignaledCount = 0;
m_dwLifeTimeout = ULONG_MAX;
m_dwRound = 0;
m_dwTimeout = 0;
m_dwRoundCount = 0;
m_dwAccumulatedCount = 0;
m_bFirstStart = TRUE;
// m_dwPauseTime = 0;
memset( m_pWarningFields, 0, sizeof(m_pWarningFields) );
}
// TRUE일 경우 측정 시간 보정 및 컨트롤
VOID HeartbeatChecker::SetEnable( BOOL bSet )
{
// 작동 중지 명령중...
if( !bSet )
{
//현재 작동중이었다면
if( this->ENABLED & m_dwStatus )
{
Stop();
}
}
// 검사 시작/재개 명령
else
{
//이미 시작중이면
if( this->ENABLED & m_dwStatus )
return;
Start();
}
}
VOID HeartbeatChecker::Start()
{
//최초 시작이면
if( m_bFirstStart )
{
DWORD dwCurTick = GetTickCount();
DWORD dwHeartbeatTick = PROCESS_HEARTBEAT_VALUATION::GetHeartbeatTick();
m_dwLifeTimeout
= dwCurTick + (dwHeartbeatTick * PROCESS_HEARTBEAT_VALUATION::GetRecordInfo()->dwMaxRound);
m_dwTimeout = dwCurTick + dwHeartbeatTick;
m_bFirstStart = FALSE;
}
//Pause 후 Resume이면
else
{
//ASSERT(m_dwPauseTime);
DWORD dwNowTickCount = GetTickCount();
//if( dwNowTickCount < m_dwPauseTime )
//{
// ASSERT(FALSE);
// return;
//}
//Pause 된 시간만큼 추가.
//DWORD dwDiff = dwNowTickCount - m_dwPauseTime;
//MessageOut( eCRITICAL_LOG, "[RESUME]: Now: %u , TO: %u ", dwNowTickCount, m_dwTimeout );
if( dwNowTickCount > m_dwTimeout )
{
m_dwLifeTimeout += ( dwNowTickCount - m_dwTimeout );
m_dwTimeout = dwNowTickCount;
}
}
m_dwStatus |= this->ENABLED;
}
VOID HeartbeatChecker::Stop()
{
m_dwStatus &= ~this->ENABLED;
// m_dwPauseTime = GetTickCount();
//MessageOut( eCRITICAL_LOG, "[PAUSE]: %u",GetTickCount() );
}
// NR : (Next Round) = n%MR
// CR : (Current Round)
// CT : (Current Tick)
// MR : (Max Round) = n % info.MaxRound
// DR : (Diff Round)
// RC : (Round Count)
// TC : (Total Count)
DWORD HeartbeatChecker::GetNextRound( DWORD dwCurTick, DWORD dwTimeout )
{
if( dwCurTick >= dwTimeout )
{
DWORD dwDiff = dwCurTick - dwTimeout;
DWORD dwPeriod = PROCESS_HEARTBEAT_VALUATION::GetHeartbeatTick();
DWORD dwNR = (DWORD)(dwDiff / dwPeriod) + m_dwRound + 1;
return dwNR;
}
// 동일 Round
return m_dwRound;
}
BOOL HeartbeatChecker::CheckOverflow()
{
// Heartbeat 신호를 받았을 때, 해당 라운드에 대한 검사이며, 동일 라운드 내에 복수개
// 신호가 들어왔을 때만 검사하는 루틴
DWORD dwOverflowRC = m_pWarningFields[this->OVERFLOW_SAMEROUND];
dwOverflowRC = (dwOverflowRC+1) > 0xFF ? 0xFF : dwOverflowRC+1;
m_pWarningFields[this->OVERFLOW_SAMEROUND] = (BYTE)dwOverflowRC;
return TRUE;
}
BOOL HeartbeatChecker::CheckValuationTime( User* pUser )
{
DWORD dwMR = PROCESS_HEARTBEAT_VALUATION::GetRecordInfo()->dwMaxRound;
DWORD rdwTC = m_dwAccumulatedCount;
RC::eCONNECTION_RESULT eRet = RC::RC_DISCONNECT_UNKNOWN;
BOOL bValuation = TRUE;
//---------------------------------------------------------------------------
// <1> 평가 주기동안 들어온 Heartbeat의 총 개수 평가 (최대치 평가)
// '2' 값은 hard-coding 일단 좀더 데이터가 요구된다.
// dwMR보다 큰 값이면 일단 SpeedHack을 이용한 고속 플레이가 될 것이다.
//if( rdwTC > dwMR + 2 )
if( rdwTC > dwMR + ((dwMR>>2) + 4) )
{
bValuation = FALSE;
eRet = RC::RC_DISCONNECT_SPEED_HACK;
}
//---------------------------------------------------------------------------
// <2> 평가 주기동안 들어온 Heartbeat의 총 개수 평가 (최소치 평가)
// 0 인 경우를 제외하고, (이건 사실 여기로 들어올 리 없다)
// '.6f'는 hard-coding 일단 좀 더 데이터가 요구된다.
if( bValuation && rdwTC == 0 )
{
bValuation = FALSE;
eRet = RC::RC_DISCONNECT_CRACKED_CLIENT;
}
if( bValuation )
{
DWORD dwMR_MIN = (DWORD)((dwMR >> 2) + 1);
if( !(rdwTC > dwMR_MIN) )
{
bValuation = FALSE;
eRet = RC::RC_DISCONNECT_IRREGULAR_CLIENT_CYCLE_TICK;
}
}
//---------------------------------------------------------------------------
// <3> 평가 주기동안 ArmorKit Heartbeat가 설정되었을 시 ArmorKit 활성 여부 평가
if( bValuation
&& (m_dwStatus&this->ARMORKIT_ENABLED)
&& !m_pWarningFields[this->ARMORKIT_SIGNALED] )
{
bValuation = FALSE;
eRet = RC::RC_DISCONNECT_ARMORKIT_NOT_SERVICED;
}
if( !bValuation && pUser )
{
m_dwLifeTimeout = ULONG_MAX;
// 이 루틴은 클라이언트 정지 상태가 해결되면 제공하기로 한다.
pUser->SetDisconnectCode( eRet );
pUser->DisconnectUser( TRUE );
}
return bValuation;
}
VOID HeartbeatChecker::Signal( User* pUser, DWORD dwCurTick )
{
//중지면 자동 Resume
if( !(this->ENABLED & m_dwStatus) )
Start();
// 작동 여부 체크 루틴 삽입할 것
// 일단은 작동중이 아니더라도 받아들이는 정책을 취하자.
++m_dwSignaledCount;
DWORD dwCR = m_dwRound;
DWORD dwNR = GetNextRound( dwCurTick, m_dwTimeout );
DWORD dwMR = PROCESS_HEARTBEAT_VALUATION::GetRecordInfo()->dwMaxRound;
DWORD dwDR = dwNR - dwCR;
DWORD& rdwRC = m_dwRoundCount;
DWORD& rdwTC = m_dwAccumulatedCount;
//MessageOut( eCRITICAL_LOG,
////_tprintf(
// _T("CT\t%8u\tTO\t%8u\tDT\t%d\tCR\t%d\tNR\t%d\tMR\t%d\tDR\t%d\tRC\t%d\tTC\t%d\n"),
// dwCurTick, m_dwTimeout, m_dwTimeout-dwCurTick, dwCR, dwNR, dwMR, dwDR, rdwRC, rdwTC );
//MessageOut( eCRITICAL_LOG,"---------------------------------------------------------------------");
if( dwDR == 0 )
{
// 동일 Round 내 복수 Count
++rdwRC;
++rdwTC;
// <NEEDED> 단일 평가 로직
if( rdwRC > 1 )
CheckOverflow();
return;
}
else
{
if( dwNR < dwMR )
{
dwCR = dwNR%dwMR;
rdwRC = 1;
++rdwTC;
}
else
{
// <NEEDED> Valuation Time
CheckValuationTime( pUser );
dwCR = dwNR%dwMR;
rdwRC = 1;
rdwTC = 1;
m_pWarningFields[this->OVERFLOW_SAMEROUND] = 0;
m_pWarningFields[this->ARMORKIT_SIGNALED] = 0;
}
m_dwRound = dwCR;
sSTRICT_HEARTBEAT_UPPERBOUND_RECORD* pRECORD = PROCESS_HEARTBEAT_VALUATION::GetRecordInfo()->pRECORD;
pRECORD = pRECORD + dwCR;
//m_dwTimeout = dwCurTick + pRECORD->m_dwFlagTick;
m_dwTimeout = m_dwTimeout + (dwDR*pRECORD->m_dwFlagTick);
// _tprintf( "Fix Value %d %d %d\n", dwDR, pRECORD->m_dwFlagTick, (dwDR*pRECORD->m_dwFlagTick) );
}
}