#include #include #include #include #include #include "LogClient/LogClient.h" #include "ErrorCode/ErrorCode.h" #include "InstanceDungeonManager.h" #include "ChannelManager.h" #include "NPCProc.h" #include "GameProc.h" #include "StructPlayer.h" #include "StructSummon.h" #include "StructMonster.h" #include "PartyManager.h" #include "LuaVM.h" #include "ThreadPlayerHelper.h" extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo; struct MapMonsterPtrEqual { const StructMonster* _ptr ; MapMonsterPtrEqual(const StructMonster* ptr):_ptr(ptr){} bool operator() (const std::pair& left) { return left.second == _ptr; } }; struct MapMonsterResIdEqual { const int _respawn_id ; MapMonsterResIdEqual(const int respawn_id):_respawn_id(respawn_id){} bool operator() (const std::pair& left) { return left.second->GetInstanceRespawnID() == _respawn_id; } }; struct QuitFunctor : public PartyManager::PartyFunctor { QuitFunctor() {} virtual bool operator()( AR_HANDLE handle ) { StructPlayer::iterator itPlayer = StructPlayer::get( handle ); StructPlayer * pPlayer = (*itPlayer); if( !pPlayer ) return false; ArPosition pos; pPlayer->GetPositionOnEnterInstanceGame( &pos ); unsigned char nLayer = 0; int current_channel = ChannelManager::GetChannelId( pPlayer->GetX(), pPlayer->GetY() ); int target_channel = ChannelManager::GetChannelId( pos.x, pos.y ); if( current_channel && current_channel == target_channel ) { nLayer = pPlayer->GetLayer(); } else if( target_channel ) { nLayer = ChannelManager::GetProperLayer( pos.x, pos.y ); } // 파티 락 걸린 상태로 호출되기도 하는 곳이므로 지역락 걸지 않음 pPlayer->PendWarp( pos.GetX(), pos.GetY(), nLayer ); ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); return true; } }; // InstanceDungeonManager::InstanceDungeonType void InstanceDungeonManager::InstanceDungeonType::onMonsterDelete( StructMonster * pMonster ) { int nRespawnID = pMonster->GetInstanceRespawnID(); AR_TIME t = GetArTime(); const GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO * pRespawnInfo = NULL; for( std::vector< const GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO * >::const_iterator itRespawn = pInstanceDungeonTypeBase->vRespawnInfo.begin() ; itRespawn != pInstanceDungeonTypeBase->vRespawnInfo.end() ; ++itRespawn ) { if( (*itRespawn)->nID != nRespawnID ) continue; pRespawnInfo = (*itRespawn); break; } { // csInstanceDungeonType 락을 걸어둔 상태로는 스크립트를 호출할 수 없으며(데드락), // 해당 락을 풀고 csMonsterDelete 같은 별도의 락 없이 스크립트를 실행하면 스크립트 안에서 GetAliveInstanceDungeonRespawnGroupMonsterCount를 // 호출하기 전에 다른 쓰레드에서 onMonsterDelete를 호출해 mapRespawnedMonster.erase를 실행할 수 있으므로 // 모든 몬스터가 한 방에 몰살당하면 각 몬스터가 동시에 onMonsterDelete를 실행하고 pRespawnInfo->szScriptOnDead 에서 // GetAliveInstanceDungeonRespawnGroupMonsterCount를 호출할 때마다 0 마리 남았다는 결과만 반복해서 나온다거나 하게 됨. // csMonsterDelete를 사용함으로써 pRespawnInfo->szScriptOnDead 에서 GetAliveInstanceDungeonRespawnGroupMonsterCount를 호출할 경우 // 모든 몬스터가 한 방의 광역 스킬에 죽었어도 남은 몬스터 수가 1씩 감소한 값을 리턴하게 됨. // 단, 이로 인해 csMonsterDelete 를 거는 부분은 모두 직렬화되어 광역 스킬로 한 번에 많은 몬스터를 죽일 경우 // 병목 현상이 순간적으로 심하게 발생할 수 있음. THREAD_SYNCHRONIZE( csMonsterDelete ); ARCADIA_LOCK( pInstanceDungeonInfo->LockWholeDungeonArea( nInstanceNo ) ); std::vector< PlayerBasicInfoForLog > vPlayerBasicInfoList; { THREAD_SYNCHRONIZE1( csInstanceDungeonType ); std::multimap< int, StructMonster * >::iterator it = std::find_if( mapRespawnedMonster.begin(), mapRespawnedMonster.end(), MapMonsterPtrEqual(pMonster) ); // 이미 삭제 처리 된 몬스터면 리젠 진행시키지 않음(사냥 진행 중인데 사망 처리가 2번 들어왔거나 리젠만 되고 vRespawnedMonster에 추가되지 않았거나... -_ -?) if( it == mapRespawnedMonster.end() ) { assert( 0 ); return; } mapRespawnedMonster.erase( it ); // 로그 작성을 위해 던전 내 참여 중이었던 유저들의 Gold/Exp/JP를 보관 for( std::vector< StructPlayer * >::const_iterator itPlayer = vPlayerList.begin() ; itPlayer != vPlayerList.end() ; ++itPlayer ) { vPlayerBasicInfoList.push_back( PlayerBasicInfoForLog( (*itPlayer) ) ); } } // 몬스터 사망 시 실행될 스크립트 호출 // * 시간제로 소환되었던 몬스터가 시간 제한에 의해 사라지는 경우에도 이 스크립트가 호출됨 // 예를 들면 초강력 무적 몬스터로부터 1분간 살아남아라! 같은 미션을 주고 몬스터 죽는 스크립트가 실행되면 완료라든가 // 근데 이런 기능을 사용하려면 스크립트나 스킬로 몬스터를 리젠시킬 때 리스폰 ID 번호를 할당해주는 기능이 필요한데 // 아직 구현되어 있지 않음. 해당 기능 구현 시에는 DB에 입력된 데이터에 해당하는 리스폰 ID 번호를 발급해도 // 다시 리젠되도록 하는 처리는 발생하지 않으므로(이 함수 바로 아랫 부분에 있음) 문제 없음. // DB에 입력된 리스폰 데이터에 없는 별도의 스크립트를 실행하게 하려면 임시로 새로운 // GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO 인스턴스를 어딘가에 등록하고 시간제로 소환된 몬스터의 // 리스폰 ID를 이용해 해당 인스턴스 포인터를 찾는 처리를 onMonsterDelete 맨 위에 해줘야 함. // 또한, 반복해서 자동으로 리젠되지 않도록 하기 위해 GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO::nPeriod를 // 0으로 설정해 둬야 함 if( pRespawnInfo && pRespawnInfo->szScriptOnDead[ 0 ] ) { // 여기는 그냥 대표 유저(첫번째 유저)를 대상으로 설정한다. StructPlayer* pFirstPlayer = NULL; if( vPlayerList.empty() == false ) { pFirstPlayer = vPlayerList.front(); } ThreadPlayerHelper TPHepler( pFirstPlayer ); char szScriptBuffer[ 256 ]; s_strcpy( szScriptBuffer, _countof( szScriptBuffer ), pRespawnInfo->szScriptOnDead ); char szElementBuffer[ 32 ]; s_sprintf( szElementBuffer, _countof( szElementBuffer ), "%d", (int)pMonster->GetX() ); XStringUtil::Replace( szScriptBuffer, _countof( szScriptBuffer ), "#@pos_x@#", szElementBuffer ); s_sprintf( szElementBuffer, _countof( szElementBuffer ), "%d", (int)pMonster->GetY() ); XStringUtil::Replace( szScriptBuffer, _countof( szScriptBuffer ), "#@pos_y@#", szElementBuffer ); s_sprintf( szElementBuffer, _countof( szElementBuffer ), "%d", (int)pMonster->GetLayer() ); XStringUtil::Replace( szScriptBuffer, _countof( szScriptBuffer ), "#@pos_layer@#", szElementBuffer ); s_sprintf( szElementBuffer, _countof( szElementBuffer ), "%u", pMonster->GetHandle() ); XStringUtil::Replace( szScriptBuffer, _countof( szScriptBuffer ), "#@monster_handle@#", szElementBuffer ); LUA()->RunString( szScriptBuffer ); LOG::Log11N4S( LM_INSTANCE_DUNGEON_PROCESS, 0, 0, pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, nInstanceNo, 0, 0, 0, 0, 0, 0, "", 0, "", 0, szScriptBuffer, LOG::STR_NTS, "MONSTER_KILL", LOG::STR_NTS ); // Gold/Exp/JP 변동 유저들에 대한 로그 작성 { THREAD_SYNCHRONIZE1( csInstanceDungeonType ); for( std::vector< StructPlayer * >::const_iterator itPlayer = vPlayerList.begin() ; itPlayer != vPlayerList.end() ; ++itPlayer ) { StructPlayer * pPlayer = (*itPlayer); // 이 검색 방식에서는 던전 내에 있다가 스크립트 처리 중에 없어졌거나, 혹은 없던 유저가 새로 생긴 경우는 로그가 남지 못 함 for( std::vector< PlayerBasicInfoForLog >::iterator itPBI = vPlayerBasicInfoList.begin() ; itPBI != vPlayerBasicInfoList.end() ; ++itPBI ) { const PlayerBasicInfoForLog & prevPBI = (*itPBI); if( prevPBI != pPlayer ) continue; PlayerBasicInfoForLog currentPBI( pPlayer ); if( prevPBI != currentPBI ) { LOG::Log11N4S( LM_INSTANCE_DUNGEON_PLAYER_INFO_CHANGE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, nInstanceNo, prevPBI.gold.GetRawData(), currentPBI.gold.GetRawData(), prevPBI.exp, currentPBI.exp, prevPBI.jp, currentPBI.jp, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, szScriptBuffer, LOG::STR_NTS, "MONSTER_KILL", LOG::STR_NTS ); } vPlayerBasicInfoList.erase( itPBI ); break; } } } } } // 시간제로 소환되었던 몬스터가 시간 제한에 의해 사라지는 경우에는 리젠 예약 처리를 하지 않음 if( pMonster->IsLifeTimeOver() ) return; // 몬스터 리젠 예약(스킬에 의해 리젠되어 리젠 정보 ID 값이 설정되지 않은 몬스터는 pRespawnInfo == NULL 이어서 리젠 예약되지 않음) if( pRespawnInfo && pRespawnInfo->nPeriod ) { THREAD_SYNCHRONIZE( csInstanceDungeonType ); vPendedMonsterRespawn.push_back( PendedMonsterRespawnInfo( pRespawnInfo, t + pRespawnInfo->nPeriod ) ); } } void InstanceDungeonManager::InstanceDungeonType::clearMonsters() { // 리젠 대기 중인 몬스터 목록 제거 vPendedMonsterRespawn.clear(); // 몬스터 제거 처리(지역 락 필요) for( std::multimap< int , StructMonster * >::iterator itMonster = mapRespawnedMonster.begin() ; itMonster != mapRespawnedMonster.end() ; /* 루프에서 ++itMonster 처리 */ ) { StructMonster* pMonster = itMonster->second; if( pMonster->IsEnable() ) { pMonster->SetDeleteHandler( NULL ); // 더이상의 스케쥴러 요청을 무시 pMonster->Disable(); // 월드에서 제거한다. if( pMonster->IsInWorld() ) { RemoveMonsterFromWorld( pMonster ); } // object delete 요청 ArcadiaServer::Instance().DeleteObject( pMonster ); itMonster = mapRespawnedMonster.erase( itMonster ); } else { ++itMonster; } } } void InstanceDungeonManager::InstanceDungeonType::onFieldPropDelete( StructFieldProp * pProp ) { THREAD_SYNCHRONIZE( csInstanceDungeonType ); std::vector< StructFieldProp * >::iterator itErase = std::find( vRespawnedStaticProp.begin(), vRespawnedStaticProp.end(), pProp ); if( itErase != vRespawnedStaticProp.end() ) { vRespawnedStaticProp.erase( itErase ); if( pProp->GetFieldPropBase()->nRegenTime != 0 ) vPendedHealingPropRespawn.push_back( PendedHealingPropRespawnInfo( pProp->GetRespawnInfo(), GetArTime() + pProp->GetFieldPropBase()->nRegenTime, true ) ); return; } itErase = std::find( vRespawnedProp.begin(), vRespawnedProp.end(), pProp ); if( itErase != vRespawnedProp.end() ) { vRespawnedProp.erase( itErase ); if( pProp->GetFieldPropBase()->nRegenTime != 0 ) vPendedHealingPropRespawn.push_back( PendedHealingPropRespawnInfo( pProp->GetRespawnInfo(), GetArTime() + pProp->GetFieldPropBase()->nRegenTime, false ) ); else delete pProp->GetRespawnInfo(); return; } // 이미 삭제된 프랍이면 아무 처리 안 함(삭제처리가 2번 일어났거나 리젠되기만 하고 vRespawnedStaticProp, vRespawnedProp에는 추가되지 않았거나 -_ -?) assert( 0 ); } void InstanceDungeonManager::InstanceDungeonType::clearHealingProps() { // 리젠 대기 중인 힐링 프랍 제거 vPendedHealingPropRespawn.clear(); // 힐링 프랍 제거 처리(지역 락 필요) for( std::vector< StructFieldProp * >::iterator itHealingProp = vRespawnedStaticProp.begin() ; itHealingProp != vRespawnedStaticProp.end() ; ++itHealingProp ) { // 해당 프랍이 ProcDelete에서 다시 리젠되도록 vPendedHealingPropRespawn에 추가되지 않도록 DeleteHandler를 제거하고 삭제함 (*itHealingProp)->SetDeleteHandler( NULL ); // 유저가 사용함으로 인해서 RemoveObject/PendFreeFieldProp은 걸려 있지만 아직 onFieldPropDelete가 호출되지 않은 경우에는 아무것도 하지 않음 if( !(*itHealingProp)->IsInWorld() ) continue; ArcadiaServer::Instance().RemoveObject( (*itHealingProp) ); StructFieldProp::PendFreeFieldProp( (*itHealingProp) ); } vRespawnedStaticProp.clear(); // 추가적으로 생성된 인던 내부의 프랍 제거 처리(지역 락 필요) for( std::vector< StructFieldProp * >::iterator itHealingProp = vRespawnedProp.begin() ; itHealingProp != vRespawnedProp.end() ; ++itHealingProp ) { // 해당 프랍이 ProcDelete에서 다시 리젠되도록 vPendedHealingPropRespawn에 추가되지 않도록 DeleteHandler를 제거하고 삭제함 (*itHealingProp)->SetDeleteHandler( NULL ); // 유저가 사용함으로 인해서 RemoveObject/PendFreeFieldProp은 걸려 있지만 아직 onFieldPropDelete가 호출되지 않은 경우에는 아무것도 하지 않음 if( !(*itHealingProp)->IsInWorld() ) continue; ArcadiaServer::Instance().RemoveObject( (*itHealingProp) ); StructFieldProp::PendFreeFieldProp( (*itHealingProp) ); delete (*itHealingProp)->GetRespawnInfo(); } vRespawnedProp.clear(); } void InstanceDungeonManager::InstanceDungeonType::clearItems() { // Although not the most efficient method, it’s less burdensome than checking whether an item drops inside the dungeon // every time it’s added to the world (since this is only called once when the dungeon ends) struct DroppedItemCollector : public ArRegionFunctor, ArObjectFunctor { // ArObjectFunctor virtual void operator()( ArObject *pObj ) const { if( !static_cast< GameObject * >( pObj )->IsItem() ) return; vItemList.push_back( static_cast< StructItem * >( pObj ) ); } // ArRegionFunctor virtual void operator()( const struct ArRegion * pRegion ) { // DoEachStaticOjbect 안에서 ArObjectFunctor가 RemoveStaticObject를 호출하면 // 해당 ArRegion의 m_vStaticObject 순회하던 게 박살나므로 포인터만 모음 pRegion->DoEachStaticObject( *this ); } mutable std::vector< StructItem * > vItemList; } droppedItemCollector; ArcadiaServer::Instance().DoEachRegion( pInstanceDungeonInfo->pInstanceDungeonBase->nDungeonRegionLeft, pInstanceDungeonInfo->pInstanceDungeonBase->nDungeonRegionTop, pInstanceDungeonInfo->pInstanceDungeonBase->nDungeonRegionRight, pInstanceDungeonInfo->pInstanceDungeonBase->nDungeonRegionBottom, nInstanceNo, droppedItemCollector ); for( std::vector< StructItem * >::iterator it = droppedItemCollector.vItemList.begin() ; it != droppedItemCollector.vItemList.end() ; ++it ) { StructItem * pItem = (*it); // 월드에서 제거(ItemCollector::onProcess에서 삭제 중인 아이템이었을 경우 실패함) if( !RemoveItemFromWorld( pItem ) ) continue; StructItem::PendFreeItem( pItem ); } } const unsigned short InstanceDungeonManager::InstanceDungeonType::joinInstance( struct StructPlayer * pPlayer ) { if( std::find( vPlayerList.begin(), vPlayerList.end(), pPlayer ) != vPlayerList.end() ) { assert( 0 ); return RESULT_ALREADY_EXIST; } vPlayerList.push_back( pPlayer ); ++nPlayerCount; tDestructionTime = 0; return RESULT_SUCCESS; } const unsigned short InstanceDungeonManager::InstanceDungeonType::leaveInstance( struct StructPlayer * pPlayer ) { std::vector< StructPlayer * >::iterator it = std::find( vPlayerList.begin(), vPlayerList.end(), pPlayer ); if( it == vPlayerList.end() ) { assert( 0 ); return RESULT_NOT_EXIST; } vPlayerList.erase( it ); --nPlayerCount; assert( nPlayerCount >= 0 ); // 적어도 방이 사라지지 않는 문제를 피하기 위해 우선 없앤다. if( nPlayerCount <= 0 ) tDestructionTime = GetArTime(); return RESULT_SUCCESS; } const unsigned short InstanceDungeonManager::InstanceDungeonType::beginInstance( struct StructPlayer * pPlayer ) { // 필요 아이템 소모 // 우선 방을 만드는 사람만 소모를 한다 __int64 nItemCount = 0; if( pInstanceDungeonTypeBase->nNeedItemCode ) { StructItem *pItem = pPlayer->FindItem( pInstanceDungeonTypeBase->nNeedItemCode ); nItemCount = pItem ? pItem->GetCount() : 0; if( !pItem || !pPlayer->EraseItem( pItem, pInstanceDungeonTypeBase->nNeedItemCount ) ) return RESULT_NOT_ACTABLE; } // 몬스터 리젠 시키고~ for( std::vector< const GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO * >::const_iterator itRespawn = pInstanceDungeonTypeBase->vRespawnInfo.begin() ; itRespawn != pInstanceDungeonTypeBase->vRespawnInfo.end() ; ++itRespawn ) { // 리젠 조건이 외부 제어에 의한 경우 패스 if( (*itRespawn)->bRespawnControl ) { continue; } for( int nIndex = 0 ; nIndex < (*itRespawn)->nCount ; ++nIndex ) { int nRespawnX; int nRespawnY; int nRespawnTryCount = 0; do { nRespawnX = XRandom( static_cast< int >( (*itRespawn)->bxArea.GetLeft() ), static_cast< int >( (*itRespawn)->bxArea.GetRight() ) ); nRespawnY = XRandom( static_cast< int >( (*itRespawn)->bxArea.GetTop() ), static_cast< int >( (*itRespawn)->bxArea.GetBottom() ) ); if( ++nRespawnTryCount > 10 ) { FILELOG( "Unable to respawn monster in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn)->nID ); _cprint( "Unable to respawn monster in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)\n", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn)->nID ); nRespawnTryCount = 0; break; } } while( GameContent::IsBlocked( nRespawnX, nRespawnY ) ); if( !nRespawnTryCount ) continue; StructMonster * pMob = respawnMonster( nRespawnX, nRespawnY, nInstanceNo, (*itRespawn)->nMonsterID, (*itRespawn)->bWandering, 0, this, true, nDifficulty ); if( !pMob ) { FILELOG( "Unknown monster is to be respawned in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn)->nID ); _cprint( "Unknown monster is to be respawned in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)\n", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn)->nID ); continue; } pMob->SetInstanceRespawnID( (*itRespawn)->nID ); mapRespawnedMonster.insert( std::pair< int , StructMonster* >( (*itRespawn)->nRespawnGroup, pMob ) ); } } // 프랍 리젠 시키고~ for( std::vector< const GameContent::FIELD_PROP_RESPAWN_INFO * >::const_iterator itHealingProp = pInstanceDungeonTypeBase->vHealingPropInfo.begin() ; itHealingProp != pInstanceDungeonTypeBase->vHealingPropInfo.end() ; ++itHealingProp ) { StructFieldProp * pProp = NULL; { pProp = StructFieldProp::Create( this, (*itHealingProp), (*itHealingProp)->x, (*itHealingProp)->y, nInstanceNo ); } if( pProp ) { vRespawnedStaticProp.push_back( pProp ); } } LOG::Log11N4S( LM_INSTANCE_DUNGEON_CREATE, pPlayer->GetAccountID(), pPlayer->GetSID(), pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, nInstanceNo, nKey, 0, 0, pInstanceDungeonTypeBase->nNeedItemCode, pInstanceDungeonTypeBase->nNeedItemCount, nItemCount - pInstanceDungeonTypeBase->nNeedItemCount, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 ); if( GameRule::bLogVulcanusDungeon && pInstanceDungeonInfo->pInstanceDungeonBase->nID == GameRule::DUNGEON_VULCANUS_ID ) { StructSummon *pMainSummon = pPlayer->GetMainSummon(); StructSummon *pSubSummon = pPlayer->GetSubSummon(); LOG::Log11N4S( LM_VULCANUS_DUNGEON_BEGIN, pPlayer->GetAccountID(), pPlayer->GetSID(), pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, nInstanceNo, pPlayer->GetJobId(), pPlayer->GetLevel(), pMainSummon ? pMainSummon->GetSummonCode() : 0, pMainSummon ? pMainSummon->GetLevel() : 0, pSubSummon ? pSubSummon->GetSummonCode() : 0, pSubSummon ? pSubSummon->GetLevel() : 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 ); } return RESULT_SUCCESS; } const unsigned short InstanceDungeonManager::InstanceDungeonType::endInstance() { if( nPlayerCount > 0 ) { assert( 0 ); return RESULT_NOT_ACTABLE; } // 리젠 대기 중인 몬스터 데이터 제거 { THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csPendedRespawn ); for( std::vector< GameContent::PENDED_RESPAWN_INFO * >::iterator itRespawn = pInstanceDungeonInfo->vPendedRespawn.begin() ; itRespawn != pInstanceDungeonInfo->vPendedRespawn.end() ; /* 루프에서 ++itRespawn 처리 */ ) { if( (*itRespawn)->layer == nInstanceNo ) { delete (*itRespawn); itRespawn = pInstanceDungeonInfo->vPendedRespawn.erase( itRespawn ); } else ++itRespawn; } } // 리젠 대기 중인 프랍 데이터 제거 { THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csPendedRespawnProp ); for( std::vector< GameContent::FIELD_PROP_RESPAWN_INFO * >::iterator itRespawn = pInstanceDungeonInfo->vPendedRespawnProp.begin() ; itRespawn != pInstanceDungeonInfo->vPendedRespawnProp.end() ; /* 루프에서 ++itRespawn 처리 */ ) { if( (*itRespawn)->layer == nInstanceNo ) { delete (*itRespawn); itRespawn = pInstanceDungeonInfo->vPendedRespawnProp.erase( itRespawn ); } else ++itRespawn; } } // 통계 및 분석을 위한 불카누스 던전 전용 로그, 몬스터의 생존 여부를 알기 위해 방 폐쇄 이전에 로그를 남긴다. if( GameRule::bLogVulcanusDungeon && pInstanceDungeonInfo->pInstanceDungeonBase->nID == GameRule::DUNGEON_VULCANUS_ID ) { LOG::Log11N4S( LM_VULCANUS_DUNGEON_END, nOwnerAccountID, nOwnerUID, pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, nInstanceNo, atoi( getFlag( "Vul1" ).c_str() ), atoi( getFlag( "Vul2" ).c_str() ), atoi( getFlag( "Vul3" ).c_str() ), !getAliveRespawnGroupMonsterCount( 20013 ), 0, 0, szOwnerAccountName, LOG::STR_NTS, szOwnerName, LOG::STR_NTS, "", 0, "", 0 ); } // 리젠되어 있는 몬스터/프랍/아이템 제거 clearMonsters(); clearHealingProps(); clearItems(); LOG::Log11N4S( LM_INSTANCE_DUNGEON_DESTROY, nOwnerAccountID, nOwnerUID, pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, nInstanceNo, nKey, 0, 0, 0, 0, 0, szOwnerAccountName, LOG::STR_NTS, szOwnerName, LOG::STR_NTS, "", 0, "", 0 ); return RESULT_SUCCESS; } const int InstanceDungeonManager::InstanceDungeonType::doEachPlayer( ArObjectFunctor & functor ) { for( std::vector< StructPlayer * >::iterator it = vPlayerList.begin() ; it != vPlayerList.end() ; ++it ) { functor( (*it) ); } return (int)vPlayerList.size(); } void InstanceDungeonManager::InstanceDungeonType::procMonsterRespawn( const AR_TIME & tCurrent ) { for( std::vector< PendedMonsterRespawnInfo >::iterator itRespawn = vPendedMonsterRespawn.begin() ; itRespawn != vPendedMonsterRespawn.end() ; /* 루프에서 ++itRespawn 처리 */ ) { if( (*itRespawn).second > tCurrent ) { ++itRespawn; continue; } for( int nIndex = 0 ; nIndex < (*itRespawn).first->nCount ; ++nIndex ) { int nRespawnX; int nRespawnY; int nRespawnTryCount = 0; do { nRespawnX = XRandom( static_cast< int >( (*itRespawn).first->bxArea.GetLeft() ), static_cast< int >( (*itRespawn).first->bxArea.GetRight() ) ); nRespawnY = XRandom( static_cast< int >( (*itRespawn).first->bxArea.GetTop() ), static_cast< int >( (*itRespawn).first->bxArea.GetBottom() ) ); if( ++nRespawnTryCount > 10 ) { FILELOG( "Unable to respawn monster in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn).first->nID ); _cprint( "Unable to respawn monster in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)\n", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn).first->nID ); nRespawnTryCount = 0; break; } } while( GameContent::IsBlocked( nRespawnX, nRespawnY ) ); if( !nRespawnTryCount ) continue; StructMonster * pMob = respawnMonster( nRespawnX, nRespawnY, nInstanceNo, (*itRespawn).first->nMonsterID, (*itRespawn).first->bWandering, 0, this, true, nDifficulty ); if( !pMob ) { FILELOG( "Unknown monster is to be respawned in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn).first->nID ); _cprint( "Unknown monster is to be respawned in instance dungeon. InstanceDungeonID(%d), InstanceID(%d), RespawnID(%d)\n", pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, (*itRespawn).first->nID ); continue; } pMob->SetInstanceRespawnID( (*itRespawn).first->nID ); mapRespawnedMonster.insert( std::pair< int , StructMonster* >( (*itRespawn).first->nRespawnGroup, pMob ) ); } itRespawn = vPendedMonsterRespawn.erase( itRespawn ); } } void InstanceDungeonManager::InstanceDungeonType::procHealingPropRespawn( const AR_TIME & tCurrent ) { for( std::vector< PendedHealingPropRespawnInfo >::iterator itRespawn = vPendedHealingPropRespawn.begin() ; itRespawn != vPendedHealingPropRespawn.end() ; /* 루프에서 ++itRespawn 처리 */ ) { if( (*itRespawn).tTime > tCurrent ) { ++itRespawn; continue; } StructFieldProp * pProp = NULL; { pProp = StructFieldProp::Create( this, (*itRespawn).pInfo, (*itRespawn).pInfo->x, (*itRespawn).pInfo->y, nInstanceNo ); } if( pProp ) { if( (*itRespawn).bStatic ) vRespawnedStaticProp.push_back( pProp ); else vRespawnedProp.push_back( pProp ); } itRespawn = vPendedHealingPropRespawn.erase( itRespawn ); } } void InstanceDungeonManager::InstanceDungeonType::setFlag( const std::string & strName , const std::string & strValue ) { m_mapFlags[ strName ] = strValue; } const std::string InstanceDungeonManager::InstanceDungeonType::getFlag( const std::string & strName ) const { std::map< std::string, std::string >::const_iterator iter = m_mapFlags.find( strName ); if( iter == m_mapFlags.end() ) return ""; return iter->second; } int InstanceDungeonManager::InstanceDungeonType::getAliveRespawnGroupMonsterCount( const int nRespawnGroup ) const { std::multimap< int, StructMonster * >::const_iterator iter_first = mapRespawnedMonster.lower_bound( nRespawnGroup ); std::multimap< int, StructMonster * >::const_iterator iter_last = mapRespawnedMonster.upper_bound( nRespawnGroup ); std::multimap< int, StructMonster * >::const_iterator iter_end = mapRespawnedMonster.end(); int nResultCount = 0; while( iter_first != iter_end && iter_first != iter_last ) { ++nResultCount; ++iter_first; } return nResultCount; } const unsigned char InstanceDungeonManager::InstanceDungeonInfo::GetNewInstanceNo() const { unsigned char nInstanceNo = 1; while( mapInstanceNo.find( nInstanceNo ) != mapInstanceNo.end() && nInstanceNo <= MAX_INSTANCE ) ++nInstanceNo; return nInstanceNo; } void InstanceDungeonManager::InstanceDungeonInfo::ProcPendedMonsterRespawn() { // [지역 락 -> csPendedRespawn / csInstanceDungeonType -> InstanceDungeonType::csInstanceDungeonType -> 지역 락] 지역 락으로 인한 데드락 발생 방지용 락 전략 std::vector< GameContent::PENDED_RESPAWN_INFO * > _vPendedRespawn; { THREAD_SYNCHRONIZE( csPendedRespawn ); if( vPendedRespawn.empty() ) return; _vPendedRespawn.swap( vPendedRespawn ); } THREAD_SYNCHRONIZE( csInstanceDungeonType ); for( std::vector< GameContent::PENDED_RESPAWN_INFO * >::const_iterator itRespawn = _vPendedRespawn.begin() ; itRespawn != _vPendedRespawn.end() ; ++itRespawn ) { GameContent::PENDED_RESPAWN_INFO * pRespawn = (*itRespawn); for( std::map< int, InstanceDungeonType * >::iterator itInstance = mapInstanceDungeonType.begin() ; itInstance != mapInstanceDungeonType.end() ; ++itInstance ) { InstanceDungeonType * pInstance = (*itInstance).second; if( pInstance->nInstanceNo != pRespawn->layer ) continue; THREAD_SYNCHRONIZE( pInstance->csInstanceDungeonType ); for( int nCountIdx = 0 ; nCountIdx < pRespawn->respawnCount ; ++nCountIdx ) { // 랜덤 위치 설정 AR_UNIT x, y; bool bValidPos = true; unsigned short try_cnt = 0; do { x = XRandom( pRespawn->x - 60, pRespawn->x + 60 ); y = XRandom( pRespawn->y - 60, pRespawn->y + 60 ); if( ++try_cnt > 300 ) { bValidPos = false; break; } } while( GameContent::CollisionToLine( pRespawn->x, pRespawn->y, x, y ) ); if( !bValidPos ) { x = pRespawn->x; y = pRespawn->y; if( GameContent::IsBlocked( x, y ) ) { FILELOG( "Unable to respawn monster in battle arena. InstanceID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)", pInstanceDungeonBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId ); _cprint( "Unable to respawn monster in battle arena. InstanceID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)\n", pInstanceDungeonBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId ); } } { StructMonster * pMob = respawnMonster( x, y, pRespawn->layer, pRespawn->monsterId, true, 0, pInstance, true, pInstance->nDifficulty ); if( !pMob ) { FILELOG( "Unknown monster is to be respawned in instance dungeon by pending. InstanceID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)", pInstanceDungeonBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId ); _cprint( "Unknown monster is to be respawned in instance dungeon by pending. InstanceID(%d), x(%d), y(%d), layer(%d), MonsterID(%d)\n", pInstanceDungeonBase->nID, pRespawn->x, pRespawn->y, pRespawn->layer, pRespawn->monsterId ); break; } if( pRespawn->initialEnemy ) pMob->AddHate( pRespawn->initialEnemy, 1 ); pMob->SetLifeTime( pRespawn->lifeTime ); //보스 몬스터가 스킬로 소환 하는 몬스터들은 키값을 0으로 설정한다. pInstance->mapRespawnedMonster.insert( std::pair< int , StructMonster* >( /*pRespawn->respawnGroup*/0 , pMob ) ); //pInstance->vRespawnedMonster.push_back( pMob ); } } } delete pRespawn; } } void InstanceDungeonManager::InstanceDungeonInfo::ProcPendedPropRespawn() { // [지역 락 -> csPendedRespawnProp / csInstanceDungeonType -> InstanceDungeonType::csInstanceDungeonType -> 지역 락] 지역 락으로 인한 데드락 발생 방지용 락 전략 std::vector< GameContent::FIELD_PROP_RESPAWN_INFO * > _vPendedRespawnProp; { THREAD_SYNCHRONIZE( csPendedRespawnProp ); if( vPendedRespawnProp.empty() ) return; _vPendedRespawnProp.swap( vPendedRespawnProp ); } THREAD_SYNCHRONIZE( csInstanceDungeonType ); for( std::vector< GameContent::FIELD_PROP_RESPAWN_INFO * >::const_iterator itRespawn = _vPendedRespawnProp.begin() ; itRespawn != _vPendedRespawnProp.end() ; ++itRespawn ) { GameContent::FIELD_PROP_RESPAWN_INFO * pRespawn = (*itRespawn); for( std::map< int, InstanceDungeonType * >::iterator itInstance = mapInstanceDungeonType.begin() ; itInstance != mapInstanceDungeonType.end() ; ++itInstance ) { InstanceDungeonType * pInstance = (*itInstance).second; if( pInstance->nInstanceNo != pRespawn->layer ) continue; THREAD_SYNCHRONIZE( pInstance->csInstanceDungeonType ); StructFieldProp * pProp = StructFieldProp::Create( pInstance, pRespawn, pRespawn->x, pRespawn->y, pInstance->nInstanceNo ); if( pProp ) { pInstance->vRespawnedProp.push_back( pProp ); } } } } ArcadiaLock InstanceDungeonManager::InstanceDungeonInfo::LockWholeDungeonArea( short layer ) const { // 데드락 발생 가능성 체크용 assert( !csInstanceDungeonType.IsLockedByCurrentThread() && !csPendedRespawn.IsLockedByCurrentThread() && !csPendedRespawnProp.IsLockedByCurrentThread() ); return ArcadiaServer::Instance().LockArea( pInstanceDungeonBase->nDungeonRegionLeft, pInstanceDungeonBase->nDungeonRegionTop, pInstanceDungeonBase->nDungeonRegionRight, pInstanceDungeonBase->nDungeonRegionBottom, layer ); } // InstanceDungeonManager InstanceDungeonManager::~InstanceDungeonManager() { if( IsInitialized() ) DeInit(); ClearInstanceDungeonInfo(); } InstanceDungeonManager & InstanceDungeonManager::Instance() { static InstanceDungeonManager _inst; return _inst; } bool InstanceDungeonManager::Init() { if( IsInitialized() ) return false; ArcadiaServer::Instance().SetObjectPriority( this, ArObject::UPDATE_PRIORITY_HIGH ); return true; } bool InstanceDungeonManager::DeInit() { if( !IsInitialized() ) return false; ArcadiaServer::Instance().SetObjectPriority( this, ArObject::UPDATE_PRIORITY_IDLE ); // 진행 중이던 인스턴스 던전이 있는 상태에서 종료 또는 뒷 처리를 해야할 경우 여기서 하면 됨 for( std::vector< InstanceDungeonInfo * >::iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { // 리젠 대기 중인 몬스터 데이터 제거 { THREAD_SYNCHRONIZE( (*it)->csPendedRespawn ); for( std::vector< GameContent::PENDED_RESPAWN_INFO * >::const_iterator itRespawn = (*it)->vPendedRespawn.begin() ; itRespawn != (*it)->vPendedRespawn.end() ; ++itRespawn ) { delete (*itRespawn); } (*it)->vPendedRespawn.clear(); } // 리젠 대기 중인 프랍 데이터 제거 { THREAD_SYNCHRONIZE( (*it)->csPendedRespawnProp ); for( std::vector< GameContent::FIELD_PROP_RESPAWN_INFO * >::const_iterator itRespawn = (*it)->vPendedRespawnProp.begin() ; itRespawn != (*it)->vPendedRespawnProp.end(); ++itRespawn ) { delete (*itRespawn); } (*it)->vPendedRespawnProp.clear(); } // 삭제되어야 할 던전 정보를 즉시 삭제하기 위해서 모으는 벡터 std::vector< InstanceDungeonType * > vPendedInstanceDungeonTypeToBeDeleted; { THREAD_SYNCHRONIZE( (*it)->csInstanceDungeonType ); // 삭제 대기 중인 던전들을 즉시 삭제할 목록에 추가 후 대기 리스트 초기화 vPendedInstanceDungeonTypeToBeDeleted.swap( (*it)->vPendedInstanceDungeonTypeToBeDeleted ); (*it)->mapInstanceNo.clear(); for( std::map< int, InstanceDungeonType * >::iterator itInstance = (*it)->mapInstanceDungeonType.begin() ; itInstance != (*it)->mapInstanceDungeonType.end() ; /* 루프에서 ++itInstance 처리 */ ) { InstanceDungeonType * pInstance = (*itInstance).second; THREAD_SYNCHRONIZE( pInstance->csInstanceDungeonType ); pInstance->endInstance(); vPendedInstanceDungeonTypeToBeDeleted.push_back( (*itInstance).second ); itInstance = (*it)->mapInstanceDungeonType.erase( itInstance ); } } for( std::vector< InstanceDungeonType * >::const_iterator itInstance = vPendedInstanceDungeonTypeToBeDeleted.begin() ; itInstance != vPendedInstanceDungeonTypeToBeDeleted.end() ; ++itInstance ) { // 삭제 전에 락을 한 번 걸었다 풀어서 다른 프로세스가 인스턴스 정보를 사용 중일 가능성을 낮춘 후에 삭제 // 그래봐야 여기서 거는 락 때문에 기다리고 있는 쓰레드가 생기면 말짱 꽝이지만, // 어차피 서버 내려가고 있는데 그딴 거 알 게 뭐삼! 걍 지워!(서버 종료되면서 불필요한 덤프가 남는 주 원인이 될 수도 있음... ``;) { THREAD_SYNCHRONIZE( (*itInstance)->csInstanceDungeonType ); } delete (*itInstance); } } return true; } const bool InstanceDungeonManager::IsInitialized() const { return GetFinalPriority() != ArSchedulerObject::UPDATE_PRIORITY_IDLE; } const bool InstanceDungeonManager::ClearInstanceDungeonInfo() { if( IsInitialized() ) return false; for( std::vector< InstanceDungeonInfo * >::const_iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { // beginInstance에서 몬스터/프랍 리젠 정보를 사용하지만 ClearInstanceDungeonInfo가 호출되는 상황(DeInit)이면 // beginInstance이 처리 중일리가 없으므로 락 안 걸고 삭제 for( std::vector< const GameContent::INSTANCE_DUNGEON_TYPE_BASE * >::const_iterator itInstance = (*it)->pInstanceDungeonBase->vInstanceDungeonTypeBase.begin() ; itInstance != (*it)->pInstanceDungeonBase->vInstanceDungeonTypeBase.end() ; ++itInstance ) { for( std::vector< const GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO * >::const_iterator itRespawn = (*itInstance)->vRespawnInfo.begin() ; itRespawn != (*itInstance)->vRespawnInfo.end() ; ++itRespawn ) { delete (*itRespawn); } for( std::vector< const GameContent::FIELD_PROP_RESPAWN_INFO * >::const_iterator itHealingProp = (*itInstance)->vHealingPropInfo.begin() ; itHealingProp != (*itInstance)->vHealingPropInfo.end() ; ++itHealingProp ) { delete (*itHealingProp); } delete (*itInstance); } delete (*it); } m_vInstanceDungeonInfo.clear(); return true; } const bool InstanceDungeonManager::RegisterInstanceDungeonBase( const GameContent::INSTANCE_DUNGEON_BASE * pInstanceDungeonBase ) { assert( !IsInitialized() ); #ifdef _DEBUG // 디버그 모드일 때만 이미 등록된 인스턴스 던전 정보인지 확인 for( std::vector< InstanceDungeonInfo * >::const_iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { if( (*it)->pInstanceDungeonBase->nID == pInstanceDungeonBase->nID ) return false; } #endif m_vInstanceDungeonInfo.push_back( new InstanceDungeonInfo( pInstanceDungeonBase ) ); return true; } const InstanceDungeonManager::InstanceDungeonInfo * InstanceDungeonManager::findInstanceDungeonInfo( const ArPosition & pos ) const { for( std::vector< InstanceDungeonInfo * >::const_iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { unsigned int nRegionX = pos.GetRX(); unsigned int nRegionY = pos.GetRY(); if( nRegionX >= (*it)->pInstanceDungeonBase->nDungeonRegionLeft && nRegionX <= (*it)->pInstanceDungeonBase->nDungeonRegionRight && nRegionY >= (*it)->pInstanceDungeonBase->nDungeonRegionTop && nRegionY <= (*it)->pInstanceDungeonBase->nDungeonRegionBottom ) return (*it); } return NULL; } const InstanceDungeonManager::InstanceDungeonInfo * InstanceDungeonManager::findInstanceDungeonInfo( int nInstanceDungeonID ) const { for( std::vector< InstanceDungeonInfo * >::const_iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { if( (*it)->pInstanceDungeonBase->nID != nInstanceDungeonID ) continue; return (*it); } return NULL; } InstanceDungeonManager::InstanceDungeonInfo * InstanceDungeonManager::findInstanceDungeonInfo( int nInstanceDungeonID ) { for( std::vector< InstanceDungeonInfo * >::iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { if( (*it)->pInstanceDungeonBase->nID != nInstanceDungeonID ) continue; return (*it); } return NULL; } const InstanceDungeonManager::InstanceDungeonType * InstanceDungeonManager::findInstanceDungeonType( const InstanceDungeonInfo * pInstanceDungeonInfo, const unsigned char nLayer ) const { typedef std::map< unsigned char, InstanceDungeonType * >::const_iterator mapIter; mapIter it = pInstanceDungeonInfo->mapInstanceNo.find( nLayer ); if( it != pInstanceDungeonInfo->mapInstanceNo.end() ) { return it->second; } return NULL; } InstanceDungeonManager::InstanceDungeonType * InstanceDungeonManager::findInstanceDungeonType( InstanceDungeonInfo * pInstanceDungeonInfo, const unsigned char nLayer ) { typedef std::map< unsigned char, InstanceDungeonType * >::iterator mapIter; mapIter it = pInstanceDungeonInfo->mapInstanceNo.find( nLayer ); if( it != pInstanceDungeonInfo->mapInstanceNo.end() ) { return it->second; } return NULL; } const int InstanceDungeonManager::GetInstanceDungeonID( const ArPosition & pos ) const { const InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( pos ); return ( pInstanceDungeonInfo != NULL ) ? pInstanceDungeonInfo->pInstanceDungeonBase->nID : 0; } const int InstanceDungeonManager::GetInstanceDungeonTypeID( int nInstanceDungeonID, const unsigned char nLayer ) const { const InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return -1; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); const InstanceDungeonType * pInstanceDungeonType = findInstanceDungeonType( pInstanceDungeonInfo, nLayer ); return ( pInstanceDungeonType != NULL ) ? pInstanceDungeonType->pInstanceDungeonTypeBase->nID : -1; } const bool InstanceDungeonManager::GetInstanceDungeonPosition( const int nInstanceDungeonID, ArPosition & pos ) const { const InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return false; pos = pInstanceDungeonInfo->pInstanceDungeonBase->posDungeon; return true; } const bool InstanceDungeonManager::SetInstanceDungeonTypeFlag( int nInstanceDungeonID, const unsigned char nLayer, const std::string & strName , const std::string & strValue ) { InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return false; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); InstanceDungeonType * pInstanceDungeonType = findInstanceDungeonType( pInstanceDungeonInfo, nLayer ); if( !pInstanceDungeonType ) return false; THREAD_SYNCHRONIZE1( pInstanceDungeonType->csInstanceDungeonType ); pInstanceDungeonType->setFlag( strName, strValue ); return true; } std::string InstanceDungeonManager::GetInstanceDungeonTypeFlag( int nInstanceDungeonID, const unsigned char nLayer, const std::string & strName ) const { const InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return ""; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); const InstanceDungeonType * pInstanceDungeonType = findInstanceDungeonType( pInstanceDungeonInfo, nLayer ); if( !pInstanceDungeonType ) return ""; THREAD_SYNCHRONIZE1( pInstanceDungeonType->csInstanceDungeonType ); // InstanceDungeonType::getFlag 함수의 리턴 타입은 const std::string & 인 반면 이 함수의 리턴 타입은 std::string 임. // 이 함수에서 리턴되는 순간 인던 관련 모든 락은 풀리고 검색된 std::string 의 인스턴스는 메모리에서 삭제될 수도 있음. // 따라서 InstanceDungeonManager 내부에서 사용되는 함수인 InstanceDungeonType::getFlag는 락을 관리하여 참조자의 유효성을 보장할 수 있지만 // 이 함수는 유효성을 보장하지 못하므로 찾아진 const std::string & 의 복사본을 리턴해야 함. // 또한 이 함수의 리턴값인 std::string을 호출자 측에서 참조자로 받아서 내용을 수정해서 사용하는 건 자유이므로(리턴값은 호출자 측 마음대로~) const를 안 붙임. return pInstanceDungeonType->getFlag( strName ); } const int InstanceDungeonManager::GetInstanceDungeonDifficulty( const int nInstanceDungeonID, const unsigned char nLayer ) const { const InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return -1; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); const InstanceDungeonType * pType = findInstanceDungeonType( pInstanceDungeonInfo, nLayer ); if( !pType ) return -1; // 여기서 Difficulty란 인스턴스 던전의 nID를 말함. 네이밍을 좀 더 직관적으로 수정해야 될 듯 // pType->nDifficulty 라는 값을 현재 안 쓰고 있다고 한다. DB에는 있는 값이니 컬럼 삭제와 함께 네이밍 리팩토링이 필요할 듯 return pType->pInstanceDungeonTypeBase->nID; } const int InstanceDungeonManager::GetAliveInstanceDungeonRespawnGroupMonsterCount( int nInstanceDungeonID, const unsigned char nLayer, const int nRespawnGroup ) const { const InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return -1; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); const InstanceDungeonType * pInstanceDungeonType = findInstanceDungeonType( pInstanceDungeonInfo, nLayer ); if( !pInstanceDungeonType ) return -1; THREAD_SYNCHRONIZE1( pInstanceDungeonType->csInstanceDungeonType ); return pInstanceDungeonType->getAliveRespawnGroupMonsterCount( nRespawnGroup ); } const unsigned short InstanceDungeonManager::CreateInstanceDungeon( int nInstanceDungeonID, struct StructPlayer * pPlayer, int nType, const unsigned char nDifficulty ) { if( GetInstanceDungeonID( pPlayer->GetPos() ) ) return RESULT_NOT_ACTABLE_IN_INSTANCE_DUNGEON; if( pPlayer->GetBattleArenaID() || pPlayer->IsInBattleArena() ) return RESULT_NOT_ACTABLE_IN_BATTLE_ARENA; InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return RESULT_NOT_ACTABLE; const GameContent::INSTANCE_DUNGEON_BASE * pInstanceDungeonBase = pInstanceDungeonInfo->pInstanceDungeonBase; unsigned char nInstanceNo = 0; { // 여기선 몇 번 레이어에 새 인스턴스 던전이 할당될지 미리 예상할 수 없으므로 전체 레이어에 락을 걸어 줌 ARCADIA_LOCK( pInstanceDungeonInfo->LockWholeDungeonArea() ); const char * pszScriptOnCreate = NULL; const GameContent::INSTANCE_DUNGEON_TYPE_BASE * pInstanceDungeonTypeBase = NULL; { THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); if( pInstanceDungeonInfo->IsInstanceFull() ) return RESULT_LIMIT_MAX; for( std::vector< const GameContent::INSTANCE_DUNGEON_TYPE_BASE * >::const_iterator itInstanceDungeonTypeBase = pInstanceDungeonBase->vInstanceDungeonTypeBase.begin(); itInstanceDungeonTypeBase != pInstanceDungeonBase->vInstanceDungeonTypeBase.end(); ++itInstanceDungeonTypeBase ) { if( nType >= 0 && (*itInstanceDungeonTypeBase)->nID != nType ) { continue; } if( (*itInstanceDungeonTypeBase)->IsProperLevel( pPlayer->GetLevel() ) ) { pInstanceDungeonTypeBase = (*itInstanceDungeonTypeBase); break; } } if( !pInstanceDungeonTypeBase ) return RESULT_NOT_ACTABLE; std::map< int, InstanceDungeonType * >::iterator itInstance; int nKey = 0; if( pPlayer->IsInParty() ) { nKey = pPlayer->GetPartyID(); // 파티 인던이 우선 순위가 높다, 솔로 인던이 있었다면 사라진다. itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.find( -pPlayer->GetPlayerUID() ); if( itInstance != pInstanceDungeonInfo->mapInstanceDungeonType.end() && itInstance->second->tDestructionTime > GetArTime() ) { itInstance->second->endInstance(); pInstanceDungeonInfo->vPendedInstanceDungeonTypeToBeDeleted.push_back( itInstance->second ); pInstanceDungeonInfo->mapInstanceDungeonType.erase( itInstance ); } itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.find( nKey ); } else { nKey = -pPlayer->GetPlayerUID(); itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.find( nKey ); } bool bDelete = false; InstanceDungeonType * pInstanceDungeonType = NULL; if( itInstance != pInstanceDungeonInfo->mapInstanceDungeonType.end() && itInstance->second->tDestructionTime > GetArTime() ) { // 찾았지만 난이도가 다른 경우, 기존의 던전을 삭제한다 if( (*itInstance).second->nDifficulty != nDifficulty ) bDelete = true; else pInstanceDungeonType = itInstance->second; } if( !pInstanceDungeonType ) { nInstanceNo = pInstanceDungeonInfo->GetNewInstanceNo(); if( nInstanceNo > MAX_INSTANCE ) return RESULT_LIMIT_MAX; // 인던을 생성하고 초기화를 한다 // 만약 입장 조건을 만족하지 못 한다면 취소된다 pInstanceDungeonType = new InstanceDungeonType( pInstanceDungeonInfo, pInstanceDungeonTypeBase, nInstanceNo, nKey, pPlayer->GetAccountID(), pPlayer->GetSID(), pPlayer->GetAccountName(), pPlayer->GetName(), nDifficulty ); unsigned short nResult = pInstanceDungeonType->beginInstance( pPlayer ); if( nResult != RESULT_SUCCESS ) { delete pInstanceDungeonType; return nResult; } // 입장에 성공했다면 이전 인던을 제거한다 if( bDelete ) { itInstance->second->endInstance(); pInstanceDungeonInfo->vPendedInstanceDungeonTypeToBeDeleted.push_back( itInstance->second ); pInstanceDungeonInfo->mapInstanceDungeonType.erase( itInstance ); } pInstanceDungeonInfo->mapInstanceNo.insert( std::make_pair( nInstanceNo, pInstanceDungeonType ) ); pInstanceDungeonInfo->mapInstanceDungeonType.insert( std::make_pair( nKey, pInstanceDungeonType ) ); // 인스턴스 생성 시 실행될 스크립트 세팅 pszScriptOnCreate = pInstanceDungeonTypeBase->szScriptOnCreate; } } // 인스턴스 생성 시 실행될 스크립트 호출 char szScriptBuffer[ 256 ] = { 0, }; if( pszScriptOnCreate ) { s_strcpy( szScriptBuffer, _countof( szScriptBuffer ), pszScriptOnCreate ); char szElementBuffer[ 32 ]; s_sprintf( szElementBuffer, _countof( szElementBuffer ), "%d", (int)nInstanceNo ); XStringUtil::Replace( szScriptBuffer, _countof( szScriptBuffer ), "#@layer@#", szElementBuffer ); LUA()->RunString( szScriptBuffer ); // 인스턴스 생성 시에는 참여 중인 유저가 없는 상태이기 때문에 유저들의 데이터 변동에 대한 로그(LM_INSTANCE_DUNGEON_PLAYER_INFO_CHANGE)를 남길 필요가 없음 } LOG::Log11N4S( LM_INSTANCE_DUNGEON_PROCESS, 0, 0, pInstanceDungeonBase->nID, pInstanceDungeonTypeBase->nID, nInstanceNo, 0, 0, 0, 0, 0, 0, "", 0, "", 0, szScriptBuffer, LOG::STR_NTS, "INSTANCE_CREATE", LOG::STR_NTS ); } if( pInstanceDungeonBase ) { ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) ); pPlayer->PendWarp( pInstanceDungeonBase->posDungeon.x, pInstanceDungeonBase->posDungeon.y, nInstanceNo ); ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); } return RESULT_SUCCESS; } const unsigned short InstanceDungeonManager::JoinInstanceDungeon( int nInstanceDungeonID, struct StructPlayer * pPlayer, ArPosition & posEnter, unsigned char & nEnterLayer, bool bStorePosition ) { if( pPlayer->GetBattleArenaID() || pPlayer->IsInBattleArena() ) return RESULT_NOT_ACTABLE_IN_BATTLE_ARENA; if( bStorePosition ) { pPlayer->StoreCurrentStatesOnEnterInstanceGame( true ); } InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return RESULT_NOT_ACTABLE; InstanceDungeonType * pInstanceDungeonType = NULL; const char * pszScriptOnJoin = NULL; unsigned char nLayer = 0; { THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); // 파티 인던이 우선 순위가 높다 int nKey = pPlayer->IsInParty() ? pPlayer->GetPartyID() : -pPlayer->GetPlayerUID(); std::map< int, InstanceDungeonType * >::iterator itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.find( nKey ); if( itInstance == pInstanceDungeonInfo->mapInstanceDungeonType.end() || ( itInstance->second->tDestructionTime && itInstance->second->tDestructionTime <= GetArTime() ) ) return RESULT_NOT_ACTABLE; pInstanceDungeonType = (*itInstance).second; THREAD_SYNCHRONIZE1( pInstanceDungeonType->csInstanceDungeonType ); unsigned short nErrorCode = pInstanceDungeonType->joinInstance( pPlayer ); if( nErrorCode != RESULT_SUCCESS ) return nErrorCode; // 인스턴스에 참여 시 실행될 스크립트 세팅 pszScriptOnJoin = pInstanceDungeonType->pInstanceDungeonTypeBase->szScriptOnJoin; nLayer = pInstanceDungeonType->nInstanceNo; LOG::Log11N4S( LM_INSTANCE_DUNGEON_JOIN, pPlayer->GetAccountID(), pPlayer->GetSID(), nInstanceDungeonID, pInstanceDungeonType->pInstanceDungeonTypeBase->nID, pInstanceDungeonType->nInstanceNo, nKey, 0, 0, pInstanceDungeonType->nPlayerCount, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 ); } // 인스턴스에 참여 시 실행될 스크립트 실행 char szScriptBuffer[ 256 ] = { 0, }; if( pszScriptOnJoin && pszScriptOnJoin[ 0 ] ) { s_strcpy( szScriptBuffer, _countof( szScriptBuffer ), pszScriptOnJoin ); char szElementBuffer[ 32 ]; s_sprintf( szElementBuffer, _countof( szElementBuffer ), "%d", (int)nLayer ); XStringUtil::Replace( szScriptBuffer, _countof( szScriptBuffer ), "#@layer@#", szElementBuffer ); ARCADIA_LOCK( pInstanceDungeonInfo->LockWholeDungeonArea( nLayer ) ); PlayerBasicInfoForLog prevPBI( pPlayer ); LUA()->RunString( szScriptBuffer ); PlayerBasicInfoForLog currentPBI( pPlayer ); if( prevPBI != currentPBI ) { LOG::Log11N4S( LM_INSTANCE_DUNGEON_PLAYER_INFO_CHANGE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonType->pInstanceDungeonTypeBase->nID, pInstanceDungeonType->nInstanceNo, prevPBI.gold.GetRawData(), currentPBI.gold.GetRawData(), prevPBI.exp, currentPBI.exp, prevPBI.jp, currentPBI.jp, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, szScriptBuffer, LOG::STR_NTS, "USER_JOIN", LOG::STR_NTS ); } } LOG::Log11N4S( LM_INSTANCE_DUNGEON_PROCESS, 0, 0, pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonType->pInstanceDungeonTypeBase->nID, pInstanceDungeonType->nInstanceNo, 0, 0, 0, 0, 0, 0, "", 0, "", 0, szScriptBuffer, LOG::STR_NTS, "USER_JOIN", LOG::STR_NTS ); // 함수 결과값으로 돌려줘야 할 입장 대상 위치값 세팅 nEnterLayer = nLayer; // 만일 bStorePosition == false 였다면, 좌표를 바꾸지 않고 입장시키는 경우(Ex. 로그인)이므로 좌표값을 캐릭터 현재 좌표로 복사해 줌. if( bStorePosition ) posEnter = pInstanceDungeonInfo->pInstanceDungeonBase->posDungeon; else posEnter = pPlayer->GetPos(); return RESULT_SUCCESS; } const unsigned short InstanceDungeonManager::LeaveInstanceDungeon( int nInstanceDungeonID, struct StructPlayer * pPlayer ) { int nPlayerInstanceDungeonID = GetInstanceDungeonID( pPlayer->GetPos() ); if( !nPlayerInstanceDungeonID ) return RESULT_ACTABLE_IN_ONLY_INSTANCE_DUNGEON; assert( nPlayerInstanceDungeonID == nInstanceDungeonID ); InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return RESULT_NOT_ACTABLE; const char * pszScriptOnLeave = NULL; unsigned char nLayer = 0; // 방에 남아있던 마지막 유저가 방에서 이탈(leaveInstance)하고 csInstanceDungeonType이 풀리면 // 스크립트를 실행하기 전에 onProcess에서 해당 방을 endInstance & delete 해 버릴 수 있음. // 이를 방지하기 위해 여기서 지역락을 먼저 걸어놓고 // 이탈 처리에 이어 스크립트 실행까지 완료한 후에 지역 락을 풀어서 방이 실제로 삭제될 수 있도록 풀어 줌. // * 동기화 요소를 추가하면 추가할 수록 성능이 떨어지므로 여기는 추후에 방의 상태 변수를 두고 // 파괴되어도 되는 상태를 leaveInstance에서 세팅하지 말고 스크립트 실행 후에 만들도록 수정하고, // 지역락은 스크립트 실행하는 블럭에서만 걸리도록 수정해야 함. // 현재 여기서는 레이어 번호를 결정지을 수도 없어서 전체 레이어에 락을 걸어야 하지만, // 위와 같이 수정하고 나면 스크립트 실행 시점에 유저가 이탈한 방의 레이어 번호를 알 수 있으므로 레이어를 지정해서 락을 걸어야 함. ARCADIA_LOCK( pInstanceDungeonInfo->LockWholeDungeonArea() ); InstanceDungeonType * pInstanceDungeonType = NULL; { THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); // 파티 인던이 우선 순위가 높다 int nKey = pPlayer->IsInParty() ? pPlayer->GetPartyID() : -pPlayer->GetPlayerUID(); std::map< int, InstanceDungeonType * >::iterator itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.find( nKey ); if( itInstance == pInstanceDungeonInfo->mapInstanceDungeonType.end() ) { // 강제 입장을 통해 인던에 입장한 경우 멤버 정보가 없어 여기 올 수 있다. return RESULT_NOT_ACTABLE; } pInstanceDungeonType = (*itInstance).second; THREAD_SYNCHRONIZE1( pInstanceDungeonType->csInstanceDungeonType ); unsigned short nErrorCode = pInstanceDungeonType->leaveInstance( pPlayer ); if( nErrorCode != RESULT_SUCCESS ) return nErrorCode; // 인스턴스에서 이탈 시 실행될 스크립트 세팅 pszScriptOnLeave = pInstanceDungeonType->pInstanceDungeonTypeBase->szScriptOnLeave; nLayer = pInstanceDungeonType->nInstanceNo; LOG::Log11N4S( LM_INSTANCE_DUNGEON_LEAVE, pPlayer->GetAccountID(), pPlayer->GetSID(), nInstanceDungeonID, pInstanceDungeonType->pInstanceDungeonTypeBase->nID, pInstanceDungeonType->nInstanceNo, nKey, 0, 0, pInstanceDungeonType->nPlayerCount, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 ); } // 인스턴스에서 이탈 시 실행될 스크립트 실행 char szScriptBuffer[ 256 ] = { 0, }; if( pszScriptOnLeave && pszScriptOnLeave[ 0 ] ) { s_strcpy( szScriptBuffer, _countof( szScriptBuffer ), pszScriptOnLeave ); char szElementBuffer[ 32 ]; s_sprintf( szElementBuffer, _countof( szElementBuffer ), "%d", (int)nLayer ); XStringUtil::Replace( szScriptBuffer, _countof( szScriptBuffer ), "#@layer@#", szElementBuffer ); PlayerBasicInfoForLog prevPBI( pPlayer ); LUA()->RunString( szScriptBuffer ); PlayerBasicInfoForLog currentPBI( pPlayer ); if( prevPBI != currentPBI ) { LOG::Log11N4S( LM_INSTANCE_DUNGEON_PLAYER_INFO_CHANGE, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonType->pInstanceDungeonTypeBase->nID, pInstanceDungeonType->nInstanceNo, prevPBI.gold.GetRawData(), currentPBI.gold.GetRawData(), prevPBI.exp, currentPBI.exp, prevPBI.jp, currentPBI.jp, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, szScriptBuffer, LOG::STR_NTS, "USER_LEAVE", LOG::STR_NTS ); } } LOG::Log11N4S( LM_INSTANCE_DUNGEON_PROCESS, 0, 0, pInstanceDungeonInfo->pInstanceDungeonBase->nID, pInstanceDungeonType->pInstanceDungeonTypeBase->nID, pInstanceDungeonType->nInstanceNo, 0, 0, 0, 0, 0, 0, "", 0, "", 0, szScriptBuffer, LOG::STR_NTS, "USER_LEAVE", LOG::STR_NTS ); return RESULT_SUCCESS; } const unsigned short InstanceDungeonManager::QuitInstanceDungeon( int nInstanceDungeonID, struct StructPlayer * pPlayer ) { int nPlayerInstanceDungeonID = GetInstanceDungeonID( pPlayer->GetPos() ); if( !nPlayerInstanceDungeonID ) return RESULT_ACTABLE_IN_ONLY_INSTANCE_DUNGEON; assert( nPlayerInstanceDungeonID == nInstanceDungeonID ); InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return RESULT_NOT_ACTABLE; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); // 파티 인던이 우선 순위가 높다 int nKey = pPlayer->IsInParty() ? pPlayer->GetPartyID() : -pPlayer->GetPlayerUID(); std::map< int, InstanceDungeonType * >::iterator itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.find( nKey ); if( itInstance == pInstanceDungeonInfo->mapInstanceDungeonType.end() ) { assert( 0 ); return RESULT_NOT_ACTABLE; } InstanceDungeonType * pInstanceDungeonType = (*itInstance).second; if( pInstanceDungeonType->nKey > 0 ) { int nPartyID = pInstanceDungeonType->nKey; QuitFunctor quit; PartyManager::GetInstance().DoEachMember( nPartyID, quit ); } else { ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) ); ArPosition pos; pPlayer->GetPositionOnEnterInstanceGame( &pos ); unsigned char nLayer = 0; int current_channel = ChannelManager::GetChannelId( pPlayer->GetX(), pPlayer->GetY() ); int target_channel = ChannelManager::GetChannelId( pos.x, pos.y ); if( current_channel && current_channel == target_channel ) { nLayer = pPlayer->GetLayer(); } else if( target_channel ) { nLayer = ChannelManager::GetProperLayer( pos.x, pos.y ); } pPlayer->PendWarp( pos.GetX(), pos.GetY(), nLayer ); ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); } return RESULT_SUCCESS; } const int InstanceDungeonManager::DoEachPlayerInInstanceDungeon( int nInstanceDungeonID, const unsigned char nLayer, ArObjectFunctor & functor ) { InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return 0; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); InstanceDungeonType * pInstanceDungeonType = findInstanceDungeonType( pInstanceDungeonInfo, nLayer ); if( !pInstanceDungeonType ) return 0; THREAD_SYNCHRONIZE1( pInstanceDungeonType->csInstanceDungeonType ); return pInstanceDungeonType->doEachPlayer( functor ); } void InstanceDungeonManager::PendRespawnMonster( const int nInstanceDungeonID, const AR_UNIT x, const AR_UNIT y, const unsigned char nLayer, const int nMonsterID, const int nRespawnCount, const AR_TIME nLifeTime, const AR_HANDLE hInitialEnemy ) { for( std::vector< InstanceDungeonInfo * >::iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { if( (*it)->pInstanceDungeonBase->nID != nInstanceDungeonID ) continue; // 지역 락을 걸고 호출되는 함수이므로 csInstanceDungeon을 걸면 안됨 { THREAD_SYNCHRONIZE( (*it)->csPendedRespawn ); (*it)->vPendedRespawn.push_back( new GameContent::PENDED_RESPAWN_INFO( x, y, nLayer, nMonsterID, nRespawnCount, true, nLifeTime, hInitialEnemy ) ); } return; } assert( 0 ); } bool InstanceDungeonManager::PendRespawnMonster( const int nRespawnID, const int nInstanceDungeonID, const int nLayer ) { InstanceDungeonInfo * pInstanceDungeonInfo = findInstanceDungeonInfo( nInstanceDungeonID ); if( !pInstanceDungeonInfo ) return false; THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); InstanceDungeonType * pInstanceDungeonType = findInstanceDungeonType( pInstanceDungeonInfo, nLayer ); if( !pInstanceDungeonType ) return false; THREAD_SYNCHRONIZE1( pInstanceDungeonType->csInstanceDungeonType ); const GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO * pRespawnInfo = NULL; for( std::vector< const GameContent::INSTANCE_DUNGEON_MONSTER_RESPAWN_INFO * >::const_iterator itRespawn = pInstanceDungeonType->pInstanceDungeonTypeBase->vRespawnInfo.begin() ; itRespawn != pInstanceDungeonType->pInstanceDungeonTypeBase->vRespawnInfo.end() ; ++itRespawn ) { if( (*itRespawn)->nID != nRespawnID ) continue; pRespawnInfo = (*itRespawn); break; } if( !pRespawnInfo ) return false; pInstanceDungeonType->vPendedMonsterRespawn.push_back( InstanceDungeonType::PendedMonsterRespawnInfo( pRespawnInfo, GetArTime() ) ); return true; } void InstanceDungeonManager::PendRespawnProp( const int nInstanceDungeonID, const int _nPropId, const AR_UNIT _x, const AR_UNIT _y, const unsigned char _layer, const float _fZOffset, const float _fRotateX, const float _fRotateY, const float _fRotateZ, const float _fScaleX, const float _fScaleY, const float _fScaleZ, const bool _bLockHeight, const float _fLockHeight ) { for( std::vector< InstanceDungeonInfo * >::iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { if( (*it)->pInstanceDungeonBase->nID != nInstanceDungeonID ) continue; // 지역 락을 걸고 호출되는 함수이므로 csInstanceDungeon을 걸면 안됨 { THREAD_SYNCHRONIZE( (*it)->csPendedRespawnProp ); (*it)->vPendedRespawnProp.push_back( new GameContent::FIELD_PROP_RESPAWN_INFO( _nPropId, _x, _y, _layer, _fZOffset, _fRotateX, _fRotateY, _fRotateZ, _fScaleX, _fScaleY, _fScaleZ, _bLockHeight, _fLockHeight ) ); } return; } assert( 0 ); } void InstanceDungeonManager::onProcess( int nThreadIdx ) { char buf[255]; s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx ); ENV().Set( buf, "InstanceDungeonManager" ); AR_TIME t = GetArTime(); s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "InstanceDungeonManager(0x%08X)", (UINT_PTR)this ); s_ThreadInfo.last_execute_time = t; if( !IsInitialized() ) return; for( std::vector< InstanceDungeonInfo * >::iterator it = m_vInstanceDungeonInfo.begin() ; it != m_vInstanceDungeonInfo.end() ; ++it ) { InstanceDungeonInfo * pInstanceDungeonInfo = (*it); // 모든 생성되어 있는 인스턴스에 대한 처리를 해야 하므로 전체 레이어에 대해 락을 걸어 줌 ARCADIA_LOCK( pInstanceDungeonInfo->LockWholeDungeonArea() ); { THREAD_SYNCHRONIZE( pInstanceDungeonInfo->csInstanceDungeonType ); // 삭제 대기 중인 인스턴스 던전 삭제 처리 // * 현재 생성되어 있는 방들을 처리하기 전에 이걸 먼저 처리함으로써 // 이번 onProcess에서 삭제 대기 상태가 된 인스턴스를 다음 onProcess에서 삭제하도록 여유 시간을 발생시킴. for( std::vector< InstanceDungeonType * >::iterator itInstance = pInstanceDungeonInfo->vPendedInstanceDungeonTypeToBeDeleted.begin() ; itInstance != pInstanceDungeonInfo->vPendedInstanceDungeonTypeToBeDeleted.end() ; ++itInstance ) { // 삭제 전에 락을 한 번 걸었다 풀어서 다른 프로세스가 인스턴스 정보를 사용 중일 가능성을 낮춘 후에 삭제 // 그래봐야 여기서 거는 락 때문에 기다리고 있는 쓰레드가 생기면 말짱 꽝이지만, // 인스턴스 정보가 vPendedInstanceDungeonToBeDelete 리스트에 추가되고 여기까지 도달하기 전에 어지간한 것들은 다 종료되기에 충분할 것으로 가정 { THREAD_SYNCHRONIZE( (*itInstance)->csInstanceDungeonType ); } InstanceDungeonType * pInstance = *itInstance; pInstanceDungeonInfo->mapInstanceNo.erase( pInstance->nInstanceNo ); delete pInstance; } pInstanceDungeonInfo->vPendedInstanceDungeonTypeToBeDeleted.clear(); // 현재 생성되어 있는 인스턴스 던전 처리 for( std::map< int, InstanceDungeonType * >::iterator itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.begin() ; itInstance != pInstanceDungeonInfo->mapInstanceDungeonType.end() ; /* 루프에서 ++itInstance 처리 */ ) { InstanceDungeonType * pInstance = (*itInstance).second; THREAD_SYNCHRONIZE( pInstance->csInstanceDungeonType ); // 아무도 없는 방 제거 if( itInstance->second->tDestructionTime && pInstance->tDestructionTime <= t ) { pInstance->endInstance(); itInstance = pInstanceDungeonInfo->mapInstanceDungeonType.erase( itInstance ); pInstanceDungeonInfo->vPendedInstanceDungeonTypeToBeDeleted.push_back( pInstance ); continue; } // 종료랑 관계 없는 방이면 리젠 처리 pInstance->procMonsterRespawn( t ); pInstance->procHealingPropRespawn( t ); ++itInstance; } } // 리젠 대기 상태인 몬스터를 리젠 처리 // 위의 처리가 끝나고 하는 이유는 종료될 방은 종료시키고 나서 해야 쓸데없는 몹 리젠/삭제가 반복되지 않기 때문 pInstanceDungeonInfo->ProcPendedMonsterRespawn(); pInstanceDungeonInfo->ProcPendedPropRespawn(); } }