398 lines
14 KiB
C++
398 lines
14 KiB
C++
|
|
#include <string>
|
|
|
|
#include <mmo/ArcadiaServer.h>
|
|
#include <toolkit/XEnv.h>
|
|
|
|
#include "ErrorCode/ErrorCode.h"
|
|
#include "LogClient/LogClient.h"
|
|
|
|
#include "CompeteManager.h"
|
|
#include "StructSummon.h"
|
|
#include "SendMessage.h"
|
|
#include "GameRule.h"
|
|
#include "PartyManager.h"
|
|
#include "GuildManager.h"
|
|
|
|
|
|
CompeteManager::~CompeteManager()
|
|
{
|
|
THREAD_SYNCHRONIZE( m_csCompete );
|
|
|
|
for( std::vector< CompeteInfoBase * >::const_iterator it = m_vCompeteInfo.begin() ; it != m_vCompeteInfo.end() ; ++it )
|
|
{
|
|
delete (*it);
|
|
}
|
|
m_vCompeteInfo.clear();
|
|
}
|
|
|
|
void CompeteManager::Init()
|
|
{
|
|
ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_HIGH );
|
|
}
|
|
|
|
void CompeteManager::DeInit()
|
|
{
|
|
ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_IDLE );
|
|
}
|
|
|
|
const unsigned short CompeteManager::RequestCompeteToPlayer( struct StructPlayer * pRequester, struct StructPlayer * pRequestee )
|
|
{
|
|
THREAD_SYNCHRONIZE( m_csCompete );
|
|
|
|
// 도전자가 신청 가능한 상태인지 확인
|
|
unsigned int nCompeteID = pRequester->GetCompeteID();
|
|
if( nCompeteID )
|
|
{
|
|
CompeteInfoBase * pCompeteInfo = getCompeteInfo( nCompeteID );
|
|
if( pCompeteInfo )
|
|
{
|
|
if( pCompeteInfo->eState == COMPETE_STATE_REQUESTING )
|
|
return RESULT_WAITING_COMPETE_REQUEST_ANSWER;
|
|
else
|
|
return RESULT_ALREADY_IN_COMPETE;
|
|
}
|
|
|
|
assert( 0 );
|
|
pRequester->SetCompeteID( 0 );
|
|
}
|
|
|
|
if( !( pRequester->IsInField() || pRequester->IsInBattleField() ) || pRequester->IsInDeathmatch() || pRequester->IsInRamadanPrayerHall() )
|
|
return RESULT_NOT_IN_COMPETIBLE_PLACE;
|
|
|
|
// 대상이 신청 가능한 상태인지 확인
|
|
nCompeteID = pRequestee->GetCompeteID();
|
|
if( nCompeteID )
|
|
{
|
|
CompeteInfoBase * pCompeteInfo = getCompeteInfo( nCompeteID );
|
|
if( pCompeteInfo )
|
|
{
|
|
if( pCompeteInfo->eState == COMPETE_STATE_REQUESTING )
|
|
return RESULT_TARGET_WAITING_COMPETE_REQUEST_ANSWER;
|
|
else
|
|
return RESULT_TARGET_ALREADY_IN_COMPETE;
|
|
}
|
|
|
|
assert( 0 );
|
|
pRequestee->SetCompeteID( 0 );
|
|
}
|
|
|
|
if( !( pRequestee->IsInField() || pRequestee->IsInBattleField() ) || pRequestee->IsInDeathmatch() || pRequestee->IsInRamadanPrayerHall() )
|
|
return RESULT_TARGET_NOT_IN_COMPETIBLE_PLACE;
|
|
|
|
// 대련 신청 시점의 거리 체크
|
|
if( pRequestee->GetPos().GetDistance( pRequester->GetPos() ) > GameRule::COMPETE_RING_SEMIDIAMETER )
|
|
{
|
|
return RESULT_TOO_FAR;
|
|
}
|
|
|
|
// 대련 정보 추가
|
|
AR_HANDLE hRequester = pRequester->GetHandle();
|
|
AR_HANDLE hRequestee = pRequestee->GetHandle();
|
|
nCompeteID = insertCompeteInfo( COMPETE_TYPE_VS_PLAYER, &hRequestee, &hRequester, COMPETE_STATE_REQUESTING, time( NULL ) + GameRule::COMPETE_ANSWER_WAITTING_DURATION );
|
|
|
|
pRequester->SetCompeteID( nCompeteID );
|
|
pRequestee->SetCompeteID( nCompeteID );
|
|
|
|
SendCompeteRequest( pRequestee, COMPETE_TYPE_VS_PLAYER, pRequester->GetName() );
|
|
|
|
LOG::Log11N4S( LM_COMPETE_REQUEST, pRequester->GetAccountID(), pRequester->GetSID(), pRequester->GetX(), pRequester->GetY(), pRequester->GetLayer(),
|
|
pRequestee->GetX(), pRequestee->GetY(), pRequestee->GetLayer(), 0, 0, nCompeteID,
|
|
pRequester->GetAccountName(), LOG::STR_NTS, pRequester->GetName(), LOG::STR_NTS, pRequestee->GetAccountName(), LOG::STR_NTS, pRequestee->GetName(), LOG::STR_NTS );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
const unsigned short CompeteManager::AnswerRequestFromPlayer( struct StructPlayer * pRequestee, const _COMPETE_ANSWER_TYPE & eAnswerType )
|
|
{
|
|
THREAD_SYNCHRONIZE( m_csCompete );
|
|
|
|
// 수락 가능 상태인지 확인
|
|
unsigned int nCompeteID = pRequestee->GetCompeteID();
|
|
if( !nCompeteID )
|
|
return RESULT_NOT_IN_COMPETE;
|
|
|
|
CompeteInfoBase * pCompeteInfo = getCompeteInfo( nCompeteID );
|
|
if( !pCompeteInfo )
|
|
{
|
|
pRequestee->SetCompeteID( 0 );
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
if( pCompeteInfo->eState != COMPETE_STATE_REQUESTING )
|
|
{
|
|
return RESULT_ALREADY_IN_COMPETE;
|
|
}
|
|
|
|
// 수락 여부 변수 유효성 체크(이상한 값일 경우 유저 거부로 처리)
|
|
switch( eAnswerType )
|
|
{
|
|
case COMPETE_ANSWER_TYPE_ACCEPT:
|
|
case COMPETE_ANSWER_TYPE_REJECT_BY_USER:
|
|
case COMPETE_ANSWER_TYPE_REJECT_BY_OPTION:
|
|
break;
|
|
default:
|
|
const_cast< _COMPETE_ANSWER_TYPE >( eAnswerType ) = COMPETE_ANSWER_TYPE_REJECT_BY_USER;
|
|
break;
|
|
}
|
|
|
|
// 최초 대련 신청자 유효성 체크
|
|
unsigned short nErrorCode = pCompeteInfo->Validate( false, false, true );
|
|
if( nErrorCode != RESULT_SUCCESS )
|
|
{
|
|
// 유저의 장소 이동으로 인한 무효화 상태가 아니라면 비정상적인 경우
|
|
assert( nErrorCode == RESULT_NOT_IN_COMPETIBLE_PLACE || nErrorCode == RESULT_TARGET_NOT_IN_COMPETIBLE_PLACE );
|
|
deleteCompeteInfo( nCompeteID );
|
|
return nErrorCode;
|
|
}
|
|
|
|
pCompeteInfo->OnAnswerRequest( eAnswerType );
|
|
|
|
// 거절이었을 경우 대련 정보 제거
|
|
if( eAnswerType != COMPETE_ANSWER_TYPE_ACCEPT )
|
|
deleteCompeteInfo( nCompeteID );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
const unsigned short CompeteManager::RetireCompeteWithPlayer( struct StructPlayer * pRetirer, const _COMPETE_END_TYPE & eEndType )
|
|
{
|
|
THREAD_SYNCHRONIZE( m_csCompete );
|
|
|
|
unsigned int nCompeteID = pRetirer->GetCompeteID();
|
|
if( !nCompeteID )
|
|
return RESULT_NOT_IN_COMPETE;
|
|
|
|
CompeteInfoBase * pCompeteInfo = getCompeteInfo( nCompeteID );
|
|
if( !pCompeteInfo )
|
|
{
|
|
pRetirer->SetCompeteID( 0 );
|
|
return RESULT_NOT_EXIST;
|
|
}
|
|
|
|
// 유효성 체크에서 실패할 경우 종료 처리가 완료되었으므로 Retire 함수는 성공...이랄까?
|
|
_COMPETE_END_TYPE eReason = COMPETE_END_BY_LOGOUT;
|
|
bool bRequesterWin = true;
|
|
if( pCompeteInfo->Validate( true, true, true, &eReason, &bRequesterWin ) != RESULT_SUCCESS )
|
|
{
|
|
const bool bRequesteeRetired = ( static_cast< PlayerCompeteInfo * >( pCompeteInfo )->GetRequesteeAsPlayer() == pRetirer );
|
|
StructPlayer * pCompetor = ( bRequesteeRetired ) ? static_cast< PlayerCompeteInfo * >( pCompeteInfo )->GetRequesterAsPlayer() : static_cast< PlayerCompeteInfo * >( pCompeteInfo )->GetRequesteeAsPlayer();
|
|
const bool bCompetorWin = ( bRequesteeRetired ) ? bRequesterWin : !bRequesterWin;
|
|
|
|
// 대련 종료 시 버프 제거 처리 추가(대련 종료 후 디버프에 의한 사망 방지)
|
|
/*
|
|
if( pCompeteInfo->eState == COMPETE_STATE_COMPETING )
|
|
{
|
|
pRetirer->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
StructSummon * pSummon = pRetirer->GetMainSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
pSummon = pRetirer->GetSubSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
}
|
|
*/
|
|
|
|
// 대련 유저 2명 각각 로그를 각각 남김
|
|
LOG::Log11N4S( LM_COMPETE_END, pRetirer->GetAccountID(), pRetirer->GetSID(), pRetirer->GetX(), pRetirer->GetY(), pRetirer->GetLayer(),
|
|
( pCompetor ) ? pCompetor->GetX() : 0, ( pCompetor ) ? pCompetor->GetY() : 0, ( pCompetor ) ? pCompetor->GetLayer() : 0, !bCompetorWin, eReason, pCompeteInfo->nID,
|
|
pRetirer->GetAccountName(), LOG::STR_NTS, pRetirer->GetName(), LOG::STR_NTS,
|
|
( pCompetor ) ? pCompetor->GetAccountName() : "", LOG::STR_NTS, ( pCompetor ) ? pCompetor->GetName() : "", LOG::STR_NTS );
|
|
|
|
if( pCompetor )
|
|
{
|
|
// 대련 종료 시 버프 제거 처리 추가(대련 종료 후 디버프에 의한 사망 방지)
|
|
/*
|
|
if( pCompeteInfo->eState == COMPETE_STATE_COMPETING )
|
|
{
|
|
pCompetor->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
StructSummon * pSummon = pCompetor->GetMainSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
pSummon = pCompetor->GetSubSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
}
|
|
*/
|
|
|
|
LOG::Log11N4S( LM_COMPETE_END, pCompetor->GetAccountID(), pCompetor->GetSID(), pCompetor->GetX(), pCompetor->GetY(), pCompetor->GetLayer(),
|
|
pRetirer->GetX(), pRetirer->GetY(), pRetirer->GetLayer(), bCompetorWin, eReason, pCompeteInfo->nID,
|
|
pCompetor->GetAccountName(), LOG::STR_NTS, pCompetor->GetName(), LOG::STR_NTS,
|
|
pRetirer->GetAccountName(), LOG::STR_NTS, pRetirer->GetName(), LOG::STR_NTS );
|
|
}
|
|
|
|
deleteCompeteInfo( nCompeteID );
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
AR_HANDLE hRetirer = pRetirer->GetHandle();
|
|
pCompeteInfo->OnRetire( &hRetirer, eEndType );
|
|
|
|
deleteCompeteInfo( nCompeteID );
|
|
|
|
return RESULT_SUCCESS;
|
|
}
|
|
|
|
const bool CompeteManager::IsStarted( const unsigned int nCompeteID, const bool bIncludeCounting ) const
|
|
{
|
|
THREAD_SYNCHRONIZE( m_csCompete );
|
|
|
|
CompeteInfoBase * pCompeteInfo = getCompeteInfo( nCompeteID );
|
|
if( !pCompeteInfo )
|
|
return false;
|
|
|
|
return ( ( bIncludeCounting && pCompeteInfo->eState == COMPETE_STATE_COUNTDOWN ) || pCompeteInfo->eState == COMPETE_STATE_COMPETING );
|
|
}
|
|
|
|
void CompeteManager::onProcess( int nThreadIdx )
|
|
{
|
|
char buf[255];
|
|
s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx );
|
|
ENV().Set( buf, "CompeteManager" );
|
|
|
|
extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo;
|
|
s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "CompeteManager(0x%08X)", (UINT_PTR)this );
|
|
s_ThreadInfo.last_execute_time = GetArTime();
|
|
|
|
THREAD_SYNCHRONIZE( m_csCompete );
|
|
|
|
time_t tCurrent = time( NULL );
|
|
|
|
for( std::vector< CompeteInfoBase * >::iterator it = m_vCompeteInfo.begin() ; it != m_vCompeteInfo.end() ; /* 루프에서 ++it */ )
|
|
{
|
|
// onProcess의 호출 주기인 1초마다 한 번씩 Validate를 무조건 함(부하 발생이 크다면 조정 필요 - 수락 이전 단계에서는 체크가 간소화될 수 있음)
|
|
_COMPETE_END_TYPE eReason = COMPETE_END_BY_LOGOUT;
|
|
bool bRequesterWin = false;
|
|
if( (*it)->Validate( true, true, true, &eReason, &bRequesterWin ) != RESULT_SUCCESS )
|
|
{
|
|
// 플레이어 대련 종료 로그 남기기
|
|
if( (*it)->GetCompeteType() == COMPETE_TYPE_VS_PLAYER )
|
|
{
|
|
StructPlayer * pRequestee = static_cast< PlayerCompeteInfo * >( *it )->GetRequesteeAsPlayer();
|
|
StructPlayer * pRequester = static_cast< PlayerCompeteInfo * >( *it )->GetRequesterAsPlayer();
|
|
|
|
// 대련 유저 2명 각각 로그를 각각 남김
|
|
// * 신청 단계에서 Validate 실패는 승자 없이 무조건 false로 남겨야 함
|
|
if( pRequestee )
|
|
{
|
|
// 대련 종료 시 버프 제거 처리 추가(대련 종료 후 디버프에 의한 사망 방지)
|
|
/*
|
|
if( (*it)->eState == COMPETE_STATE_COMPETING )
|
|
{
|
|
pRequestee->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
StructSummon * pSummon = pRequestee->GetMainSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
pSummon = pRequestee->GetSubSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
}
|
|
*/
|
|
|
|
LOG::Log11N4S( LM_COMPETE_END, pRequestee->GetAccountID(), pRequestee->GetSID(), pRequestee->GetX(), pRequestee->GetY(), pRequestee->GetLayer(),
|
|
( pRequester ) ? pRequester->GetX() : 0, ( pRequester ) ? pRequester->GetY() : 0, ( pRequester ) ? pRequester->GetLayer() : 0, ( (*it)->eState != COMPETE_STATE_REQUESTING ) ? !bRequesterWin : false, eReason, (*it)->nID,
|
|
pRequestee->GetAccountName(), LOG::STR_NTS, pRequestee->GetName(), LOG::STR_NTS,
|
|
( pRequester ) ? pRequester->GetAccountName() : "", LOG::STR_NTS, ( pRequester ) ? pRequester->GetName() : "", LOG::STR_NTS );
|
|
}
|
|
if( pRequester )
|
|
{
|
|
// 대련 종료 시 버프 제거 처리 추가(대련 종료 후 디버프에 의한 사망 방지)
|
|
/*
|
|
if( (*it)->eState == COMPETE_STATE_COMPETING )
|
|
{
|
|
pRequester->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
StructSummon * pSummon = pRequester->GetMainSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
pSummon = pRequester->GetSubSummon();
|
|
if( pSummon && pSummon->IsInWorld() ) pSummon->PendClearStateWithFlag( StateInfo::ERASE_ON_DEAD );
|
|
}
|
|
*/
|
|
|
|
LOG::Log11N4S( LM_COMPETE_END, pRequester->GetAccountID(), pRequester->GetSID(), pRequester->GetX(), pRequester->GetY(), pRequester->GetLayer(),
|
|
( pRequestee ) ? pRequestee->GetX() : 0, ( pRequestee ) ? pRequestee->GetY() : 0, ( pRequestee ) ? pRequestee->GetLayer() : 0, ( (*it)->eState != COMPETE_STATE_REQUESTING ) ? bRequesterWin : false, eReason, (*it)->nID,
|
|
pRequester->GetAccountName(), LOG::STR_NTS, pRequester->GetName(), LOG::STR_NTS,
|
|
( pRequestee ) ? pRequestee->GetAccountName() : "", LOG::STR_NTS, ( pRequestee ) ? pRequestee->GetName() : "", LOG::STR_NTS );
|
|
}
|
|
}
|
|
|
|
delete (*it);
|
|
it = m_vCompeteInfo.erase( it );
|
|
continue;
|
|
}
|
|
|
|
if( (*it)->tNextStateChange == NOT_SCHEDULED_TIME || (*it)->tNextStateChange > tCurrent )
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
switch( (*it)->eState )
|
|
{
|
|
case COMPETE_STATE_REQUESTING:
|
|
// 응답 대기 시간 초과 처리
|
|
(*it)->OnAnswerTimeout();
|
|
|
|
delete (*it);
|
|
it = m_vCompeteInfo.erase( it );
|
|
break;
|
|
case COMPETE_STATE_COUNTDOWN:
|
|
// 카운트 다운 완료 처리(대련 시작 됨)
|
|
(*it)->OnEndCountdown();
|
|
++it;
|
|
break;
|
|
case COMPETE_STATE_COMPETING:
|
|
// 대련 시간 초과 처리
|
|
(*it)->OnCompeteTimeout();
|
|
|
|
delete (*it);
|
|
it = m_vCompeteInfo.erase( it );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const unsigned int CompeteManager::insertCompeteInfo( const _COMPETE_TYPE & eType, const void * pRequestee, const void * pRequester, const _COMPETE_STATE & eState, const time_t & tNextStateChange )
|
|
{
|
|
if( !pRequestee || !pRequester )
|
|
return 0;
|
|
|
|
CompeteInfoBase * pCompeteInfo = NULL;
|
|
|
|
switch( eType )
|
|
{
|
|
case COMPETE_TYPE_VS_PLAYER:
|
|
pCompeteInfo = new PlayerCompeteInfo( *static_cast< const AR_HANDLE * >( pRequestee ), *static_cast< const AR_HANDLE * >( pRequester ), eState, tNextStateChange );
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
|
|
if( !pCompeteInfo )
|
|
return 0;
|
|
|
|
m_vCompeteInfo.push_back( pCompeteInfo );
|
|
|
|
return pCompeteInfo->nID;
|
|
}
|
|
|
|
const bool CompeteManager::deleteCompeteInfo( const unsigned int nCompeteID )
|
|
{
|
|
for( std::vector< CompeteInfoBase * >::iterator it = m_vCompeteInfo.begin() ; it != m_vCompeteInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->nID == nCompeteID )
|
|
{
|
|
delete (*it);
|
|
m_vCompeteInfo.erase( it );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CompeteInfoBase * CompeteManager::getCompeteInfo( const unsigned int nCompeteID ) const
|
|
{
|
|
for( std::vector< CompeteInfoBase * >::const_iterator it = m_vCompeteInfo.begin() ; it != m_vCompeteInfo.end() ; ++it )
|
|
{
|
|
if( (*it)->nID == nCompeteID )
|
|
return (*it);
|
|
}
|
|
|
|
return NULL;
|
|
}
|