680 lines
21 KiB
C++
680 lines
21 KiB
C++
|
|
#include <mmo/ArcadiaServer.h>
|
|
#include <logging/FileLog.h>
|
|
#include <toolkit/XEnv.h>
|
|
#include <toolkit/XConsole.h>
|
|
|
|
#include "LogClient/LogClient.h"
|
|
|
|
#include "RankingManager.h"
|
|
#include "StructPlayer.h"
|
|
#include "DB_Commands.h"
|
|
|
|
|
|
const size_t RankingManager::RANKING_TOP_RECORD_MIN_RANK[ RANKING_TYPE_MAX_INDEX ] = { 10, 0, 10, 10, 10 };
|
|
const c_fixed10 RankingManager::RANKING_TOP_RECORD_MIN_SCORE[ RANKING_TYPE_MAX_INDEX ] = { 100, 0, 0, 0, 10 };
|
|
|
|
const unsigned int RankingManager::_RANKING_INFO::GetRank( const int nPlayerUID ) const
|
|
{
|
|
_RANKING_DATA * pRank = NULL;
|
|
if( m_hsRanking.lookup( nPlayerUID, pRank ) && pRank->m_bIsValid )
|
|
return pRank->m_nRank;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const c_fixed10 RankingManager::_RANKING_INFO::GetRankingScore( const int nPlayerUID ) const
|
|
{
|
|
_RANKING_DATA * pRank = NULL;
|
|
if( m_hsRanking.lookup( nPlayerUID, pRank ) && pRank->m_bIsValid )
|
|
return pRank->m_fScore;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void RankingManager::_RANKING_INFO::SetRankingScore( const int nPlayerUID, const char * pszPlayerName, const c_fixed10 & fScore, const bool bIsValid )
|
|
{
|
|
_RANKING_DATA * pRank = NULL;
|
|
|
|
if( m_hsRanking.lookup( nPlayerUID, pRank ) )
|
|
{
|
|
if( pRank->m_fScore == fScore )
|
|
return;
|
|
|
|
bool bIsScoreUp = pRank->m_fScore < fScore;
|
|
pRank->m_fScore = fScore;
|
|
// pRank->m_bIsValid 는 RankingManager::InvalidateRankingScore에 의해서만 변경됨
|
|
|
|
rerankData( pRank, bIsScoreUp );
|
|
}
|
|
else
|
|
{
|
|
StructPlayer::RegisterPlayerName( nPlayerUID, pszPlayerName );
|
|
|
|
if( fScore == 0 )
|
|
return;
|
|
|
|
pRank = new _RANKING_DATA( nPlayerUID, fScore, bIsValid );
|
|
pRank->m_nRank = static_cast< int >( m_vRanking.size() + 1 );
|
|
|
|
m_vRanking.push_back( pRank );
|
|
m_hsRanking.add( nPlayerUID, pRank );
|
|
|
|
// 없던 데이터 추가한 경우에는 최하위 랭크로 책정되어 있으니 상향 체크를 진행
|
|
rerankData( pRank, true );
|
|
}
|
|
}
|
|
|
|
void RankingManager::_RANKING_INFO::ClearRankingData()
|
|
{
|
|
for( std::vector< _RANKING_DATA * >::const_iterator it = m_vRanking.begin() ; it != m_vRanking.end() ; ++it )
|
|
delete (*it);
|
|
|
|
m_vRanking.clear();
|
|
m_hsRanking.clear();
|
|
}
|
|
|
|
void RankingManager::_RANKING_INFO::rerankData( RankingManager::_RANKING_DATA * pRank, const bool bIsScoreUp )
|
|
{
|
|
std::vector< _RANKING_DATA * >::iterator it = m_vRanking.begin() + ( pRank->m_nRank - 1 );
|
|
|
|
#ifdef _DEBUG
|
|
assert( it == std::find( m_vRanking.begin(), m_vRanking.end(), pRank ) );
|
|
#endif
|
|
|
|
if( it == m_vRanking.end() )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
if( bIsScoreUp )
|
|
{
|
|
if( it == m_vRanking.begin() )
|
|
return;
|
|
|
|
std::vector< _RANKING_DATA * >::iterator itBetter = it - 1;
|
|
while( (*itBetter)->m_fScore < (*it)->m_fScore )
|
|
{
|
|
_RANKING_DATA * pSwapBuffer = (*itBetter);
|
|
(*itBetter) = (*it);
|
|
(*it) = pSwapBuffer;
|
|
|
|
++( (*it)->m_nRank );
|
|
--( (*itBetter)->m_nRank );
|
|
|
|
if( itBetter == m_vRanking.begin() )
|
|
break;
|
|
|
|
--it;
|
|
--itBetter;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( it + 1 == m_vRanking.end() )
|
|
return;
|
|
|
|
std::vector< _RANKING_DATA * >::iterator itWorse = it + 1;
|
|
while( (*itWorse)->m_fScore > (*it)->m_fScore )
|
|
{
|
|
_RANKING_DATA * pSwapBuffer = (*itWorse);
|
|
(*itWorse) = (*it);
|
|
(*it) = pSwapBuffer;
|
|
|
|
--( (*it)->m_nRank );
|
|
++( (*itWorse)->m_nRank );
|
|
|
|
if( ++itWorse == m_vRanking.end() )
|
|
break;
|
|
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
RankingManager & RankingManager::Instance()
|
|
{
|
|
static RankingManager _instance;
|
|
|
|
return _instance;
|
|
}
|
|
|
|
RankingManager::~RankingManager()
|
|
{
|
|
if( IsInitialized() )
|
|
DeInit();
|
|
}
|
|
|
|
void RankingManager::Init()
|
|
{
|
|
m_bInitialized = true;
|
|
ArcadiaServer::Instance().SetObjectPriority( &RankingManager::Instance(), ArSchedulerObject::UPDATE_PRIORITY_LOW );
|
|
}
|
|
|
|
void RankingManager::DeInit()
|
|
{
|
|
ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_IDLE );
|
|
m_bInitialized = false;
|
|
|
|
for( int i = 0 ; i < RANKING_TYPE_MAX_INDEX ; ++i )
|
|
{
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( static_cast< _RANKING_TYPE >( i ) );
|
|
|
|
if( !pRankingInfo )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
pRankingInfo->ClearRankingData();
|
|
}
|
|
}
|
|
|
|
const bool RankingManager::IsInitialized() const
|
|
{
|
|
return GetFinalPriority() != ArSchedulerObject::UPDATE_PRIORITY_IDLE;
|
|
}
|
|
|
|
void RankingManager::Push( GameDBManager::DBProc* pWork )
|
|
{
|
|
THREAD_SYNCRONIZE( m_QueryLock );
|
|
|
|
if( m_lQueryList.empty() )
|
|
{
|
|
DB().Push( pWork );
|
|
}
|
|
|
|
m_lQueryList.push_back( pWork );
|
|
}
|
|
|
|
void RankingManager::onEndQuery()
|
|
{
|
|
THREAD_SYNCRONIZE( m_QueryLock );
|
|
|
|
m_lQueryList.pop_front();
|
|
|
|
if( !m_lQueryList.empty() )
|
|
{
|
|
DB().Push( m_lQueryList.front() );
|
|
}
|
|
}
|
|
|
|
const time_t RankingManager::GetNextSettlingTime( const _RANKING_TYPE & eType ) const
|
|
{
|
|
if( !IsInitialized() )
|
|
{
|
|
assert( 0 );
|
|
return 0;
|
|
}
|
|
|
|
const _RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return 0;
|
|
|
|
return pRankingInfo->m_tNextSettling;
|
|
}
|
|
|
|
const bool RankingManager::SetNextSettlingTime( const RankingManager::_RANKING_TYPE & eType, const time_t & tNextSettlingTime )
|
|
{
|
|
if( IsInitialized() )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return false;
|
|
|
|
pRankingInfo->m_tNextSettling = tNextSettlingTime;
|
|
return true;
|
|
}
|
|
|
|
const unsigned int RankingManager::GetRank( const _RANKING_TYPE & eType, const int nPlayerUID ) const
|
|
{
|
|
const _RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return 0;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
return pRankingInfo->GetRank( nPlayerUID );
|
|
}
|
|
|
|
const c_fixed10 RankingManager::GetRankingScore( const _RANKING_TYPE & eType, const int nPlayerUID ) const
|
|
{
|
|
const _RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return 0;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
return pRankingInfo->GetRankingScore( nPlayerUID );
|
|
}
|
|
|
|
void RankingManager::SetRankingScore( const _RANKING_TYPE & eType, const int nPlayerUID, const char * pszPlayerName, const c_fixed10 & fScore, const bool bIsValid )
|
|
{
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
pRankingInfo->SetRankingScore( nPlayerUID, pszPlayerName, fScore, bIsValid );
|
|
|
|
// 초기화된 상태에서 발생하는 SetRankingScore만 DB에 적용해야 하며, 초기화되지 않은 상태에서의 호출은 로딩에 의한 데이터 입력이므로 DB갱신 안 함
|
|
if( IsInitialized() )
|
|
Push( new DB_SetRankingScore( eType, nPlayerUID, fScore ) );
|
|
}
|
|
|
|
void RankingManager::AddRankingScore( const _RANKING_TYPE & eType, const int nPlayerUID, const char * pszPlayerName, const c_fixed10 & fAddend, const bool bIsValid )
|
|
{
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
c_fixed10 fScore = pRankingInfo->GetRankingScore( nPlayerUID );
|
|
|
|
pRankingInfo->SetRankingScore( nPlayerUID, pszPlayerName, fScore + fAddend, bIsValid );
|
|
|
|
// 초기화된 상태에서 발생하는 SetRankingScore만 DB에 적용해야 하며, 초기화되지 않은 상태에서의 호출은 로딩에 의한 데이터 입력이므로 DB갱신 안 함
|
|
if( IsInitialized() )
|
|
Push( new DB_SetRankingScore( eType, nPlayerUID, fScore + fAddend ) );
|
|
}
|
|
|
|
void RankingManager::DeleteRankingScore( const int nPlayerUID )
|
|
{
|
|
for( int nRankingType = RANKING_TYPE_DONATION ; nRankingType < RANKING_TYPE_MAX_INDEX ; ++nRankingType )
|
|
{
|
|
_RANKING_TYPE eType = static_cast< _RANKING_TYPE >( nRankingType );
|
|
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
continue;
|
|
|
|
switch( eType )
|
|
{
|
|
case RANKING_TYPE_DONATION:
|
|
case RANKING_TYPE_HUNTAHOLIC_THIS_MONTH:
|
|
case RANKING_TYPE_HUNTAHOLIC_LAST_MONTH:
|
|
case RANKING_TYPE_HUNTAHOLIC_TOTAL:
|
|
{
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
_RANKING_DATA * pRank = NULL;
|
|
if( !pRankingInfo->m_hsRanking.lookup( nPlayerUID, pRank ) )
|
|
break;
|
|
|
|
pRankingInfo->m_hsRanking.erase( nPlayerUID );
|
|
|
|
std::vector< _RANKING_DATA * >::iterator it = pRankingInfo->m_vRanking.begin() + ( pRank->m_nRank - 1 );
|
|
assert( (*it) == pRank );
|
|
|
|
it = pRankingInfo->m_vRanking.erase( it );
|
|
while( it != pRankingInfo->m_vRanking.end() )
|
|
{
|
|
--(*it)->m_nRank;
|
|
++it;
|
|
}
|
|
|
|
// DB에서 삭제는 결산 때 일괄적으로 하는 것만 하고 이런 경우에는 점수를 0을 만들어 둠. 서버 재시작 시 로딩에서는 0점짜리는 메모리에 로드하지 않음.
|
|
Push( new DB_SetRankingScore( eType, nPlayerUID, 0 ) );
|
|
|
|
delete pRank;
|
|
}
|
|
break;
|
|
case RANKING_TYPE_DONATION_REWARD:
|
|
// 캐릭터 삭제한 경우에 처리되는 부분이므로 무효화시켜도 상관 없지만 삭제 캐릭터 복구받은 후에 보상을 못 받게 되는 경우가 발생하므로 여기선 아무것도 안 함.
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RankingManager::GetRankingTopRecord( const _RANKING_TYPE & eType, std::vector< RankingRecord > & vTopRecord )
|
|
{
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
vTopRecord.clear();
|
|
|
|
for( std::vector< _RANKING_DATA * >::const_iterator it = pRankingInfo->m_vRanking.begin() ; it != pRankingInfo->m_vRanking.end() ; ++it )
|
|
{
|
|
if( (*it)->m_nRank > RANKING_TOP_RECORD_MIN_RANK[ eType ] || (*it)->m_fScore < RANKING_TOP_RECORD_MIN_SCORE[ eType ] )
|
|
break;
|
|
|
|
vTopRecord.push_back( RankingRecord( (*it)->m_nPlayerUID, (*it)->m_fScore ) );
|
|
}
|
|
}
|
|
|
|
const bool RankingManager::IsValidRankingScore( const _RANKING_TYPE & eType, const int nPlayerUID ) const
|
|
{
|
|
const _RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return false;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
_RANKING_DATA * pRank = NULL;
|
|
if( !pRankingInfo->m_hsRanking.lookup( nPlayerUID, pRank ) )
|
|
return false;
|
|
|
|
return pRank->m_bIsValid;
|
|
}
|
|
|
|
const bool RankingManager::InvalidateRankingScore( const _RANKING_TYPE & eType, const int nPlayerUID )
|
|
{
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
return false;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
_RANKING_DATA * pRank = NULL;
|
|
if( !pRankingInfo->m_hsRanking.lookup( nPlayerUID, pRank ) )
|
|
return false;
|
|
|
|
if( !pRank->m_bIsValid )
|
|
return false;
|
|
|
|
// { 다음 달까지 10초 이하의 시간밖에 남아있지 않다면 무조건 실패 처리(10초 후에 달이 바뀌어 있는지 체크)
|
|
// SCRIPT_ShowDonationMenu에서 InvalidateRankingScore 함수를 호출하는 시점과 그 이후 시간 관련 처리 시점의 시간차로 인해
|
|
// 달이 바뀌는 순간 보상을 받으면 바뀌기 직전의 보상 대상자가 바뀐 이후의 한 달간 사용할 수 있는 보상 아이템을 받을 수 있기 때문
|
|
time_t tCurrent = time( NULL );
|
|
struct tm tmCurrent;
|
|
errno_t nError = localtime_s( &tmCurrent, &tCurrent );
|
|
if( nError )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
tCurrent += 10;
|
|
struct tm tmPast10Sec;
|
|
nError = localtime_s( &tmPast10Sec, &tCurrent );
|
|
if( nError )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
if( tmCurrent.tm_mon != tmPast10Sec.tm_mon )
|
|
return false;
|
|
// } 다음 달까지 10초 이하의 시간밖에 남아있지 않다면 무조건 실패 처리(10초 후에 달이 바뀌어 있는지 체크)
|
|
|
|
pRank->m_bIsValid = false;
|
|
Push( new DB_InvalidateRankingScore( eType, nPlayerUID ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
void RankingManager::onProcess( int nThreadIdx )
|
|
{
|
|
char buf[255];
|
|
s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx );
|
|
ENV().Set( buf, "RankingManager" );
|
|
|
|
extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo;
|
|
s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "RankingManager(0x%08X)", (UINT_PTR)this );
|
|
s_ThreadInfo.last_execute_time = GetArTime();
|
|
|
|
time_t tCurrent = time( NULL );
|
|
|
|
for( int nRankingType = RANKING_TYPE_DONATION ; nRankingType < RANKING_TYPE_MAX_INDEX ; ++nRankingType )
|
|
{
|
|
_RANKING_TYPE eType = static_cast< _RANKING_TYPE >( nRankingType );
|
|
_RANKING_INFO * pRankingInfo = getRankingInfo( eType );
|
|
if( !pRankingInfo )
|
|
continue;
|
|
|
|
THREAD_SYNCHRONIZE( pRankingInfo->m_csRanking );
|
|
|
|
// Skip if the ranking settlement time has not yet passed
|
|
if( pRankingInfo->m_tNextSettling > tCurrent )
|
|
continue;
|
|
|
|
// During donation ranking settlement, also process donation ranking rewards. For reward settlement, only the next settlement time is set in memory (already pre-set in the DB)
|
|
|
|
time_t tNextSettling = INFINITE;
|
|
// 다음 결산 시점 설정
|
|
switch( eType )
|
|
{
|
|
case RANKING_TYPE_DONATION:
|
|
case RANKING_TYPE_DONATION_REWARD:
|
|
case RANKING_TYPE_HUNTAHOLIC_THIS_MONTH:
|
|
case RANKING_TYPE_HUNTAHOLIC_LAST_MONTH:
|
|
{
|
|
// Set to 00:00:00 on the 1st day of the next month
|
|
struct tm tmLocal;
|
|
errno_t nError = localtime_s( &tmLocal, &tCurrent );
|
|
assert( !nError );
|
|
|
|
// AziaMafia Goddess
|
|
// ++tmLocal.tm_mon;
|
|
// tmLocal.tm_mday = 1;
|
|
|
|
_cprint( "before week day : %d \n", tmLocal.tm_wday);
|
|
_cprint( "before day : %d \n", tmLocal.tm_mday);
|
|
|
|
if (tmLocal.tm_wday == 1)
|
|
{
|
|
tmLocal.tm_mday += 7;
|
|
}
|
|
else
|
|
{
|
|
tmLocal.tm_mday += 8 - (tmLocal.tm_wday);
|
|
}
|
|
|
|
|
|
_cprint( "after week day : %d \n", tmLocal.tm_wday);
|
|
_cprint( "after day : %d \n", tmLocal.tm_mday);
|
|
|
|
tmLocal.tm_hour = 0;
|
|
tmLocal.tm_min = 0;
|
|
tmLocal.tm_sec = 0;
|
|
tmLocal.tm_isdst = -1;
|
|
|
|
|
|
|
|
tNextSettling = mktime( &tmLocal );
|
|
assert( tNextSettling != -1 );
|
|
|
|
_cprint( "Settling a ranking data: id(%d), tCurrent(%I64d), tNextSettling(%I64d -> %I64d = %04d-%02d-%02d %02d:%02d:%02d)\n",
|
|
eType, tCurrent, pRankingInfo->m_tNextSettling, tNextSettling,
|
|
tmLocal.tm_year + 1900, tmLocal.tm_mon + 1, tmLocal.tm_mday, tmLocal.tm_hour, tmLocal.tm_min, tmLocal.tm_sec );
|
|
FILELOG( "Settling a ranking data: id(%d), tCurrent(%I64d), tNextSettling(%I64d -> %I64d = %04d-%02d-%02d %02d:%02d:%02d)",
|
|
eType, tCurrent, pRankingInfo->m_tNextSettling, tNextSettling,
|
|
tmLocal.tm_year + 1900, tmLocal.tm_mon+1 , tmLocal.tm_mday, tmLocal.tm_hour, tmLocal.tm_min, tmLocal.tm_sec );
|
|
|
|
// 새로 계산된 다음 랭킹 결산 시점이 열흘 이내일 경우 그 다음 달로 재 계산
|
|
if( tNextSettling - tCurrent < 86400) // 86400 old = 864000
|
|
{
|
|
if( ENV().GetInt( "game.generate_dump_on_ranking_error", -1 ) == eType )
|
|
{
|
|
try
|
|
{
|
|
// Exception 형태로 오류 메시지를 날리기 위한 버퍼
|
|
char szErrorMsg[128];
|
|
|
|
struct tm tmCurrent;
|
|
if( localtime_s( &tmCurrent, &tCurrent ) )
|
|
throw "Time value calculating failed";
|
|
|
|
char szDumpFileName[ MAX_PATH ];
|
|
s_sprintf( szDumpFileName, _countof( szDumpFileName ), "ArcadiaDeesse %04d-%02d-%02d %02d-%02d-%02d.dmp",
|
|
tmCurrent.tm_year + 1900, tmCurrent.tm_mon + 1, tmCurrent.tm_mday, tmCurrent.tm_hour, tmCurrent.tm_min, tmCurrent.tm_sec );
|
|
|
|
HANDLE hDumpFile = CreateFile( szDumpFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
|
|
if( hDumpFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
DWORD nLastError = GetLastError();
|
|
s_sprintf( szErrorMsg, _countof( szErrorMsg ), "CreateFile failed - %u", nLastError );
|
|
throw szErrorMsg;
|
|
}
|
|
|
|
if( !MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithFullMemory, NULL, NULL, NULL ) )
|
|
{
|
|
DWORD nLastError = GetLastError();
|
|
s_sprintf( szErrorMsg, _countof( szErrorMsg ), "MiniDumpWriteDump failed - %u", nLastError );
|
|
throw szErrorMsg;
|
|
}
|
|
|
|
CloseHandle( hDumpFile );
|
|
}
|
|
catch( const char * pszException )
|
|
{
|
|
_cprint( " -> Generating a dump failed(Engine: %s).\n", pszException );
|
|
FILELOG( " -> Generating a dump failed(Engine: %s).", pszException );
|
|
}
|
|
catch( std::exception & e )
|
|
{
|
|
_cprint( " -> Generating a dump failed(General: %s).\n", e.what() );
|
|
FILELOG( " -> Generating a dump failed(General: %s).", e.what() );
|
|
}
|
|
}
|
|
|
|
// AziaMafia Déesse
|
|
//++tmLocal.tm_mon;
|
|
//tmLocal.tm_mday = 1;
|
|
|
|
if (tmLocal.tm_wday == 1)
|
|
{
|
|
tmLocal.tm_mday += 7;
|
|
}
|
|
else
|
|
{
|
|
tmLocal.tm_mday += 8 - (tmLocal.tm_wday);
|
|
}
|
|
|
|
tmLocal.tm_hour = 0;
|
|
tmLocal.tm_min = 0;
|
|
tmLocal.tm_sec = 0;
|
|
tmLocal.tm_isdst = -1;
|
|
|
|
|
|
|
|
tNextSettling = mktime( &tmLocal );
|
|
assert( tNextSettling != -1 );
|
|
|
|
_cprint( " -> Next settling is too close. Auto-adjusted to %I64d = %04d-%02d-%02d %02d:%02d:%02d\n", tNextSettling,
|
|
tmLocal.tm_year + 1900, tmLocal.tm_mon + 1, tmLocal.tm_mday, tmLocal.tm_hour, tmLocal.tm_min, tmLocal.tm_sec );
|
|
FILELOG( " -> Next settling is too close. Auto-adjusted to %I64d = %04d-%02d-%02d %02d:%02d:%02d", tNextSettling,
|
|
tmLocal.tm_year + 1900, tmLocal.tm_mon + 1, tmLocal.tm_mday, tmLocal.tm_hour, tmLocal.tm_min, tmLocal.tm_sec );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RANKING_TYPE_HUNTAHOLIC_TOTAL:
|
|
{
|
|
// 헌터홀릭 토탈 점수 랭킹은 결산이라는 게 없음
|
|
// 2100년 1월 1일로 설정
|
|
struct tm tmLocal;
|
|
|
|
tmLocal.tm_year = 200;
|
|
tmLocal.tm_mon = 1;
|
|
tmLocal.tm_mday = 1;
|
|
tmLocal.tm_hour = 0;
|
|
tmLocal.tm_min = 0;
|
|
tmLocal.tm_sec = 0;
|
|
tmLocal.tm_isdst = -1;
|
|
|
|
tNextSettling = mktime( &tmLocal );
|
|
assert( tNextSettling != -1 );
|
|
pRankingInfo->m_tNextSettling = tNextSettling;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
|
|
LOG::Log11N4S( LM_RANKING_SETTLE, 0, 0, eType, tCurrent, pRankingInfo->m_tNextSettling, tNextSettling, 0, 0, 0, 0, 0, "", LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS );
|
|
|
|
pRankingInfo->m_tNextSettling = tNextSettling;
|
|
|
|
switch( eType )
|
|
{
|
|
case RANKING_TYPE_DONATION:
|
|
{
|
|
// 지난 달 보상 대상자 데이터 제거
|
|
_RANKING_INFO * pRewardRankingInfo = getRankingInfo( RANKING_TYPE_DONATION_REWARD );
|
|
if( pRewardRankingInfo )
|
|
{
|
|
pRewardRankingInfo->ClearRankingData();
|
|
Push( new DB_SettleRanking( RANKING_TYPE_DONATION_REWARD, pRankingInfo->m_tNextSettling ) );
|
|
}
|
|
else
|
|
assert( 0 );
|
|
|
|
// 최고 기록 보유자 로그 생성
|
|
for( std::vector< _RANKING_DATA * >::const_iterator it = pRankingInfo->m_vRanking.begin() ; it != pRankingInfo->m_vRanking.end() ; ++it )
|
|
{
|
|
if( (*it)->m_nRank > RANKING_TOP_RECORD_MIN_RANK[ RANKING_TYPE_DONATION ] || (*it)->m_fScore < RANKING_TOP_RECORD_MIN_SCORE[ RANKING_TYPE_DONATION ] )
|
|
break;
|
|
|
|
// 이번 달 보상 대상자 추가
|
|
pRewardRankingInfo->SetRankingScore( (*it)->m_nPlayerUID, StructPlayer::GetPlayerName( (*it)->m_nPlayerUID ), (*it)->m_fScore, true );
|
|
Push( new DB_SetRankingScore( RANKING_TYPE_DONATION_REWARD, (*it)->m_nPlayerUID, (*it)->m_fScore ) );
|
|
|
|
LOG::Log11N4S( LM_RANKING_TOP_RECORD, 0, (*it)->m_nPlayerUID, RANKING_TYPE_DONATION, (*it)->m_nRank, (*it)->m_fScore.get(), 0, 0, 0, 0, 0, 0,
|
|
"", LOG::STR_NTS, StructPlayer::GetPlayerName( (*it)->m_nPlayerUID ), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS );
|
|
}
|
|
|
|
// 결산 처리(메모리 데이터 소거, DB 처리 요청)
|
|
pRankingInfo->ClearRankingData();
|
|
Push( new DB_SettleRanking( RANKING_TYPE_DONATION, pRankingInfo->m_tNextSettling ) );
|
|
}
|
|
break;
|
|
|
|
case RANKING_TYPE_DONATION_REWARD:
|
|
// 스코어별 랭킹 데이터가 아니므로 아무것도 하지 않음. 실제 데이터 갱신은 기부 랭킹 결산 처리 중에 모두 이루어 짐.
|
|
break;
|
|
|
|
case RANKING_TYPE_HUNTAHOLIC_THIS_MONTH:
|
|
{
|
|
// 이번 달 데이터가 지난 달 데이터가 되어야 하므로 우선 지난 달 데이터 모두 제거
|
|
_RANKING_INFO * pLastMonthRankingInfo = getRankingInfo( RANKING_TYPE_HUNTAHOLIC_LAST_MONTH );
|
|
if( pLastMonthRankingInfo )
|
|
{
|
|
pLastMonthRankingInfo->ClearRankingData();
|
|
Push( new DB_SettleRanking( RANKING_TYPE_HUNTAHOLIC_LAST_MONTH, pRankingInfo->m_tNextSettling ) );
|
|
}
|
|
else
|
|
assert( 0 );
|
|
|
|
// 이번 달 데이터를 지난 달 데이터로 모두 이동(_RANKING_DATA들의 포인터만 옮기고 m_hsRanking만 리빌드해주면 됨. KHash가 복사 연산자가 없어서 리빌드해야됨...;;)
|
|
// DB에서 데이터 이동(이번달 -> 지난달)은 smp_settle_ranking에서 ranking_id가 RANKING_TYPE_HUNTAHOLIC_THIS_MONTH 이면 처리함
|
|
// 위와같이 하면 프로시저가 드러워(?)지지만 다 DELETE 하고 새로 INSERT 하는 것보다는 성능적인 이득이 매우 큼
|
|
pLastMonthRankingInfo->m_vRanking.swap( pRankingInfo->m_vRanking );
|
|
pRankingInfo->m_hsRanking.clear();
|
|
for( std::vector< _RANKING_DATA * >::const_iterator it = pLastMonthRankingInfo->m_vRanking.begin() ; it != pLastMonthRankingInfo->m_vRanking.end() ; ++it )
|
|
{
|
|
pLastMonthRankingInfo->m_hsRanking.add( (*it)->m_nPlayerUID, (*it) );
|
|
}
|
|
|
|
Push( new DB_SettleRanking( RANKING_TYPE_HUNTAHOLIC_THIS_MONTH, pRankingInfo->m_tNextSettling ) );
|
|
}
|
|
break;
|
|
|
|
case RANKING_TYPE_HUNTAHOLIC_LAST_MONTH:
|
|
// 지난 달 데이터이므로 별도로 처리는 없음. 실제 데이터 갱신은 헌터홀릭 이번 달 랭킹 처리 중에 모두 이루어 짐.
|
|
break;
|
|
|
|
case RANKING_TYPE_HUNTAHOLIC_TOTAL:
|
|
// 누적 데이터이므로 결산이 없음.
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
}
|
|
}
|