383 lines
12 KiB
C++
383 lines
12 KiB
C++
|
|
#include <toolkit/XEnv.h>
|
|
#include <dump/XException.h>
|
|
#include <toolkit/XConsole.h>
|
|
#include <logging/FileLog.h>
|
|
|
|
#include "CommunityLoader.h"
|
|
#include "PartyManager.h"
|
|
#include "GameDBUtil.h"
|
|
#include "DB_Commands.h"
|
|
#include "GuildManager.h"
|
|
#include "DungeonManager.h"
|
|
#include "DBPerformanceTracker.h"
|
|
#include "ADOConnection.h"
|
|
|
|
struct dbParty : public CADORecordBinding
|
|
{
|
|
BEGIN_ADO_BINDING(dbParty)
|
|
|
|
ADO_VARIABLE_LENGTH_ENTRY4(1, adInteger, party_sid, sizeof(party_sid), FALSE)
|
|
ADO_VARIABLE_LENGTH_ENTRY4(2, adVarWChar, szPartyName, _countof(szPartyName) - 1, FALSE)
|
|
ADO_VARIABLE_LENGTH_ENTRY4(3, adInteger, leader_sid, sizeof(leader_sid), FALSE)
|
|
ADO_VARIABLE_LENGTH_ENTRY4(4, adInteger, share_mode, sizeof(share_mode), FALSE)
|
|
ADO_VARIABLE_LENGTH_ENTRY4(5, adInteger, party_type, sizeof(party_type), FALSE)
|
|
ADO_VARIABLE_LENGTH_ENTRY4(6, adInteger, lead_party_id, sizeof(lead_party_id), FALSE)
|
|
|
|
END_ADO_BINDING()
|
|
|
|
int party_sid;
|
|
wchar_t szPartyName[61];
|
|
int leader_sid;
|
|
int share_mode;
|
|
int party_type;
|
|
int lead_party_id;
|
|
};
|
|
|
|
bool PartyLoader::onProcess( int nThreadNum )
|
|
{
|
|
if( !PartyManager::GetInstance().Init() )
|
|
{
|
|
_cprint( "Party load error!\n" );
|
|
FILELOG( "Party load error!" );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int get_guild_id( _ConnectionPtr & connection, int leader_sid )
|
|
{
|
|
_CommandPtr command;
|
|
if( CreateDBCommand( command, connection ) == false )
|
|
{
|
|
throw XException( "ADO COMMAND_PTR CREATE ERROR!" );
|
|
}
|
|
|
|
command->CommandType = adCmdStoredProc;
|
|
command->CommandText = _bstr_t( "dbo.smp_get_guild_id" );
|
|
|
|
command->Parameters->Append( command->CreateParameter( "IN_PLAYER_SID", adInteger, adParamInput, sizeof( leader_sid ), leader_sid ) );
|
|
command->Parameters->Append( command->CreateParameter( "OUT_GUILD_ID", adInteger, adParamOutput, 4, 0 ) );
|
|
|
|
_RecordsetPtr rs;
|
|
|
|
DBPerformanceTrackHelper helper;
|
|
try
|
|
{
|
|
helper.start();
|
|
rs = command->Execute( NULL, NULL, adCmdStoredProc );
|
|
helper.end( "dbo.smp_get_guild_id" );
|
|
}
|
|
catch( _com_error &e )
|
|
{
|
|
helper.end( e.Error(), "dbo.smp_get_guild_id" );
|
|
LogDBError( e, "get_guild_id()", "dbo.smp_get_guild_id" );
|
|
|
|
std::string strError = "GameDB ERROR : ";
|
|
strError += e.Description();
|
|
throw XException( strError );
|
|
}
|
|
|
|
if( command->Parameters->Item[ "OUT_GUILD_ID" ]->Value.vt != VT_NULL )
|
|
{
|
|
return command->Parameters->Item[ "OUT_GUILD_ID" ]->Value;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void PartyManager::loadPartyMemberTagList()
|
|
{
|
|
DBPerformanceTrackHelper helper;
|
|
try
|
|
{
|
|
DBConnection db;
|
|
InitUserDbConnection( db.connection );
|
|
if( db.CreateCommand( db.command ) == false )
|
|
{
|
|
throw XException( "ADO COMMAND_PTR CREATE ERROR!" );
|
|
}
|
|
|
|
db.command->CommandType = adCmdStoredProc;
|
|
db.command->CommandText = _bstr_t( "dbo.smp_load_party_member_info" );
|
|
|
|
_RecordsetPtr rs;
|
|
|
|
helper.start();
|
|
rs = db.command->Execute( NULL, NULL, adCmdStoredProc );
|
|
helper.end( "dbo.smp_load_party_member_info" );
|
|
|
|
int nLoadingPartyID = 0;
|
|
std::vector< PartyMemberTag > *pvLoadingPartyMemberTagList = NULL;
|
|
|
|
while( rs->State != adStateClosed && !rs->EndOfFile )
|
|
{
|
|
int sid, party_id, level, job;
|
|
_bstr_t name = rs->Fields->GetItem("name")->Value;
|
|
|
|
sid = rs->Fields->GetItem("sid")->Value;
|
|
party_id = rs->Fields->GetItem("party_id")->Value;
|
|
level = rs->Fields->GetItem("lv")->Value;
|
|
job = rs->Fields->GetItem("job")->Value;
|
|
|
|
// 최초 로딩이거나 이전 로딩된 데이터의 길드 ID가 현재 로드된 길드 ID와 다를 경우 해쉬에서 새로 검색
|
|
if( !nLoadingPartyID || nLoadingPartyID != party_id )
|
|
{
|
|
// 기존 해쉬에 없던 길드 ID면 벡터 새로 생성
|
|
if( !m_hshPartyMemberTagList.lookup( party_id, pvLoadingPartyMemberTagList ) )
|
|
{
|
|
pvLoadingPartyMemberTagList = new std::vector< PartyMemberTag >;
|
|
m_hshPartyMemberTagList.add( party_id, pvLoadingPartyMemberTagList );
|
|
}
|
|
nLoadingPartyID = party_id;
|
|
}
|
|
|
|
assert( nLoadingPartyID == party_id && pvLoadingPartyMemberTagList );
|
|
|
|
// DB에 저장된 파티원 정보는 가명 기능을 사용하지 않으므로 별도로 DB에서 Character.alias를 로드하지 않고 name으로 동일하게 세팅
|
|
pvLoadingPartyMemberTagList->push_back( PartyManager::PartyMemberTag( sid, name, name, level, job ) );
|
|
rs->MoveNext();
|
|
}
|
|
}
|
|
catch( _com_error &e )
|
|
{
|
|
helper.end( e.Error(), "dbo.smp_load_party_member_info" );
|
|
LogDBError( e, "PartyManager", "dbo.smp_load_party_member_info" );
|
|
|
|
std::string strError = "GameDB ERROR : ";
|
|
strError += e.Description();
|
|
throw XException( strError );
|
|
}
|
|
}
|
|
|
|
int PartyManager::_getPartyMemberTagList( const int party_id, const int leader_sid, std::vector< PartyManager::PartyMemberTag > & result )
|
|
{
|
|
std::vector< PartyManager::PartyMemberTag > *pvMemberTagList = NULL;
|
|
if( !m_hshPartyMemberTagList.lookup( party_id, pvMemberTagList ) )
|
|
{
|
|
_cprint( "Party info with no member detected: party_id(%d)\n", party_id );
|
|
FILELOG( "Party info with no member detected: party_id(%d)", party_id );
|
|
return 0;
|
|
}
|
|
|
|
// 파티 리더 인덱스 찾기
|
|
int leader_index = 0;
|
|
for( std::vector< PartyManager::PartyMemberTag >::iterator it = pvMemberTagList->begin() ; it != pvMemberTagList->end() ; ++it, ++leader_index )
|
|
{
|
|
if( (*it).sid == leader_sid )
|
|
break;
|
|
}
|
|
if( leader_index == pvMemberTagList->size() ) // leader_sid에 해당하는 Character 데이터 없음
|
|
leader_index = -1;
|
|
|
|
// 벡터를 교환
|
|
result.swap( *pvMemberTagList );
|
|
|
|
// 사용된 PartyMemberTag 리스트는 제거
|
|
m_hshPartyMemberTagList.erase( party_id );
|
|
delete pvMemberTagList;
|
|
|
|
return leader_index;
|
|
}
|
|
|
|
void PartyManager::loadPartyList()
|
|
{
|
|
#ifdef FRAUN_PERFORMANCE_LOG
|
|
DWORD dwTime = GetSafeTickCount();
|
|
#endif
|
|
DBConnection db;
|
|
InitUserDbConnection( db.connection );
|
|
if( db.CreateCommand( db.command ) == false )
|
|
{
|
|
throw XException( "ADO COMMAND_PTR CREATE ERROR!" );
|
|
}
|
|
|
|
db.command->CommandType = adCmdStoredProc;
|
|
db.command->CommandText = _bstr_t( "dbo.smp_load_party_list" );
|
|
|
|
_RecordsetPtr pRstParty;
|
|
|
|
DBPerformanceTrackHelper helper;
|
|
try
|
|
{
|
|
helper.start();
|
|
pRstParty = db.command->Execute(NULL, NULL, adCmdText);
|
|
helper.end( "dbo.smp_load_party_list" );
|
|
}
|
|
catch( _com_error &e )
|
|
{
|
|
helper.end( e.Error(), "dbo.smp_load_party_list" );
|
|
LogDBError( e, "PartyManager", "dbo.smp_load_party_list" );
|
|
|
|
std::string strError = "GameDB ERROR : ";
|
|
strError += e.Description();
|
|
throw XException( strError );
|
|
}
|
|
|
|
IADORecordBinding *picRs = NULL; // Interface Pointer declared.
|
|
|
|
dbParty emprs; // C++ Class object
|
|
|
|
pRstParty->QueryInterface( __uuidof(IADORecordBinding),(LPVOID*)&picRs );
|
|
|
|
picRs->BindToRecordset(&emprs);
|
|
|
|
int cnt = 0;
|
|
|
|
while( pRstParty->State != adStateClosed && !pRstParty->EndOfFile )
|
|
{
|
|
// max 파티 ID 수정
|
|
if( m_nMaxPartyID < emprs.party_sid ) m_nMaxPartyID = emprs.party_sid;
|
|
|
|
char szPartyName[61];
|
|
|
|
int code_page = ENV().GetInt( "CodePage", CP_ACP );
|
|
WideCharToMultiByte( code_page, 0, emprs.szPartyName, 61, szPartyName, 61, NULL, NULL );
|
|
|
|
// 파티 생성
|
|
PartyInfo *pInfo = makeParty( emprs.party_sid, szPartyName, emprs.leader_sid, static_cast< _ITEM_SHARE_MODE >( emprs.share_mode ), static_cast< _PARTY_TYPE >( emprs.party_type ) );
|
|
if( pInfo == NULL )
|
|
{
|
|
FILELOG( "ERROR: makeParty failed, PartyID: %d, PartyName: %s", emprs.party_sid, szPartyName );
|
|
_cprint( "ERROR: makeParty failed, PartyID: %d, PartyName: %s\n", emprs.party_sid, szPartyName );
|
|
|
|
pRstParty->MoveNext();
|
|
continue;
|
|
}
|
|
|
|
// 멤버 정보 얻어온다
|
|
int leader_index = _getPartyMemberTagList( emprs.party_sid, emprs.leader_sid, pInfo->vMemberNameList );
|
|
|
|
// 1. 파티원 없는 파티이거나
|
|
// 2. 리더가 없는 파티이거나
|
|
// 3. 베어로드 타입의 파티는 삭제
|
|
// 4. 배틀 아레나 관련 파티는 삭제
|
|
// * 이 시점에 파티를 삭제해야 Party 테이블의 데이터와 로딩된 PartyMemberTag가 제대로 삭제 처리되며, DB상의 Character.party_id도 0으로 리셋됨
|
|
if( pInfo->vMemberNameList.empty() ||
|
|
leader_index == -1 ||
|
|
pInfo->ePartyType == TYPE_HUNTAHOLIC_PARTY ||
|
|
pInfo->ePartyType == TYPE_BATTLE_ARENA_TEAM ||
|
|
pInfo->ePartyType == TYPE_BATTLE_ARENA_EXERCISE_TEAM )
|
|
{
|
|
DestroyParty( pInfo->nPartyID );
|
|
|
|
pRstParty->MoveNext();
|
|
continue;
|
|
}
|
|
|
|
const std::string & strLeaderName = pInfo->vMemberNameList[ leader_index ].strName;
|
|
pInfo->strLeaderName = strLeaderName;
|
|
// DB에서 로드되는 타입의 파티에서는 가명을 사용하지 않기 때문에 실명을 표시 이름으로 세팅해 줌
|
|
pInfo->strLeaderDisplayName = strLeaderName;
|
|
pInfo->nLeaderJobId = pInfo->vMemberNameList[ leader_index ].nJobId;
|
|
|
|
if( emprs.party_type == TYPE_RAID_ATTACKTEAM || emprs.party_type == TYPE_SIEGE_ATTACKTEAM )
|
|
{
|
|
int guild_id = get_guild_id( db.connection, pInfo->nLeaderSID );
|
|
int dungeon_id = GuildManager::GetInstance().GetRaidDungeonID( guild_id );
|
|
bool bIsRaidParty = emprs.party_type == TYPE_RAID_ATTACKTEAM;
|
|
|
|
if( !guild_id || !dungeon_id )
|
|
{
|
|
destroyParty( pInfo );
|
|
|
|
pRstParty->MoveNext();
|
|
continue;
|
|
}
|
|
|
|
if( emprs.lead_party_id != pInfo->nPartyID )
|
|
{
|
|
if( !IsExistAttackTeam( guild_id ) )
|
|
{
|
|
destroyParty( pInfo );
|
|
|
|
pRstParty->MoveNext();
|
|
continue;
|
|
}
|
|
|
|
PartyInfo * pLeadPartyInfo = getPartyInfo( emprs.lead_party_id );
|
|
if( !pLeadPartyInfo || !pLeadPartyInfo->pPartyLinkInfo || pLeadPartyInfo->ePartyType != pInfo->ePartyType )
|
|
{
|
|
// 이거 현실적으로 있을 수가 없는게, 게임 내에서 공대 파티의 메인 파티가 먼저 결성되고 그 후에 서브 파티가 결성되기 때문에
|
|
// 로딩 시 메인 파티보다 서브 파티가 먼저 로드될 가능성은 거의 없음. 발생했다면 어떤 경우인지 확인하고, 그 상황을 피할 수 없다면
|
|
// 메인 파티보다 먼저 로드된 서브 파티의 목록을 가지고 있다가 모든 파티 로드 후에 추가로 join 시켜줘야 함. 이 처리는 구현되어 있지 않음
|
|
// 그리고 메인 파티는 있었는데 파티 링크 정보만 없는 경우는 메인 파티가 로드는 됐는데 DB 상에서 lead_party_id 가 0 이었다는 이야기가 됨.
|
|
// 정상 상태가 아니니 별도의 대처도 불가능.
|
|
assert( 0 );
|
|
|
|
// 현재는 예상 밖의 상황이니 그냥 파티 해산 -,. -;
|
|
destroyParty( pInfo );
|
|
|
|
pRstParty->MoveNext();
|
|
continue;
|
|
}
|
|
|
|
if( !joinLinkedParty( pLeadPartyInfo->pPartyLinkInfo, pInfo ) )
|
|
{
|
|
// ?-_ -;?
|
|
destroyParty( pInfo );
|
|
|
|
pRstParty->MoveNext();
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int nMaxGuildParty = 0;
|
|
|
|
if( bIsRaidParty )
|
|
{
|
|
nMaxGuildParty = DungeonManager::Instance().GetMaxRaidParty( dungeon_id );
|
|
}
|
|
else
|
|
{
|
|
nMaxGuildParty = DungeonManager::Instance().GetMaxGuildParty( dungeon_id );
|
|
}
|
|
|
|
if( !makeAttackTeam( pInfo, guild_id, nMaxGuildParty ) )
|
|
{
|
|
destroyParty( pInfo );
|
|
|
|
pRstParty->MoveNext();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
pRstParty->MoveNext();
|
|
cnt++;
|
|
}
|
|
|
|
picRs->Release();
|
|
pRstParty->Close();
|
|
|
|
#ifdef FRAUN_PERFORMANCE_LOG
|
|
DWORD loadingTime = GetSafeTickCount() - dwTime;
|
|
_cprint("Total %d Parties loaded; time taken: %d\n", cnt, loadingTime);
|
|
FILELOG("Total %d Parties loaded; time taken: %d", cnt, loadingTime);
|
|
#else
|
|
_cprint( "Total %d Parties loaded.\n", cnt );
|
|
FILELOG( "Total %d Parties loaded.", cnt );
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
void PartyManager::finishLoading()
|
|
{
|
|
// 로딩에 사용되는 PartyMemberTag 가 남아있으면 제거
|
|
KHash< std::vector< PartyMemberTag > *, hashPr_mod_int >::node *pValueNode = NULL;
|
|
bool bQuitFlag = m_hshPartyMemberTagList.get_first_node( pValueNode );
|
|
while( bQuitFlag )
|
|
{
|
|
_cprint( "Party info does not exist: party_id(%d)\n", pValueNode->key );
|
|
FILELOG( "Party info does not exist: party_id(%d)", pValueNode->key );
|
|
|
|
pValueNode->value->clear();
|
|
delete pValueNode->value;
|
|
|
|
bQuitFlag = m_hshPartyMemberTagList.get_next_node( pValueNode );
|
|
}
|
|
|
|
m_hshPartyMemberTagList.clear();
|
|
}
|