Files
Leviathan/Server/GameServer/Game/DaemonProc/RankingManager.cpp
T
2026-06-01 12:46:52 +02:00

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 );
}
}
}