#include "ErrorCode/ErrorCode.h" #include "LogClient/LogClient.h" #include "BattleArenaWithWaitQueue.h" #include "PartyManager.h" #include "DB_Commands.h" #include "BattleArenaManager.h" BattleArenaWithWaitQueue::BattleArenaWithWaitQueue( const BattleArenaBaseServer* pArenaBase, ThreadSafeIntMap* pPartyToArenaMap ) : BattleArena( pArenaBase, pPartyToArenaMap ) { } BattleArenaWithWaitQueue::~BattleArenaWithWaitQueue() { } _BATTLE_GRADE BattleArenaWithWaitQueue::GetPlayableLongestWaitingGrade() const { _BATTLE_GRADE nPlayableLongestWaitingGrade = BG_INVALID; AR_TIME tEarliestWaitStart = INFINITE_TIME; for( int nGrade = 0; nGrade < MAX_BATTLE_GRADE; ++nGrade ) { const std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = _getWaitQueue( static_cast< _BATTLE_GRADE >( nGrade ) ); if( pWaitQueue == NULL ) continue; if( pWaitQueue->empty() ) continue; AR_TIME tWaitStart = pWaitQueue->front().second; if( tWaitStart < tEarliestWaitStart && pWaitQueue->size() >= m_pArenaBase->nMinMember ) { tEarliestWaitStart = tWaitStart; nPlayableLongestWaitingGrade = static_cast< _BATTLE_GRADE >( nGrade ); } } return nPlayableLongestWaitingGrade; } unsigned short BattleArenaWithWaitQueue::_joinWaitQueue( StructPlayer* pPlayer, _BATTLE_GRADE eGrade ) { // 락 체크 assert( m_csArena.IsLockedByCurrentThread() ); assert( eGrade != BG_INVALID ); if( _isFull() ) return RESULT_LIMIT_MAX; int nCurrentPartyID = pPlayer->GetPartyID(); if( nCurrentPartyID && PartyManager::GetInstance().GetPartyType( nCurrentPartyID ) != PartyManager::TYPE_NORMAL_PARTY ) return RESULT_NOT_ACTABLE_IN_SIEGE_OR_RAID; // 현재 진행 중인 경기의 체크 for( unsigned int nInstanceNo = 1; nInstanceNo <= MAX_BATTLE_ARENA_INSTANCE_NO_PER_ARENA; ++nInstanceNo ) { BattleArenaInstance* pBattleInstance = _getBattleInstance( nInstanceNo ); if( pBattleInstance == NULL ) { continue; } THREAD_SYNCHRONIZE1( pBattleInstance->m_csBattle ); if( pBattleInstance->m_eGrade == eGrade && !pBattleInstance->m_bStart && !pBattleInstance->m_bEnd ) { // 방에 참여될 때는 가명 사용 여부 설정 pPlayer->SetUseAlias( true ); unsigned short nResult = pBattleInstance->_addPlayer( pPlayer ); // 최근에 만들어진 방에 참여가 성공했다면 대기열에 넣을 필요가 없으므로 여기서 성공으로 리턴 if( nResult == RESULT_SUCCESS ) return RESULT_SUCCESS; // 여기 도달한 경우는 최근에 만들어진 방에 인원 초과였던 경우가 대부분이며, // 다른 원인으로 참여 실패했다고 해도 기존의 경기에는 참여 안 하고 대기열로 가면 별 문제 없으므로 // 별다른 처리 없이 그냥 넘어가서 대기열에 현재 유저를 추가함 // 설정됐던 가명 사용 여부 해제 pPlayer->SetUseAlias( false ); } } std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = _getWaitQueue( eGrade ); if( pWaitQueue == NULL ) { return RESULT_LIMIT_MAX; } pWaitQueue->push_back( PlayerWaitInfo( pPlayer, GetArTime() ) ); // 대기열에서 대기중인 유저들에게 대기 중인 유저 수 갱신 방송 BattleArenaManager::BroadcastWaitUserCountMessage( this, eGrade ); LOG::Log11N4S( LM_BATTLE_ARENA_JOIN_WAIT_QUEUE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), m_pArenaBase->nID, 0, eGrade, pWaitQueue->size(), 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS ); // 대기열에 유저를 추가했다면 RESULT_SUCCESS가 아니라 RESULT_PENDING을 리턴함 // * 이 함수의 리턴값으로 대기열에 들어간 건지 최근에 시작되었던 방에 들어간 건지를 구분하기 위함... 리턴값 상수 이름으로는 다소 모호하지만;; return RESULT_PENDING; } unsigned short BattleArenaWithWaitQueue::_leaveWaitQueue( StructPlayer* pPlayer, _ARENA_LEAVE_TYPE eLeaveType ) { // 락 체크 assert( m_csArena.IsLockedByCurrentThread() ); // 대기열에 등록하던 시점과 대기열에서 이탈하는 시점 사이에 캐릭터의 레벨이 변경되었을 가능성이 있기 때문에 // 여기서 GetBattleArenaGrade( pPlayer ) 에 의해 얻어진 아레나 등급만을 믿으면 안 됨. // 하지만 대기 시점의 등급과 동일할 가능성이 높기 때문에 가장 먼저 현재 레벨에 해당하는 등급을 검사함. _BATTLE_GRADE eGrade = GetBattleArenaGrade( pPlayer->GetLevel() ); if( eGrade == BG_INVALID ) { // 레벨 다운 BG_INVALID 가 올수 있다. eGrade = BG_ROOKIE; } _BATTLE_GRADE aeGrades[ MAX_BATTLE_GRADE ] = { eGrade, ( eGrade == BG_ROOKIE ) ? BG_GROW : BG_ROOKIE, ( eGrade == BG_MAJOR ) ? BG_GROW : BG_MAJOR }; for( int i = 0; i < MAX_BATTLE_GRADE; ++i ) { std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = _getWaitQueue( aeGrades[ i ] ); if( pWaitQueue != NULL ) { for( std::vector< PlayerWaitInfo >::iterator it = pWaitQueue->begin(); it != pWaitQueue->end(); ++it ) { StructPlayer* pTemp = (*it).first; if( pTemp != pPlayer ) continue; BattleArenaManager::SendBattleArenaLeaveMessage( pPlayer, eLeaveType ); pWaitQueue->erase( it ); // 대기열에서 대기중인 유저들에게 대기 중인 유저 수 갱신 방송 BattleArenaManager::BroadcastWaitUserCountMessage( this, aeGrades[ i ] ); pPlayer->SetUseAlias( false ); pPlayer->SetBattleArenaID( 0 ); LOG::Log11N4S( LM_BATTLE_ARENA_LEAVE_WAIT_QUEUE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), m_pArenaBase->nID, 0, aeGrades[ i ], pWaitQueue->size(), 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS ); return RESULT_SUCCESS; } } } return RESULT_NOT_EXIST; } unsigned short BattleArenaWithWaitQueue::_leaveWaitQueue( PlayerUID nPlayerUID, _ARENA_LEAVE_TYPE eLeaveType ) { // 락 체크 assert( m_csArena.IsLockedByCurrentThread() ); _BATTLE_GRADE aeGrades[ MAX_BATTLE_GRADE ] = { BG_ROOKIE, BG_GROW, BG_MAJOR }; for( int i = 0; i < MAX_BATTLE_GRADE; ++i ) { std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = _getWaitQueue( aeGrades[ i ] ); if( pWaitQueue != NULL ) { for( std::vector< PlayerWaitInfo >::iterator it = pWaitQueue->begin(); it != pWaitQueue->end(); ++it ) { StructPlayer* pPlayer = (*it).first; if( pPlayer->GetPlayerUID() != nPlayerUID ) continue; BattleArenaManager::SendBattleArenaLeaveMessage( pPlayer, eLeaveType ); pWaitQueue->erase( it ); // 대기열에서 대기중인 유저들에게 대기 중인 유저 수 갱신 방송 BattleArenaManager::BroadcastWaitUserCountMessage( this, aeGrades[ i ] ); pPlayer->SetUseAlias( false ); pPlayer->SetBattleArenaID( 0 ); LOG::Log11N4S( LM_BATTLE_ARENA_LEAVE_WAIT_QUEUE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), m_pArenaBase->nID, 0, aeGrades[ i ], pWaitQueue->size(), 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS ); return RESULT_SUCCESS; } } } return RESULT_NOT_EXIST; } unsigned char BattleArenaWithWaitQueue::_tryStartNewBattle() { // 인스턴스를 더 생성할 수 없다면 아무 체크도 하지 않고 넘어감 if( _isFull() ) return INVALID_BATTLE_ARENA_INSTANCE_NO; // 새로운 경기가 생성되었을 때 부여될 인스턴스 번호 // * 새로운 경기 시작을 시도할 때마다 필요하면 새로 할당하고, 이전에 할당했던 번호가 쓰이지 않고 남아 있으면 다음 등급 체크에서 그대로 이용함 // 그리고 등급별 새 경기 시작 처리가 모두 종료된 후에는 마지막으로 할당했던 번호가 쓰이지 않고 남아 있으면 해제함 _BATTLE_GRADE eGrade = GetPlayableLongestWaitingGrade(); if( eGrade == BG_INVALID ) { return INVALID_BATTLE_ARENA_INSTANCE_NO; } unsigned char nNewInstanceNo = _allocNewInstanceNo(); if( nNewInstanceNo == INVALID_BATTLE_ARENA_INSTANCE_NO ) { // 현재 체크 등급 이하의 등급에서 방이 새로 생성되면서 풀이 된 경우에만 여기로 들어와야 정상이고 그 외에는 로직 버그일 가능성이 높음 // * 루프 도는 사이에 어떤 경기가 종료되어서 _isFull() == false인 상황으로 바뀌는 경우는 csArena 때문에 없음 assert( _isFull() ); // 한 번 _isFull() == true가 되면 나머지 등급에 대해서는 방을 추가로 생성할 수 없으므로 break; return INVALID_BATTLE_ARENA_INSTANCE_NO; } unsigned short nErrorCode = _createNewBattle( eGrade, nNewInstanceNo ); if( nErrorCode == RESULT_SUCCESS ) { return nNewInstanceNo; } else { // 로그 남겨야 할만한 오류 코드들은 로그 출력 및 성공 시 추가 뒷처리 switch( nErrorCode ) { // 로그 남길 리턴값들 case RESULT_LIMIT_MAX: // 대기열 풀이면 방 만드는 시도 자체 이전에 대기열에 유저가 등록되는 액션 자체를 차단해야 하지 않나? break; case RESULT_ALREADY_EXIST: // _addPlayer에서 기존에 가입되어 있던 파티에서 탈퇴시키는 처리 실패 break; case RESULT_UNKNOWN: // 뭐여;; break; // 일반적인 실패 케이스 case RESULT_LIMIT_MIN: case RESULT_NOT_ENOUGH_BULLET: break; // 새로운 경기 생성 성공 case RESULT_SUCCESS: break; default: // 예상 못 한 리턴값을 넘기는 곳을 찾아서 경우에 따라 로그를 남기거나 아무것도 안하는 그룹 case 들에 추가할 것 assert( 0 ); break; } if( nNewInstanceNo != INVALID_BATTLE_ARENA_INSTANCE_NO ) _freeInstanceNo( nNewInstanceNo ); } return INVALID_BATTLE_ARENA_INSTANCE_NO; } unsigned short BattleArenaWithWaitQueue::_createNewBattle( _BATTLE_GRADE eGrade, unsigned char nNewInstanceNo ) { // 락 체크 assert( m_csArena.IsLockedByCurrentThread() ); assert( IsValidBattleArenaInstanceNo( nNewInstanceNo ) ); std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = _getWaitQueue( eGrade ); if( pWaitQueue == NULL ) { return RESULT_LIMIT_MIN; } int nWaitingMemberCount = (int)pWaitQueue->size(); if( nWaitingMemberCount < m_pArenaBase->nMinMember ) { return RESULT_LIMIT_MIN; } // 대기열에서 기다리는 유저들 중 실제 참여 가능한 유저 목록 선별(룰 상으로는 모두가 항상 가능해야 하지만 어떤 버그로 인한 증상 등) StructPlayer* apJoinablePlayer[ GameRule::BATTLE_ARENA_MAX_MEMBER_COUNT ] = { NULL, }; int nJoinPlayerCount = 0; { std::vector< PlayerWaitInfo >::iterator it; for( it = pWaitQueue->begin(); it != pWaitQueue->end(); /* 루프에서 ++it 처리 */ ) { StructPlayer* pPlayer = (*it).first; // 대기열에 대기했을 때는 가명 사용 여부를 세팅하지 않은 상태이므로 여기에서 pPlayer->IsUsingAlias() == true 이면 // 어디선가 잘못해서 대기열에 있는 유저에게 SetUseAlias( true )를 호출한 경우임 assert( !pPlayer->IsUsingAlias() ); if( pPlayer->GetLogoutTime() != 0 ) { pPlayer->SetBattleArenaID( 0 ); // 아래의 두 줄은 어차피 게임 내로 진입했던 건 아니니 굳이 할 필요 없지만 혹시나 해서... assert( !pPlayer->GetBattleArenaInstanceNo() ); pPlayer->SetBattleArenaInstanceNo( 0 ); // 로그아웃 대기 인사람은 삭제한다. it = pWaitQueue->erase( it ); LOG::Log11N4S( LM_BATTLE_ARENA_LEAVE_WAIT_QUEUE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), m_pArenaBase->nID, 0, eGrade, pWaitQueue->size(), 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS ); continue; } int nPartyID = pPlayer->GetPartyID(); // 경기에 진입시키지 않고 대기열에서도 이탈시켜야 하는 유저 if( nPartyID && PartyManager::GetInstance().GetPartyType( nPartyID ) != PartyManager::TYPE_NORMAL_PARTY ) { BattleArenaManager::SendBattleArenaLeaveMessage( pPlayer, ALT_PARTICIPATING_SPECIAL_PARTY ); // 가명 사용 여부는 세팅된 적이 없으므로 해제할 필요도 없음 // 유저에게 세팅된 BattleArenaID를 0으로 변경해야 하는데, 지역 락을 여기서 걸어줄 수는 없음. // 우선은 csArena에 의존해서 유저의 BattleArenaID도 보호한다고 생각하고 처리함. pPlayer->SetBattleArenaID( 0 ); // 아래의 두 줄은 어차피 게임 내로 진입했던 건 아니니 굳이 할 필요 없지만 혹시나 해서... assert( !pPlayer->GetBattleArenaInstanceNo() ); pPlayer->SetBattleArenaInstanceNo( 0 ); it = pWaitQueue->erase( it ); LOG::Log11N4S( LM_BATTLE_ARENA_LEAVE_WAIT_QUEUE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), m_pArenaBase->nID, 0, eGrade, pWaitQueue->size(), 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS ); continue; } apJoinablePlayer[ nJoinPlayerCount ] = pPlayer; ++nJoinPlayerCount; ++it; if( nJoinPlayerCount == m_pArenaBase->nMaxMember ) break; } // 선별하고 나서 보니 하나의 경기를 시작하기 위해 필요한 최소 인원보다 적다면 // 새로운 경기 시작은 없었던 일로 하고 다른 유저가 대기열에 들어오길 대기 if( nJoinPlayerCount < m_pArenaBase->nMinMember ) { // 대기열에서 대기중인 유저들에게 대기 중인 유저 수 갱신 방송 BattleArenaManager::BroadcastWaitUserCountMessage( this, eGrade ); return RESULT_LIMIT_MIN; } // 선별된 유저들로 새로운 경기를 시작할 수 있으므로, 해당 유저들은 대기열에서 제거 pWaitQueue->erase( pWaitQueue->begin(), it ); } // 랜덤으로 팀 분배 StructPlayer* apTeamMember[ GameRule::BATTLE_ARENA_MAX_TEAM_COUNT ][ GameRule::BATTLE_ARENA_MAX_MEMBER_COUNT_PER_TEAM ] = { 0, }; { char anSlotNo[ GameRule::BATTLE_ARENA_MAX_MEMBER_COUNT ] = { 0, }; for( int i = 0; i < nJoinPlayerCount; ++i ) { anSlotNo[ i ] = i; } // 각 유저마다 랜덤 슬롯에 배치 for( int i = 0; i < nJoinPlayerCount; ++i ) { int nSlotIndex = XRandom( i, nJoinPlayerCount - 1 ); std::swap( anSlotNo[ i ], anSlotNo[ nSlotIndex ] ); apTeamMember[ anSlotNo[ i ] % m_pArenaBase->nTeamCount ][ anSlotNo[ i ] / m_pArenaBase->nTeamCount ] = apJoinablePlayer[ i ]; } } // 인원 불균형 시 특정 팀이 항상 더 많은 멤버를 가지고 시작하지 않도록 팀의 순서를 바꾸기 처리 // * 이를 위해 이하의 코드 블럭에서는 팀 번호를 접근할 때에 반드시 anTeamIndex[ i ] 를 거치도록 해야 함 int anTeamIndex[ GameRule::BATTLE_ARENA_MAX_TEAM_COUNT ] = { 0, }; for( int i = 0; i < m_pArenaBase->nTeamCount; ++i ) { anTeamIndex[ i ] = i; } if( nJoinPlayerCount % m_pArenaBase->nTeamCount ) { for( int i = 0; i < m_pArenaBase->nTeamCount; ++i ) { // 50% 확률로 다음 팀과 팀 순서 뒤집기 if( XRandom( 1, 100 ) <= 50 ) { std::swap( anTeamIndex[ i ], anTeamIndex[ ( i + 1 ) % m_pArenaBase->nTeamCount ] ); } } } // 대기열에서 대기중인 유저들에게 대기 중인 유저 수 갱신 방송 BattleArenaManager::BroadcastWaitUserCountMessage( this, eGrade ); BattleArenaInstance*& pBattleInstance = m_apBattleList[ nNewInstanceNo ]; // 처음 해당 방 번호에 경기가 생성되는 경우 BattleArenaInstance 객체를 할당(이후는 같은 방 번호의 경우 기존에 할당받았던 객체를 재사용함) if( pBattleInstance == NULL ) { pBattleInstance = new BattleArenaInstance( m_pArenaBase, m_pPartyToArenaMap, &m_partyToInstanceMap, nNewInstanceNo ); } THREAD_SYNCHRONIZE1( pBattleInstance->m_csBattle ); pBattleInstance->Init( eGrade, GetArTime() + m_pArenaBase->nCountdownDuration ); // 각 팀별 파티 생성 및 참가 for( int nTeamIndex = 0; nTeamIndex < m_pArenaBase->nTeamCount; ++nTeamIndex ) { int nTeamNo = anTeamIndex[ nTeamIndex ]; unsigned short nResult = RESULT_SUCCESS; for( int nPlayerIndex = 0; nPlayerIndex < GameRule::BATTLE_ARENA_MAX_MEMBER_COUNT_PER_TEAM; ++nPlayerIndex ) { StructPlayer* pPlayer = apTeamMember[ nTeamNo ][ nPlayerIndex ]; if( pPlayer == NULL ) { continue; } // 가명 사용 여부 세팅 pPlayer->SetUseAlias( true ); LOG::Log11N4S( LM_BATTLE_ARENA_LEAVE_WAIT_QUEUE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), m_pArenaBase->nID, 0, eGrade, pWaitQueue->size(), 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS ); nResult = pBattleInstance->_addPlayer( pPlayer, nTeamIndex ); // 성공적으로 경기에 추가되었으면 다음 유저 처리(참여된 유저들에게 서로 방송을 하는 것은 첨여 처리 완료 이후에 함) if( nResult == RESULT_SUCCESS ) { apTeamMember[ nTeamNo ][ nPlayerIndex ] = NULL; continue; } // 현재 유저는 방 참여에 실패했고, 해당 유저만 이탈되면 되는 경우 if( nResult == RESULT_NOT_ACTABLE_IN_SIEGE_OR_RAID || nResult == RESULT_TARGET_IN_SIEGE_OR_RAID || nResult == RESULT_LIMIT_MAX || nResult == RESULT_INVALID_TEXT || nResult == RESULT_ALREADY_EXIST ) { // 페널티 줘야 하는 경우 if( nResult == RESULT_NOT_ACTABLE_IN_SIEGE_OR_RAID || nResult == RESULT_TARGET_IN_SIEGE_OR_RAID ) { pPlayer->IncBattleArenaPenalty(); pPlayer->DBQuery( new DB_UpdateBattleArenaPenalty( pPlayer ) ); BattleArenaManager::SendBattleArenaPenaltyMessage( pPlayer ); } // 참여 유저 목록에서 해당 유저 제거 apTeamMember[ nTeamNo ][ nPlayerIndex ] = NULL; // 자신에게 이탈되었다는 것을 알려 줌 BattleArenaManager::SendBattleArenaLeaveMessage( pPlayer, ALT_FAILED_TO_JOIN_BATTLE ); // 유저가 이탈했다는 것을 나머지 참가자에게 방송해야 함(PartyManager::DoEachMember를 쓸 수 없는게, 아직 파티 편성이 다 안 된 상태라서 -_ -;) // * 방송 순서는 관계 없으므로 anTeamIndex를 거치지 않고 바로 nTeamNo로 각 팀을 순회 // * RESULT_INVALID_TEXT 인 경우는 가명 정보가 정상적으로 처리되지 않은 경우이기 때문에 방송을 할 수 없음(_addPlayer 자체가 실패했으니 방송 안해도 무방함) if( nResult != RESULT_INVALID_TEXT ) { BattleArenaInstance::PlayerInfo null_pi( pPlayer ); BattleArenaManager::BroadcastBattleArenaLeaveBattleMessageWithLog( pBattleInstance, &null_pi, pPlayer, pBattleInstance->m_nWinTeamNo, INVALID_BATTLE_ARENA_TEAM_NO, pPlayer->GetAlias(), ALT_FAILED_TO_JOIN_BATTLE ); } // 이탈된 유저에게 세팅된 정보 제거 pPlayer->SetUseAlias( false ); pPlayer->SetBattleArenaID( 0 ); // 유저 한 명이 이탈했지만 경기 시작에는 문제는 없으므로 nResult를 RESULT_SUCCESS로 다시 바꿔 줌 nResult = RESULT_SUCCESS; } // 유저 참가 처리가 실패했는데, 해당 유저만 쫓아내서 될 상황이 아니라면 방 생성 자체를 실패 처리 else if( nResult == RESULT_UNKNOWN ) break; } // 방 생성이 불가능해지는 문제는 없었지만 한 팀이 모두 이탈된 경우인지 체크 if( nResult == RESULT_SUCCESS ) { if( pBattleInstance->_getTeamMemberCount( nTeamNo ) == 0 ) { nResult = RESULT_NOT_ENOUGH_BULLET; } } // 중간에 복구가 불가능한 형태로 유저 참여가 실패한 경우에는 나머지 유저들에게는 미안하지만 방을 파토냄 if( nResult != RESULT_SUCCESS ) { BattleArenaNULLFunctor null_functor; pBattleInstance->_removeAllPlayer( ALT_FAILED_TO_START_BATTLE, null_functor ); // 참여 중이던 모든 유저들에게 경기 시작 실패로 대기열에서 이탈되었음을 알려줌 for( int _nTeamNo = 0; _nTeamNo < m_pArenaBase->nTeamCount; ++_nTeamNo ) { // apTeamMember 중간중간에 NULL 값이 들어있을 수 있지만, // 불의의 사고로 이탈된 유저가 이미 있었다면 해당 칸이 NULL이 된 이후에 그 뒤로도 다른 유저가 있을 수 있으므로 // _nPlayerIndex 순회는 반드시 [ 0 ~ GameRule::BATTLE_ARENA_MAX_MEMBER_COUNT_PER_TEAM ) 의 범위를 다 해야 함 for( int _nPlayerIndex = 0; _nPlayerIndex < GameRule::BATTLE_ARENA_MAX_MEMBER_COUNT_PER_TEAM; ++_nPlayerIndex ) { StructPlayer* pPlayer = apTeamMember[ _nTeamNo ][ _nPlayerIndex ]; if( pPlayer == NULL ) continue; BattleArenaManager::SendBattleArenaLeaveMessage( pPlayer, ALT_FAILED_TO_START_BATTLE ); pPlayer->SetUseAlias( false ); pPlayer->SetBattleArenaID( 0 ); LOG::Log11N4S( LM_BATTLE_ARENA_LEAVE_WAIT_QUEUE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), m_pArenaBase->nID, 0, eGrade, pWaitQueue->size(), 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS ); } } return nResult; } } pBattleInstance->_initInstance(); return RESULT_SUCCESS; } AR_TIME BattleArenaWithWaitQueue::_getWaitStartTime( _BATTLE_GRADE eGrade ) const { AR_TIME tWaitStart = 0; const std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = _getWaitQueue( eGrade ); if( pWaitQueue != NULL ) { if( pWaitQueue->empty() == false ) { tWaitStart = pWaitQueue->front().second; } } return tWaitStart; } std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* BattleArenaWithWaitQueue::_getWaitQueue( _BATTLE_GRADE eGrade ) { assert( IsValidBattleArenaGrade( eGrade ) ); std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = NULL; if( IsValidBattleArenaGrade( eGrade ) ) { pWaitQueue = &m_vWaitQueue[eGrade]; } return pWaitQueue; } const std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* BattleArenaWithWaitQueue::_getWaitQueue( _BATTLE_GRADE eGrade ) const { assert( IsValidBattleArenaGrade( eGrade ) ); const std::vector< BattleArenaWithWaitQueue::PlayerWaitInfo >* pWaitQueue = NULL; if( IsValidBattleArenaGrade( eGrade ) ) { pWaitQueue = &m_vWaitQueue[eGrade]; } return pWaitQueue; }