Files
Sun1602/Server/MasterServer/MasterServer.cpp
T
2022-10-26 12:25:11 +08:00

2026 lines
61 KiB
C++

#include "stdafx.h"
#include <conio.h>
#include <IOCPServer.h>
#include <GMList.h>
#include "MasterServer.h"
#include "ServerSession.h"
#include "AccountDBProxySession.h"
#include "OpServerSession.h"
#include "PacketHandler.h"
#include "ServerSessionFactory.h"
#include "ServerSessionManager.h"
#include "ServerInfoManager.h"
#include "MasterServerInfoParser.h"
// 종료 프로토콜 위해
#include "Protocol_ServerCommon.h"
#include "PacketStruct_ServerCommon.h"
#include "PacketStruct_MO.h"
// 버전 찍기
#include <Version.h>
#include <ServerBuildVersion.h>
#include <ServerGlobal.h>
#include "GameServerSession.h"
#include <PacketStruct_GM.h>
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
#include "SolarAuthServerSession.h"
#endif
//#define _TEST_ABUSE
#ifdef _TEST_ABUSE
#include <SolarDateTime.h>
#endif
// 통계 위해
#include <StatisticsManager.h>
//=================================================================================================
// 콜백함수
//=================================================================================================
NetworkObject* CreateAcceptedObject()
{
ServerSession *pServerSession = ServerSessionFactory::Instance()->AllocServerSession( TEMP_SERVER );
return (NetworkObject*)pServerSession;
}
//=================================================================================================
// 콜백함수
//=================================================================================================
VOID DestroyAcceptedObject( NetworkObject *pNetworkObject )
{
ServerSessionFactory::Instance()->FreeServerSession( (ServerSession*)pNetworkObject );
}
//=================================================================================================
// 콜백함수
//=================================================================================================
VOID DestroyConnectedObject( NetworkObject *pNetworkObject )
{
}
MasterServer::MasterServer()
{
m_pIOCPServer = NULL;
m_bShutdown = FALSE;
m_pAccountDBProxySession = NULL;
m_pOpServerSession = NULL;
m_pServerMoneyInfoTable = NULL;
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
SetPhaseStatus(PHASE_UNKNOWN);
#endif //__WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
}
MasterServer::~MasterServer()
{
if( m_pIOCPServer ) delete m_pIOCPServer;
if( m_pAccountDBProxySession ) delete m_pAccountDBProxySession;
if( m_pOpServerSession ) delete m_pOpServerSession;
ASSERT( m_pServerMoneyInfoTable );
}
VOID MasterServer::Release()
{
m_pIOCPServer->Shutdown();
if( m_pAccountDBProxySession )
{
m_pAccountDBProxySession->Release();
}
if( m_pOpServerSession )
{
m_pOpServerSession->Release();
}
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
WorkLoadManager::DestroyInstance();
#endif //__WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
GMList::Instance()->Release();
m_pServerMoneyInfoTable->RemoveAll();
SAFE_DELETE( m_pServerMoneyInfoTable );
m_bExitServer = FALSE;
m_eExitType = eEXIT_NONE;
#ifdef _WHATTHEHELL_AGENTSHUTDOWN_20060524
m_pExitAgentTable->RemoveAll();
SAFE_DELETE( m_pExitAgentTable );
#endif
m_pExitServerTable->RemoveAll();
SAFE_DELETE( m_pExitServerTable );
m_pExitDBServerTable->RemoveAll();
SAFE_DELETE( m_pExitDBServerTable );
SAFE_DELETE(g_pSunLog);
}
BOOL MasterServer::Init()
{
#ifdef _TEST_ABUSE
OLD_SERVER_KEY oldKey;
SERVER_KEY testKey;
testKey.Set(1,2,3,4);
oldKey = testKey;
#endif
// INI 파일 파싱
if( !MasterServerInfoParser::Instance()->ParseINI( "MasterServer.ini" ) )
{
printf( "MasterServer.ini parsing failed.\n" );
return FALSE;
}
// 버전 찍기
#ifdef _DEBUG
DisplayDebugInfo( GetServerType(), 0, TRUE, C2S_HIGH_VERSION_NO, C2S_MIDDLE_VERSION_NO, C2S_LOW_VERSION_NO, SERVER_RELEASE_BUILD_NO, 0, 0, 1 );
#else
DisplayDebugInfo( GetServerType(), 0, FALSE, C2S_HIGH_VERSION_NO, C2S_MIDDLE_VERSION_NO, C2S_LOW_VERSION_NO, SERVER_RELEASE_BUILD_NO, 0, 0, 1 );
#endif
//SUN Log File 초기화
g_pSunLog = new CSunLog;
SUNLOG_INFO* pSunLogInfo = MasterServerInfoParser::Instance()->GetSunLogInfo();
if(g_pSunLog->Init(pSunLogInfo->bySunLogOption, (BYTE)pSunLogInfo->wSunLogfileLevel,
pSunLogInfo->pszSunLogfilePath, "Master") == false)
{
//SunLog를 만들지 않습니다.
}
// IO 엔진 인스턴스 생성 및 초기화
m_pIOCPServer = new IOCPServer;
#ifndef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
IOHANDLER_DESC desc;
desc.dwIoHandlerKey = IOHANDLER_KEY;
desc.dwMaxAcceptSession = MasterServerInfoParser::Instance()->GetIoDesc()->dwMaxAcceptSession;
desc.dwMaxConnectSession = MasterServerInfoParser::Instance()->GetIoDesc()->dwMaxConnectSession;
desc.dwSendBufferSize = MasterServerInfoParser::Instance()->GetIoDesc()->dwSendBufferSize;
desc.dwRecvBufferSize = MasterServerInfoParser::Instance()->GetIoDesc()->dwRecvBufferSize;
desc.dwTimeOut = MasterServerInfoParser::Instance()->GetIoDesc()->dwTimeOut;
desc.dwMaxPacketSize = MasterServerInfoParser::Instance()->GetIoDesc()->dwMaxPacketSize;
desc.dwNumberOfIoThreads = MasterServerInfoParser::Instance()->GetIoDesc()->dwNumberOfIoThreads;
desc.dwNumberOfAcceptThreads = MasterServerInfoParser::Instance()->GetIoDesc()->dwNumberOfAcceptThreads;
desc.dwNumberOfConnectThreads = MasterServerInfoParser::Instance()->GetIoDesc()->dwNumberOfConnectThreads;
desc.fnCreateAcceptedObject = CreateAcceptedObject;
desc.fnDestroyAcceptedObject = DestroyAcceptedObject;
desc.fnDestroyConnectedObject = DestroyConnectedObject;
m_pIOCPServer->Init( &desc, 1 );
// GM List 로딩
//GMList::Instance()->Load( "data\\GMList.txt" );
#else //__WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
INI_IOHANDLER_DESC* pIO_DESC = NULL;
IOHANDLER_DESC desc[2];
IOHANDLER_DESC& descINSIDE = desc[INSIDE_IOHANDLER];
pIO_DESC = MasterServerInfoParser::Instance()->GetIoDesc();
descINSIDE.dwIoHandlerKey = INSIDE_IOHANDLER;
descINSIDE.dwMaxAcceptSession = pIO_DESC->dwMaxAcceptSession;
descINSIDE.dwMaxConnectSession = pIO_DESC->dwMaxConnectSession;
descINSIDE.dwSendBufferSize = pIO_DESC->dwSendBufferSize;
descINSIDE.dwRecvBufferSize = pIO_DESC->dwRecvBufferSize;
descINSIDE.dwTimeOut = pIO_DESC->dwTimeOut;
descINSIDE.dwMaxPacketSize = pIO_DESC->dwMaxPacketSize;
descINSIDE.dwNumberOfIoThreads = pIO_DESC->dwNumberOfIoThreads;
descINSIDE.dwNumberOfAcceptThreads = pIO_DESC->dwNumberOfAcceptThreads;
descINSIDE.dwNumberOfConnectThreads = pIO_DESC->dwNumberOfConnectThreads;
descINSIDE.fnCreateAcceptedObject = CreateAcceptedObject;
descINSIDE.fnDestroyAcceptedObject = DestroyAcceptedObject;
descINSIDE.fnDestroyConnectedObject = DestroyConnectedObject;
pIO_DESC = MasterServerInfoParser::Instance()->GetIoDescOutside();
IOHANDLER_DESC& descOUTSIDE = desc[OUTSIDE_IOHANDLER];
descOUTSIDE.dwIoHandlerKey = OUTSIDE_IOHANDLER;
descOUTSIDE.dwMaxAcceptSession = pIO_DESC->dwMaxAcceptSession;
descOUTSIDE.dwMaxConnectSession = pIO_DESC->dwMaxConnectSession;
descOUTSIDE.dwSendBufferSize = pIO_DESC->dwSendBufferSize;
descOUTSIDE.dwRecvBufferSize = pIO_DESC->dwRecvBufferSize;
descOUTSIDE.dwTimeOut = pIO_DESC->dwTimeOut;
descOUTSIDE.dwMaxPacketSize = pIO_DESC->dwMaxPacketSize;
descOUTSIDE.dwNumberOfIoThreads = pIO_DESC->dwNumberOfIoThreads;
descOUTSIDE.dwNumberOfAcceptThreads = pIO_DESC->dwNumberOfAcceptThreads;
descOUTSIDE.dwNumberOfConnectThreads = pIO_DESC->dwNumberOfConnectThreads;
descOUTSIDE.fnCreateAcceptedObject = CreateAcceptedObject; //다른 것으로 대체할 것
descOUTSIDE.fnDestroyAcceptedObject = DestroyAcceptedObject; //다른 것으로 대체할 것
descOUTSIDE.fnDestroyConnectedObject = DestroyConnectedObject;
m_pIOCPServer->Init( desc, 2 );
//// GM List 로딩
//GMList::Instance()->Load( "data\\GMList.txt" );
#endif //__WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
// 패킷 핸들러 등록
PacketHandler::Instance()->RegisterHandler_DM();
PacketHandler::Instance()->RegisterHandler_MX();
PacketHandler::Instance()->RegisterHandler_BM();
PacketHandler::Instance()->RegisterHandler_FM();
PacketHandler::Instance()->RegisterHandler_GM();
PacketHandler::Instance()->RegisterHandler_AM();
PacketHandler::Instance()->RegisterHandler_MW();
PacketHandler::Instance()->RegisterHandler_MO();
m_pAccountDBProxySession = (AccountDBProxySession*)ServerSessionFactory::Instance()->AllocServerSession( ACCOUNT_DBPROXY );
m_pOpServerSession = (OpServerSession*)ServerSessionFactory::Instance()->AllocServerSession( OP_SERVER );
// DB Proxy 서버 연결 정보 세팅
m_pAccountDBProxySession->SetAddr( MasterServerInfoParser::Instance()->GetAccountDBProxyIP(),
MasterServerInfoParser::Instance()->GetAccountDBProxyPort() );
printf( "Account DB proxy address(%s:%d)\n", MasterServerInfoParser::Instance()->GetAccountDBProxyIP(),
MasterServerInfoParser::Instance()->GetAccountDBProxyPort() );
// Op Server 연결 정보 세팅
m_pOpServerSession->SetAddr( MasterServerInfoParser::Instance()->GetOpServerIP(),
MasterServerInfoParser::Instance()->GetOpServerPort() );
MessageOut( eCRITICAL_LOG, "Op server address(%s:%d)", MasterServerInfoParser::Instance()->GetOpServerIP(),
MasterServerInfoParser::Instance()->GetOpServerPort() );
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
PacketHandler::Instance()->RegisterHandler_SM();
m_pSolarAuthServerSession = (SolarAuthServerSession*)ServerSessionFactory::Instance()->AllocServerSession( SOLARAUTH_SERVER );
// Solar Auth 서버 연결 정보 세팅
m_pSolarAuthServerSession->SetAddr( MasterServerInfoParser::Instance()->GetSolarAuthServerIP(),
MasterServerInfoParser::Instance()->GetSolarAuthServerPort() );
printf( "SolarAuthServer IP address(%s:%d)\n", MasterServerInfoParser::Instance()->GetSolarAuthServerIP(),
MasterServerInfoParser::Instance()->GetSolarAuthServerPort() );
WorkLoadManager::CreateInstance();
sSCRIPT_DATA_INFO_NODE pNodeTable[SCRIPT_CODE_KEY(LIST_SIZE)] =
{
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ACCESSORYITEMINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ACSCRIPT), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(AICOMMONINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(AITYPEINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ARMORITEMINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ARMORTYPEDAMAGEINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(COMPOSITE_NPC), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(DEC1), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(DEC2), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(DEVCOMMAND), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(DROPRATIOINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ENC1), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ENC2), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ENCHANT), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(EXPVALUEINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(EXTRASTONEOPTIONINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(FATIGUEOPTION), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(FIELD), NULL),
//SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(FILELIST), NULL),
// break;
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(FORMULARATIO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(GMCOMMAND), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(GMLIST), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(GROUP), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(INITCHARINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(INITCHARINFOLIST), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMCOMPOSELIST), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMCOMPOSITEINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMCOMPOSITERESULTINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMCRYSTALIZELIST), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMDROPINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMDROPPERGRADE), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMTYPEDROPRATIO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(ITEMTYPELIST), NULL),
//SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(LIMITEDMAPPORT), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(MAPENTERANCEPORTAL), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(MATCHINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(MISSIONREWARDINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(NOTICE_SAMPLE), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(NPCEXTRAINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(NPCINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(PKSCRIPT), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(QUESTINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(RANKOPTIONINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(RAREITEMDROPINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(REGEN), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(REGEN_POS), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(REQUIREFIELDLEVELINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(REWARDINFOLIST), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(SHOPINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(SHOUTRANGE), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(SKILLINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(SOCKETOPTIONINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(STATEINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(STYLEINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(STYLEQUICKREGISTINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(SUMMON), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(SUNEVENTINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(SUPERUSERLIST), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(THEMOLAC), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(THEMOLAD), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(THEMOLAS), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(VSHUNTINGREWARDINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(WASTEITEMINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(WEAPONITEMINFO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(WORLD), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(FIELDWMO), NULL),
SCRIPT_DATA_INFO_SET(SCRIPT_CODE_KEY(FIELDWUT), NULL),
SCRIPT_DATA_INFO_SET_EMPTY(),
};
ScriptDataManager::RegisterDataList( pNodeTable );
ScriptDataManager::RegisterCallbackAllDataLoaded( MasterServer::StartDataService );
#endif
m_ReconnectTimer.SetTimer( 1000 );
// 서버 통화량 정보 초기화
m_pServerMoneyInfoTable = new util::SolarHashTable<SERVER_MONEY_INFO*>;
m_pServerMoneyInfoTable->Initialize( 100 );
// 서버 종료 정보 초기화
m_bExitServer = FALSE;
m_eExitType = eEXIT_NONE;
#ifdef _WHATTHEHELL_AGENTSHUTDOWN_20060524
m_pExitAgentTable = new util::SolarHashTable<SERVER_KEY*>;
m_pExitAgentTable->Initialize( 100 );
#endif
m_pExitServerTable = new util::SolarHashTable<SERVER_KEY*>;
m_pExitServerTable->Initialize( 1000 ); // 최대 1000개 서버 종료 가능?
m_pExitDBServerTable = new util::SolarHashTable<SERVER_KEY*>;
m_pExitDBServerTable->Initialize( 100 ); // 최대 100개 서버 종료 가능?
m_ExitTimer.SetTimer( MasterServerInfoParser::Instance()->GetExitInterval() ); // 종료 응답 대기 시간
m_ExitTimer.DisableCheckTime();
// 통화량 주기 초기화
m_MoneyTimer.SetTimer( MasterServerInfoParser::Instance()->GetStatisticsInterval() );
return TRUE;
}
VOID MasterServer::StartListen()
{
// DB Proxy에 연결된 후에 리슨 시작
if( !m_pIOCPServer->IsListening( INSIDE_IOHANDLER ) )
{
char szMasterServerIP[MAX_IPADDRESS_SIZE+1]={0,};
WORD wMasterServerPort;
strncpy( szMasterServerIP, MasterServerInfoParser::Instance()->GetMasterServerIP(), MAX_IPADDRESS_SIZE );
wMasterServerPort = MasterServerInfoParser::Instance()->GetMasterServerPort();
if( !m_pIOCPServer->StartListen( INSIDE_IOHANDLER, szMasterServerIP, wMasterServerPort ) )
{
MessageOut( eCRITICAL_LOG, "*** Listen failed. ***" );
Shutdown();
}
else
{
MessageOut( eCRITICAL_LOG, "Listen started...(%s:%d)", szMasterServerIP, wMasterServerPort );
}
}
// Account DB pxory 세션은 DB 정보를 받은 지금 시점에 서버 세션 매니져에 추가
SERVER_KEY serverKey = ServerInfoManager::Instance()->GetServerKey( ACCOUNT_DBPROXY, (char*)m_pAccountDBProxySession->GetConnectIP().c_str() );
if( !ServerSessionManager::Instance()->FindServer( serverKey ) )
{
// 서버 정보를 업데이트 하는 경우 또 추가하지 않게끔 서버키로 찾아본 후에 추가
m_pAccountDBProxySession->SetServerKey( serverKey );
ServerSessionManager::Instance()->AddServer( m_pAccountDBProxySession );
}
// Op Server 세션은 DB 정보를 받은 지금 시점에 서버 세션 매니져에 추가
SERVER_KEY opServerKey = ServerInfoManager::Instance()->GetServerKey( OP_SERVER, (char*)m_pOpServerSession->GetConnectIP().c_str() );
if( !ServerSessionManager::Instance()->FindServer( opServerKey ) )
{
// Op Server가 연결이 되어있는 경우 추가!
if( m_pOpServerSession->IsConnected() )
{
m_pOpServerSession->SetServerKey( opServerKey );
ServerSessionManager::Instance()->AddServer( m_pOpServerSession );
}
}
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
// Solar Auth Server 세션은 DB 정보를 받은 지금 시점에 서버 세션 매니져에 추가
SERVER_KEY solarAuthServerKey = ServerInfoManager::Instance()->GetServerKey( SOLARAUTH_SERVER, (char*)m_pSolarAuthServerSession->GetConnectIP().c_str() );
if( !ServerSessionManager::Instance()->FindServer( solarAuthServerKey ) )
{
// 서버 정보를 업데이트 하는 경우 또 추가하지 않게끔 서버키로 찾아본 후에 추가
m_pSolarAuthServerSession->SetServerKey( solarAuthServerKey );
ServerSessionManager::Instance()->AddServer( m_pSolarAuthServerSession );
}
#endif
}
VOID MasterServer::Run()
{
// Account DB Proxy에 접속 시도
// ConnectToServer( m_pAccountDBProxySession, (char*)m_pAccountDBProxySession->GetConnectIP().c_str(), m_pAccountDBProxySession->GetConnectPort() );
// Op Server에 접속시도
// ConnectToServer( m_pOpServerSession, (char*)m_pOpServerSession->GetConnectIP().c_str(), m_pOpServerSession->GetConnectPort() );
// 메인 루프
while( !m_bShutdown )
{
Sleep( 10 );
if( !Update() ) break;
}
}
BOOL MasterServer::Update()
{
// I/O 엔진 업데이트
m_pIOCPServer->Update();
UpdateFPS();
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
WorkLoadManager::Update();
#endif //__WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
ServerSessionManager::Instance()->Update();
g_pSunLog->Update();
// 콘솔처리
if( ProcessConsole() == FALSE )
{
return FALSE; // 서버 종료
}
if( m_ReconnectTimer.IsExpired() )
{
MaintainConnection();
}
// 돈 처리
if( m_MoneyTimer.IsExpired() )
{
SendMoneyReq();
}
return TRUE;
}
VOID MasterServer::MaintainConnection()
{
// 마스터 서버가 종료중이면 리턴
if( m_bShutdown ) return;
// Account DB proxy와 연결이 끊어져 있으면 다시 접속을 시도한다.
if( !m_pAccountDBProxySession->IsConnected() && !m_pAccountDBProxySession->GetConnectIP().empty() )
{
ConnectToServer( m_pAccountDBProxySession, (char*)m_pAccountDBProxySession->GetConnectIP().c_str(), m_pAccountDBProxySession->GetConnectPort() );
}
/*
// 혹시 무슨 이유로 OpServer가 NULL이면
if( ServerSessionManager::Instance()->GetOpServer() == NULL )
{
// m_pOpServerSession과 맞춰준다
ServerSessionManager::Instance()->AddServer( m_pOpServerSession );
}
*/
// Op Server와 연결이 끊어져 있으면 재접속
if( !m_pOpServerSession->IsConnected() && !m_pOpServerSession->GetConnectIP().empty() )
{
ConnectToServer( m_pOpServerSession, (char*)m_pOpServerSession->GetConnectIP().c_str(), m_pOpServerSession->GetConnectPort() );
}
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
if( !m_pSolarAuthServerSession->IsConnected() && !m_pSolarAuthServerSession->GetConnectIP().empty() )
{
ConnectToServerOutside( m_pSolarAuthServerSession, (char*)m_pSolarAuthServerSession->GetConnectIP().c_str(), m_pSolarAuthServerSession->GetConnectPort() );
}
#endif
// 정상종료 명령을 WOPS에서 받았는지 check하고 필요한 종료 과정 수행
CheckExitServer();
// if( CheckExitServer() )
// {
// Shutdown();
// }
}
#include <PacketStruct_MX.h>
// 기능 제한 정보 DB에서 읽어오도록 요청
BOOL MasterServer::SendRestrictReq()
{
if( m_pAccountDBProxySession && m_pAccountDBProxySession->IsConnected() )
{
MasterServerInfoParser::Instance()->ClearFuncRestrictInfo();
MSG_MX_FUNCRESTRICT_RELOAD_REQ sendMsg;
m_pAccountDBProxySession->Send( (BYTE*)&sendMsg, sizeof(sendMsg) );
return TRUE;
}
return FALSE;
}
VOID MasterServer::SendRestrictAns()
{
// 금지 기능들 정보 날려준다
MSG_GM_OPERATION_FUNCRESTRICT_CMD funcMsg;
MasterServerInfoParser::Instance()->GetFuncRestrictInfo( (BYTE*)&funcMsg );
// 내용 있어야 보내준다
if( funcMsg.byRestrictNum )
{
ServerSessionManager::Instance()->SendGameServer( (BYTE*)&funcMsg, funcMsg.GetSize() );
}
}
BOOL MasterServer::ProcessConsole()
{
if( kbhit() )
{
char ch = getch();
ch = toupper(ch);
switch(ch)
{
case 0x1b:
return FALSE;
case ' ':
DisplayServerInfo();
break;
case 'U':
ServerInfoManager::Instance()->Clear();
m_pAccountDBProxySession->SendReqServerInfo();
break;
case 'S':
// 인터벌 정보 리로드
MasterServerInfoParser::Instance()->ReloadInterval();
m_ExitTimer.Reset();
m_ExitTimer.SetTimer( MasterServerInfoParser::Instance()->GetExitInterval() ); // 종료 응답 대기 시간
m_ExitTimer.DisableCheckTime();
// 통화량 주기 초기화
m_MoneyTimer.Reset();
m_MoneyTimer.SetTimer( MasterServerInfoParser::Instance()->GetStatisticsInterval() );
MessageOut( eCRITICAL_LOG, "Interval Info Reload[Exit:%u][Statistics:%u]", m_ExitTimer.GetIntervalTime(),
m_MoneyTimer.GetIntervalTime() );
break;
case 'R':
if( MasterServerInfoParser::Instance()->IsRestrictInfoFromFile() )
{
MasterServerInfoParser::Instance()->LoadFuncRestrictInfo();
// 금지 기능들 정보 있으면 날려준다
MSG_GM_OPERATION_FUNCRESTRICT_CMD funcMsg;
MasterServerInfoParser::Instance()->GetFuncRestrictInfo( (BYTE*)&funcMsg );
// 내용 있어야 보내준다
if( funcMsg.byRestrictNum )
{
ServerSessionManager::Instance()->SendGameServer( (BYTE*)&funcMsg, funcMsg.GetSize() );
}
MessageOut( eCRITICAL_LOG, "Reload FuncRestrict Setting AND Broadcast" );
}
else // DB에서 읽어오는 경우 DB에 금지 기능들 정보 요청
{
SendRestrictReq();
}
#ifdef _TEST_ABUSE
// 테스트용 코드 - 가라로 어뷰징 보낸다
MSG_MO_RTTG_ABUSING_NTF sendMsg;
SERVER_KEY serverKey;
serverKey.Set( 1, 2, 3, 4 );
GetDateTime_YYMMDDHHMMSS( sendMsg.m_dw64Time );
sendMsg.m_dwUserGuid = 3724;
sendMsg.m_dwCharGuid = 12938;
sendMsg.m_ServerKey = serverKey;
sendMsg.m_byAbuseType = 1;
wsprintf( sendMsg.m_ptszDesc, "가라 테스트 데이터~[3724][12938][1,2,3,4][3]" );
sendMsg.m_byLength = _tcslen(sendMsg.m_ptszDesc);
ServerSession *pOpServer = GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA Abusing 보고 실패:No RTTA[Type:%u][UserGuid:%u][CharGuid:%u][Desc:%u]", sendMsg.m_byAbuseType,
sendMsg.m_dwUserGuid,
sendMsg.m_dwCharGuid,
sendMsg.m_ptszDesc );
return FALSE;
}
pOpServer->Send( (BYTE*)&sendMsg, sendMsg.GetSize() );
MessageOut( eCRITICAL_LOG, "RTTA Abusing Alarm:No RTTA[Type:%u][UserGuid:%u][CharGuid:%u]", sendMsg.m_byAbuseType,
sendMsg.m_dwUserGuid,
sendMsg.m_dwCharGuid );
// 가라로 서버 정보도 리로드
// SendRestrictReq();
#endif
// RTTA 허용 상태도 읽어서 리턴 - 이 코드는 일단 봉인
// MasterServerInfoParser::Instance()->LoadRTTARestrictInfo();
// MessageOut( eCRITICAL_LOG, "Reload RTTARestrict Setting" );
break;
/*
case 'S':
//테스트 - 가라로 서버 종료 요청
SERVER_KEY exitKey;
exitKey.Set( 0, 255, 0, 0 );
ServerSessionManager::Instance()->AddExitServerList( exitKey );
SetExitServer(); // 종료 모드 돌입!
SetExitKey( 0, 0 );
SetExitType( eEXIT_AGENTSTART );
//서버 종료는 순차적으로(필드/배틀/에이전트->DB프락시) 이루어져야 하므로, 서버 종료 모드로 들어가서 처리해야 한다
MessageOut( eFULL_LOG, "정상 종료 가라로 수신[W:%u/C:%u/T:%u/I:%u]", exitKey.GetWorldID(),
exitKey.GetChannelID(),
exitKey.GetServerType(),
exitKey.GetServerID() );
break;
case 'D':
// 테스트-접속종료 요청
// 현재 접속중인 모든 Agent 서버에게 이 프로토콜을 전달한다
MSG_AM_OPERATION_CHAR_DISCONNECT_SYN sendMsg;
sendMsg.m_dwUserGuid = 4342;
SERVER_SESSION_VEC agentList = ServerSessionManager::Instance()->GetAgentServerList();
if( agentList.empty() )
{
MessageOut( eCRITICAL_LOG, "MO_RTTG_USERDISCONNECT Error - No Agent Server 4342" );
return TRUE;
}
SERVER_SESSION_VEC::iterator iter;
ServerSession *pAgentServer=NULL;
for( iter = agentList.begin(); iter != agentList.end(); iter++ )
{
pAgentServer = (*iter);
pAgentServer->Send( (BYTE*)&sendMsg, sizeof(MSG_AM_OPERATION_CHAR_DISCONNECT_SYN) );
}
MessageOut( eFULL_LOG, "USERDISCONNECT Send 4342" );
break;
*/
}
}
return TRUE;
}
VOID MasterServer::ConnectToServer( NetworkObject *pNetworkObject, char *pszIP, WORD wPort )
{
m_pIOCPServer->Connect( INSIDE_IOHANDLER, pNetworkObject, pszIP, wPort );
}
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
VOID MasterServer::ConnectToServerOutside( NetworkObject *pNetworkObject, char *pszIP, WORD wPort )
{
m_pIOCPServer->Connect( OUTSIDE_IOHANDLER, pNetworkObject, pszIP, wPort );
}
#endif //__WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
VOID MasterServer::UpdateFPS()
{
// FPS Check
static int cnt = 0;
static DWORD prevTick = GetTickCount();
cnt++;
DWORD curTick = GetTickCount();
if( prevTick + 1000 < curTick )
{
m_dwFPS = cnt;
//printf( "%d FPS\n", cnt );
prevTick = curTick;
cnt = 0;
if( m_dwFPS < 10 )
{
DISPMSG( "============================================================\n" );
DISPMSG( "============================================================\n" );
DISPMSG( "[MasterServer::UpdateFPS] FPS Warning!!! [%d] \n", m_dwFPS );
DISPMSG( "============================================================\n" );
DISPMSG( "============================================================\n" );
}
}
}
VOID MasterServer::DisplayServerInfo()
{
printf( "======== Master Server ========\n" );
printf( " Frame per Second: %d\n", m_dwFPS );
printf( " Total Connection: %d\n", m_pIOCPServer->GetNumberOfConnections( INSIDE_IOHANDLER ) );
if( !m_pAccountDBProxySession->IsConnected() )
{
printf( ">> Warning: Not connected to account DB proxy server.\n" );
}
if( !m_pOpServerSession->IsConnected() )
{
printf( ">> Warning: Not connected to operating server(RTTA).\n" );
}
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
if( !m_pSolarAuthServerSession->IsConnected() )
{
printf( ">> Warning: Not connected to Solar Auth Server.\n" );
}
#endif
// 맵 훑어서 서버 정보 주루룩 읊어준다
WORLD_MAP_ITER iterWorld;
CHANNEL_MAP mapChannel;
CHANNEL_MAP_ITER iterChannel;
SERVERKEY_VEC vecServer;
SERVERKEY_VEC_ITER iterServer;
SERVER_KEY serverKey;
DISPMSG( "===== World Info =====\n" );
for( iterWorld = m_WorldMap.begin(); iterWorld != m_WorldMap.end(); ++iterWorld )
{
DISPMSG( "======= World %u =======\n", (*iterWorld).first );
mapChannel = (*iterWorld).second;
for( iterChannel = mapChannel.begin(); iterChannel != mapChannel.end(); ++iterChannel )
{
#ifdef _DEBUG
DISPMSG( "====== Channel %u ======\n", (*iterChannel).first );
vecServer = (*iterChannel).second;
for( iterServer = vecServer.begin(); iterServer != vecServer.end(); ++iterServer )
{
serverKey = (*iterServer);
DISPMSG( "%s\n", GetServerType2String( static_cast<eSERVER_TYPE>(serverKey.GetServerType()) ) );
}
#else
vecServer = (*iterChannel).second;
DISPMSG( "=== Channel %u: %u ===\n", (*iterChannel).first, vecServer.size() );
#endif
// printf( "========================\n" );
}
}
DISPMSG( "========================\n" );
}
BOOL MasterServer::IsConnectedTo( eSERVER_TYPE eServerType )
{
switch( eServerType )
{
case ACCOUNT_DBPROXY:
return m_pAccountDBProxySession->IsConnected();
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
case SOLARAUTH_SERVER:
return m_pSolarAuthServerSession->IsConnected();
#endif
}
assert( !"그런 서버 타입은 없어요" );
return FALSE;
}
/*
VOID MasterServer::SetMoney( SERVER_KEY serverkey, ULONGLONG &ui64GetMoney, ULONGLONG &ui64LoseMoney, ULONGLONG &ui64UserMoney )
{
SERVER_MONEY_INFO *pServer = m_pServerMoneyInfoTable->GetData( serverkey );
if(pServer == NULL)
{
// 없으면 만들어
pServer = new SERVER_MONEY_INFO;
pServer->byWorldID = serverkey.GetWorldID();
pServer->byChannelID = serverkey.GetChannelID();
pServer->byServerType = serverkey.GetServerType();
pServer->byServerID = serverkey.GetServerID();
pServer->ui64TotalGetMoney = ui64GetMoney;
pServer->ui64TotalLoseMoney = ui64LoseMoney;
pServer->ui64TotalUserMoney = ui64UserMoney;
m_pServerMoneyInfoTable->Add( pServer, serverkey );
}
else
{
// 있으면 더해
pServer->ui64TotalGetMoney += ui64GetMoney;
pServer->ui64TotalLoseMoney += ui64LoseMoney;
pServer->ui64TotalUserMoney = ui64UserMoney; // 얘는 더하면 안된다
}
}
BOOL MasterServer::GetMoney( SERVER_KEY serverkey, ULONGLONG &ui64GetMoney, ULONGLONG &ui64LoseMoney, ULONGLONG &ui64UserMoney )
{
SERVER_MONEY_INFO *pServer = m_pServerMoneyInfoTable->GetData( serverkey );
if( pServer == NULL )
{
return FALSE;
}
ui64GetMoney = pServer->ui64TotalGetMoney;
ui64LoseMoney = pServer->ui64TotalLoseMoney;
ui64UserMoney = pServer->ui64TotalUserMoney;
return TRUE;
}
VOID MasterServer::RemoveMoney( SERVER_KEY serverkey )
{
SERVER_MONEY_INFO *pServer = m_pServerMoneyInfoTable->GetData( serverkey );
if( pServer == NULL )
{
return;
}
m_pServerMoneyInfoTable->Remove( serverkey );
}
DWORD MasterServer::GetMoneyInfoNum()
{
return m_pServerMoneyInfoTable->GetDataNum();
}
VOID MasterServer::SetMoneyInfoFirst()
{
m_pServerMoneyInfoTable->SetFirst();
}
SERVER_MONEY_INFO* MasterServer::GetMoneyInfoNext()
{
return m_pServerMoneyInfoTable->GetNext();
}
*/
#ifdef _WHATTHEHELL_AGENTSHUTDOWN_20060524
BOOL MasterServer::AddExitAgentServer( SERVER_KEY sServerKey )
{
SERVER_KEY *pServerKey = new SERVER_KEY;
pServerKey->dwKey = sServerKey.dwKey;
return m_pExitAgentTable->Add( pServerKey, pServerKey->dwKey );
}
VOID MasterServer::RemoveExitAgentServer( SERVER_KEY serverKey )
{
SERVER_KEY *pServerKey = m_pExitAgentTable->GetData( serverKey.dwKey );
if( pServerKey )
{
m_pExitAgentTable->Remove( serverKey.dwKey );
delete pServerKey;
MessageOut( eFULL_LOG, "RemoveExitAgentServer - delete [%u/%u]", serverKey.GetWorldID(), serverKey.GetChannelID() );
return;
}
MessageOut( eFULL_LOG, "RemoveExitAgentServer Error - No Exit ServerKey[%u/%u]", serverKey.GetWorldID(), serverKey.GetChannelID() );
}
#endif
// 종료시킬 서버 추가
BOOL MasterServer::AddExitServer( SERVER_KEY sServerKey )
{
SERVER_KEY *pServerKey = new SERVER_KEY;
pServerKey->m_dwKey = sServerKey.m_dwKey;
return m_pExitServerTable->Add( pServerKey, pServerKey->m_dwKey );
}
// 종료시킬 서버 삭제
VOID MasterServer::RemoveExitServer( SERVER_KEY serverKey )
{
SERVER_KEY *pServerKey = m_pExitServerTable->GetData( serverKey.m_dwKey );
if( pServerKey )
{
m_pExitServerTable->Remove( serverKey.m_dwKey );
delete pServerKey;
MessageOut( eFULL_LOG, "RemoveExitServer - delete [%u/%u/%u/%u]", serverKey.GetWorldID(),
serverKey.GetChannelID(),
serverKey.GetServerType(),
serverKey.GetServerID() );
return;
}
MessageOut( eFULL_LOG, "RemoveExitServer Error - No Exit ServerKey[%u/%u/%u/%u]", serverKey.GetWorldID(),
serverKey.GetChannelID(),
serverKey.GetServerType(),
serverKey.GetServerID() );
}
// 종료시킬 서버 추가
BOOL MasterServer::AddExitDBServer( SERVER_KEY sServerKey )
{
SERVER_KEY *pServerKey = new SERVER_KEY;
pServerKey->m_dwKey = sServerKey.m_dwKey;
return m_pExitDBServerTable->Add( pServerKey, pServerKey->m_dwKey );
}
// 종료시킬 서버 삭제
VOID MasterServer::RemoveExitDBServer( SERVER_KEY serverKey )
{
SERVER_KEY *pServerKey = m_pExitDBServerTable->GetData( serverKey.m_dwKey );
if( pServerKey )
{
m_pExitDBServerTable->Remove( serverKey.m_dwKey );
delete pServerKey;
MessageOut( eFULL_LOG, "RemoveExitDBServer - delete [%u/%u/%u/%u]", serverKey.GetWorldID(),
serverKey.GetChannelID(),
serverKey.GetServerType(),
serverKey.GetServerID() );
// SetExitType( FALSE ); // 다 보냈다
return;
}
// SetExitType( FALSE ); // 다 보냈다
MessageOut( eFULL_LOG, "RemoveExitDBServer [%u/%u/%u/%u]", serverKey.GetWorldID(),
serverKey.GetChannelID(),
serverKey.GetServerType(),
serverKey.GetServerID() );
}
BOOL MasterServer::CheckExitServer()
{
/*
// 테스트 코드 - 2초에 한번씩 가짜 월드서버 동접을 보낸다
static DWORD dwLastTime = GetTickCount();
DWORD dwThisTime = GetTickCount();
if( dwThisTime >= dwLastTime + 8000 )
{
MSG_MO_RTTG_USERCOUNT_NTF sendMsg;
DWORD dwTotalVillageCount = 0, dwTotalVillagePCRoomCount = 0;
DWORD dwTotalBattleZoneCount = 0, dwTotalBattleZonePCRoomCount = 0;
MSG_MO_RTTG_BATTLEZONEINFO_ANS battleMsg;
TCHAR szMsg[512];
ServerSession *pOpServer = GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA 동접수 보고 실패:No Data" );
return FALSE;
}
for( BYTE byWorld = 1; byWorld < 6; byWorld++ )
{
if( rand() % 2 )
{
continue;
}
// 배틀존 상태
battleMsg.byCount = 3;
// 동접
sendMsg.dwWorldID = byWorld;
dwTotalVillageCount = 0;
dwTotalVillagePCRoomCount = 0;
dwTotalBattleZoneCount = 0;
dwTotalBattleZonePCRoomCount = 0;
for(BYTE i=1; i<=3; i++)
{
sendMsg.dwChannelID = i;
sendMsg.dwVillageCount = i * 300;
sendMsg.dwVillagePCRoomCount = i * 200;
sendMsg.dwBattleZoneCount = i * 200;
sendMsg.dwBattleZonePCRoomCount = i * 300;
pOpServer->Send( (BYTE*)&sendMsg, sizeof(MSG_MO_RTTG_USERCOUNT_NTF) );
MessageOut( eCRITICAL_LOG, "RTTA로 동접수 보고: W(%u)C(%u)V(%u/%u)B(%u/%u)", sendMsg.dwWorldID,
sendMsg.dwChannelID,
sendMsg.dwVillageCount,
sendMsg.dwVillagePCRoomCount,
sendMsg.dwBattleZoneCount,
sendMsg.dwBattleZonePCRoomCount );
dwTotalVillageCount += sendMsg.dwVillageCount;
dwTotalVillagePCRoomCount += sendMsg.dwVillagePCRoomCount;
dwTotalBattleZoneCount += sendMsg.dwBattleZoneCount;
dwTotalBattleZonePCRoomCount += sendMsg.dwBattleZonePCRoomCount;
// 배틀존
battleMsg.ChannelInfo[i].byWorldID = byWorld;
battleMsg.ChannelInfo[i-1].byChannelID = i;
battleMsg.ChannelInfo[i-1].byWorldID = byWorld;
memset( &battleMsg.ChannelInfo[i].dwZoneCount, 0, sizeof(DWORD) * eZONETYPE_MAX );
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_VILLAGE] = sendMsg.dwVillageCount;
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_FIELD] = sendMsg.dwVillagePCRoomCount;
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_HUNTING] = sendMsg.dwBattleZonePCRoomCount;
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_MISSION] = sendMsg.dwBattleZoneCount - 100;
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_PVP] = 100;
_snprintf(szMsg, sizeof(szMsg), "배틀존 상태 : W(%d)C(%d)V(%u)H(%u)M(%u)PVP(%u)", battleMsg.ChannelInfo[i-1].byWorldID,
battleMsg.ChannelInfo[i-1].byChannelID,
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_VILLAGE],
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_HUNTING],
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_MISSION],
battleMsg.ChannelInfo[i-1].dwZoneCount[eZONETYPE_PVP] );
SUNLOG->InsertMessage( szMsg, eMIDDLE_LOG );
}
MessageOut( eCRITICAL_LOG, "월드 동접 확인: W(%u)V(%u/%u)B(%u/%u)", sendMsg.dwWorldID,
dwTotalVillageCount,
dwTotalVillagePCRoomCount,
dwTotalBattleZoneCount,
dwTotalBattleZonePCRoomCount );
pOpServer->Send( (BYTE*)&battleMsg, battleMsg.GetSize() );
dwLastTime = dwThisTime;
}
}
// 테스트 코드 끝
*/
// 정상 종료 프로세스중인지 확인한다
if( !IsExitServer() )
{
return FALSE;
}
// static DWORD dwLastTick = GetTickCount();
// DWORD dwTickCount = GetTickCount();
SERVERKEY_HASH_ITR iter;
ServerSession *pServerSession = NULL;
SERVER_KEY ExitServerKey;
MSG_MO_RTTG_SERVERSHUTDOWN_ANS sendMsg;
switch( m_eExitType )
{
case eEXIT_NONE:
{
m_bExitFailed = FALSE;
break;
}
#ifdef _WHATTHEHELL_AGENTSHUTDOWN_20060524
case eEXIT_AGENTSTART: // 에이전트 종료 패킷 보낸다
{
MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ sendMsg;
sendMsg.m_byCategory = 0;
sendMsg.m_byProtocol = SERVERCOMMON_SERVERSHUTDOWN_REQ;
for( iter = m_pExitAgentTable->begin(); iter != m_pExitAgentTable->end(); ++iter )
{
ExitServerKey = *(*iter);
pServerSession = ServerSessionManager::Instance()->FindServer( ExitServerKey );
if( pServerSession )
{
pServerSession->Send( (BYTE*)&sendMsg, sizeof(MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ) );
}
else
{
m_pExitAgentTable->erase( iter );
break;
}
m_eExitType = eEXIT_AGENTWAIT;
m_ExitTimer.Reset();
m_ExitTimer.EnableCheckTime();
MessageOut( eFULL_LOG, "Send AgentExit Message" );
}
break;
}
case eEXIT_AGENTWAIT: // 에이전트 종료 패킷 보내고 대기
{
if( m_pExitAgentTable->GetDataNum() )
{
m_eExitType = eEXIT_GAMESTART;
MessageOut( eFULL_LOG, "Send AgentExit Complete" );
break;
}
// 일정 시간 이상 응답 없으면 전부 오류 처리
if( m_ExitTimer.IsExpired() )
{
ServerSession* pOpServer = GetOpServer();
SERVER_KEY* pExitServerKey = NULL;
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA ServerShutDown TimeOut Error(Agent) - No RTTA Server" );
return FALSE;
}
while( m_pExitAgentTable->GetDataNum() )
{
pExitServerKey = m_pExitAgentTable->GetHeadData();
if( pExitServerKey )
{
// 오류 전송
sendMsg.dwError = 1;
sendMsg.m_ServerKey = *pExitServerKey;
sendMsg.dwKey = GetExitKey();
sendMsg.i64WopsKey = GetExit64Key();
if( !m_bExitFailed )
{
pOpServer->Send( (BYTE*)&sendMsg, sizeof(MSG_MO_RTTG_SERVERSHUTDOWN_ANS) );
m_bExitFailed = TRUE;
}
// 이미 처리한 서버키는 삭제
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN DB TimeOut(Agent)[%u/%u]", pExitServerKey->GetWorldID(),
pExitServerKey->GetChannelID() );
delete pExitServerKey;
}
else
{
break;
}
}
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN TimeOut Error Return(Agent)" );
m_ExitTimer.DisableCheckTime();
m_pExitAgentTable->clear();
}
break;
}
#endif
case eEXIT_GAMESTART: // 게임 종료 패킷 보낸다
{
MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ sendMsg;
sendMsg.m_byCategory = 0;
sendMsg.m_byProtocol = SERVERCOMMON_SERVERSHUTDOWN_REQ;
for( iter = m_pExitServerTable->begin(); iter != m_pExitServerTable->end(); ++iter )
{
ExitServerKey = *(*iter);
pServerSession = ServerSessionManager::Instance()->FindServer( ExitServerKey );
if( pServerSession )
{
pServerSession->Send( (BYTE*)&sendMsg, sizeof(MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ) );
}
else
{
m_pExitServerTable->erase( iter );
break;
}
}
m_eExitType = eEXIT_GAMEWAIT;
m_ExitTimer.Reset();
m_ExitTimer.EnableCheckTime();
MessageOut( eFULL_LOG, "Send ServerExit Message" );
break;
}
case eEXIT_GAMEWAIT: // 게임 종료 패킷 보내고 대기
{
if( !m_pExitServerTable->GetDataNum() )
{
m_eExitType = eEXIT_DBPROXY;
MessageOut( eFULL_LOG, "Send ServerExit Complete" );
break;
}
// 일정 시간 이상 응답 없으면 전부 오류 처리
if( m_ExitTimer.IsExpired() )
{
ServerSession* pOpServer = GetOpServer();
SERVER_KEY* pExitServerKey = NULL;
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA ServerShutDown TimeOut Error - No RTTA Server");
return FALSE;
}
while( m_pExitServerTable->GetDataNum() )
{
pExitServerKey = m_pExitServerTable->GetHeadData();;
if( pExitServerKey )
{
// 오류 전송
sendMsg.dwError = 1;
sendMsg.m_ServerKey = *pExitServerKey;
sendMsg.dwKey = GetExitKey();
sendMsg.i64WopsKey = GetExit64Key();
if( !m_bExitFailed )
{
pOpServer->Send( (BYTE*)&sendMsg, sizeof(MSG_MO_RTTG_SERVERSHUTDOWN_ANS) );
m_bExitFailed = TRUE;
}
// 이미 처리한 서버키는 삭제
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN DB TimeOut[%u/%u/%u/%u]", pExitServerKey->GetWorldID(),
pExitServerKey->GetChannelID(),
pExitServerKey->GetServerType(),
pExitServerKey->GetServerID() );
m_pExitServerTable->Remove( pExitServerKey->m_dwKey );
delete pExitServerKey;
}
else
{
break;
}
}
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN TimeOut Error Return" );
m_ExitTimer.DisableCheckTime();
m_pExitServerTable->clear();
}
break;
}
case eEXIT_DBPROXY: // DB 종료 패킷 보낸다
{
MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ sendMsg;
sendMsg.m_byCategory = 0;
sendMsg.m_byProtocol = SERVERCOMMON_SERVERSHUTDOWN_REQ;
for( iter = m_pExitDBServerTable->begin(); iter != m_pExitDBServerTable->end(); ++iter )
{
ExitServerKey = *(*iter);
pServerSession = ServerSessionManager::Instance()->FindServer( ExitServerKey );
if( pServerSession )
{
pServerSession->Send( (BYTE*)&sendMsg, sizeof(MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ) );
}
else
{
m_pExitDBServerTable->erase( iter );
}
}
m_eExitType = eEXIT_DBPROXYWAIT;
MessageOut( eFULL_LOG, "Send DBServerExit Message" );
m_ExitTimer.Reset();
m_ExitTimer.EnableCheckTime();
break;
}
case eEXIT_DBPROXYWAIT: // DB 종료 패킷 보내고 대기
{
if( !m_pExitDBServerTable->GetDataNum() )
{
m_eExitType = eEXIT_END;
MessageOut( eFULL_LOG, "Send DBServerExit Complete" );
break;
}
// 일정 시간 이상 응답 없으면 전부 오류 처리
if( m_ExitTimer.IsExpired() )
{
ServerSession* pOpServer = GetOpServer();
SERVER_KEY* pExitServerKey = NULL;
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA ServerShutDown TimeOut Error - No RTTA Server");
return FALSE;
}
while( m_pExitDBServerTable->GetDataNum() )
{
pExitServerKey = m_pExitDBServerTable->GetHeadData();
if( pExitServerKey )
{
// 오류 전송
sendMsg.dwError = 1;
sendMsg.m_ServerKey = *pExitServerKey;
sendMsg.dwKey = GetExitKey();
sendMsg.i64WopsKey = GetExit64Key();
if( !m_bExitFailed )
{
pOpServer->Send( (BYTE*)&sendMsg, sizeof(MSG_MO_RTTG_SERVERSHUTDOWN_ANS) );
m_bExitFailed = TRUE;
}
// 이미 처리한 서버키는 삭제
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN DB TimeOut[%u/%u/%u/%u]", pExitServerKey->GetWorldID(),
pExitServerKey->GetChannelID(),
pExitServerKey->GetServerType(),
pExitServerKey->GetServerID() );
m_pExitDBServerTable->Remove( pExitServerKey->m_dwKey );
delete pExitServerKey;
}
else
{
break;
}
}
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN DB TimeOut Error Return" );
m_ExitTimer.DisableCheckTime();
m_pExitDBServerTable->clear();
}
break;
}
case eEXIT_END:
{
// 종료되었다는 응답을 RTTA에 보낸다
sendMsg.dwError = 0;
sendMsg.dwKey = GetExitKey();
sendMsg.i64WopsKey = GetExit64Key();
ServerSession* pOpServer = GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA ServerShutDown_ACK Error - No RTTA Server");
return FALSE;
}
if( !m_bExitFailed )
{
pOpServer->Send( (BYTE*)&sendMsg, sizeof(MSG_MO_RTTG_SERVERSHUTDOWN_ANS) );
m_bExitFailed = TRUE;
}
m_eExitType = eEXIT_NONE;
m_bExitServer = FALSE;
m_ExitServerKey.Set( 0, 0, 0, 0 );
m_ExitTimer.DisableCheckTime();
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN_ACK Return" );
break;
}
default: // 에러
{
break;
}
}
/*
if( m_ExitTimer.IsExpired() )
{
// 10초 이상 응답 없으면 종료 패킷 오류
MessageOut( eCRITICAL_LOG, "ExitServer Error - [%u/%u/%u/%u] No Response", m_ExitServerKey.GetWorldID(),
m_ExitServerKey.GetChannelID(),
m_ExitServerKey.GetServerType(),
m_ExitServerKey.GetServerID() );
// 오류를 알려주고 다음 서버로 넘어간다
SERVER_KEY *pExitServerKey = m_pExitServerTable->GetHeadData();
if( pExitServerKey )
{
// 오류 전송
MSG_MO_RTTG_SERVERSHUTDOWN_ANS sendMsg;
sendMsg.dwError = 1;
sendMsg.m_ServerKey = *pExitServerKey;
sendMsg.dwKey = GetExitKey();
ServerSession* pOpServer = ServerSessionManager::Instance()->GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA ServerShutDown TimeOut Error - No RTTA Server");
return FALSE;
}
pOpServer->Send( (BYTE*)&sendMsg, sizeof(MSG_MO_RTTG_SERVERSHUTDOWN_ANS) );
// 이미 처리한 서버키는 삭제
m_pExitServerTable->Remove( pExitServerKey->dwKey );
delete pExitServerKey;
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN TimeOut Error Return" );
}
SetExitType( FALSE );
dwLastTick = GetTickCount();
}
}
else
{
// 종료할 서버가 테이블에 들어 있는지 확인
if( m_pExitServerTable->GetDataNum() > 0 )
{
// 앞에 하나 지운다
util::SolarHashTable<SERVER_KEY *>::iterator iter = m_pExitServerTable->begin();
SERVER_KEY ExitServerKey = *(*iter);
ServerSession *pSession = ServerSessionManager::Instance()->FindServer( ExitServerKey );
if( !pSession )
{
//이미 종료
m_pExitServerTable->erase( iter ); // 데이터만 지운다
if( 0 == m_pExitServerTable->GetDataNum() )
{
// 종료할 서버 다 종료 했으면 끝~
m_bExitServer = FALSE;
}
}
else // 서버 있으면 종료
{
// DB Proxy 서버라면
if( pSession->GetServerType() == GAME_DBPROXY || ACCOUNT_DBPROXY )
{
// 다른 서버 종료하는 시간 동안 대기하기 위해 5초 준다
if( dwLastTick + 5000 > dwTickCount )
{
return FALSE;
}
dwLastTick = GetTickCount();
}
// 서버 타입에 따라
MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ sendMsg;
sendMsg.m_byCategory = 0;
sendMsg.m_byProtocol = SERVERCOMMON_SERVERSHUTDOWN_REQ;
pSession->Send( (BYTE*)&sendMsg, sizeof(MSG_SERVERCOMMON_SERVERSHUTDOWN_REQ) );
SetExitType( TRUE ); // 종료 요청 보냈다!
MessageOut( eFULL_LOG, "Send ServerExit Message [%u/%u/%u/%u]", pSession->GetServerKey().GetWorldID(),
pSession->GetServerKey().GetChannelID(),
pSession->GetServerKey().GetServerType(),
pSession->GetServerKey().GetServerID() );
m_ExitTimer.Reset();
dwLastTick = GetTickCount();
}
}
else
{
m_bExitServer = FALSE;
// 다 끝났다~
}
}
if( IsExitServer() && !IsExitType() && m_pExitServerTable->GetDataNum() == 0 )
{
// 완전 종료 상황이면!
SetExitServer( FALSE );
// 종료되었다는 응답을 RTTA에 보낸다
MSG_MO_RTTG_SERVERSHUTDOWN_ANS sendMsg;
sendMsg.dwError = 0;
sendMsg.dwKey = GetExitKey();
ServerSession* pOpServer = ServerSessionManager::Instance()->GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA ServerShutDown_ACK Error - No RTTA Server");
return FALSE;
}
pOpServer->Send( (BYTE*)&sendMsg, sizeof(MSG_MO_RTTG_SERVERSHUTDOWN_ANS) );
MessageOut( eFULL_LOG, "RTTA SERVERSHUTDOWN_ACK Return" );
// 다 끝났다~
SetExitServer( FALSE );
// 서버 전체 종료라면 자기 자신도 종료한다
if( m_ExitServerKey.GetWorldID() == 0 )
{
return TRUE;
}
}
*/
return FALSE;
}
// 살아 있는 서버 목록에 추가
VOID MasterServer::AddServerMap( SERVER_KEY &serverKey )
{
WORLD_MAP_ITER iter = m_WorldMap.find( serverKey.GetWorldID() );
// 해당 월드가 없으면 추가
if( iter == m_WorldMap.end() )
{
CHANNEL_MAP channelMap;
channelMap.clear();
// 채널에 서버 키도 추가
SERVERKEY_VEC serverKeyVec;
serverKeyVec.clear();
serverKeyVec.push_back( serverKey );
channelMap.insert( CHANNEL_MAP_PAIR( serverKey.GetChannelID(), serverKeyVec ) );
m_WorldMap.insert( WORLD_MAP_PAIR( serverKey.GetWorldID(), channelMap ) );
}
// 해당 월드의 채널 맵 있으면 거기에 데이터 추가
else
{
CHANNEL_MAP &channelMap = (*iter).second;
CHANNEL_MAP_ITER iterChannel = channelMap.find( serverKey.GetChannelID() );
// 해당 채널의 벡터 없으면 추가
if( iterChannel == channelMap.end() )
{
SERVERKEY_VEC serverKeyVec;
serverKeyVec.clear();
serverKeyVec.push_back( serverKey );
channelMap.insert( CHANNEL_MAP_PAIR( serverKey.GetChannelID(), serverKeyVec ) );
}
else
{
SERVERKEY_VEC &serverKeyVec = (*iterChannel).second;
serverKeyVec.push_back( serverKey );
}
}
}
VOID MasterServer::RemoveServerMap( SERVER_KEY &serverKey )
{
WORLD_MAP_ITER iterWorld;
iterWorld = m_WorldMap.find( serverKey.GetWorldID() );
if( iterWorld != m_WorldMap.end() )
{
CHANNEL_MAP_ITER &iterChannel = (*iterWorld).second.find( serverKey.GetChannelID() );
if( iterChannel != (*iterWorld).second.end() )
{
SERVERKEY_VEC_ITER iterServer;
SERVERKEY_VEC &vecServer = (*iterChannel).second;
for( iterServer = vecServer.begin(); iterServer != vecServer.end(); ++iterServer )
{
if( (*iterServer) == serverKey )
{
vecServer.erase( iterServer );
MessageOut( eFULL_LOG, "Remove Server[%u/%u/%u/%u]", serverKey.GetWorldID(),
serverKey.GetChannelID(),
serverKey.GetServerType(),
serverKey.GetServerID() );
break;
}
}
if( vecServer.empty() )
{
(*iterWorld).second.erase( iterChannel );
}
}
if( (*iterWorld).second.empty() )
{
m_WorldMap.erase( iterWorld );
}
}
}
// 게임서버들의 통화량 전달 처리
VOID MasterServer::CheckServerMoney()
{
WORLD_MAP_ITER iterWorld;
CHANNEL_MAP_ITER iterChannel;
SERVERKEY_VEC_ITER iterServer;
GameServerSession* pGameServer = NULL;
ULONGLONG ui64TempUserMoney;
MSG_MO_RTTG_USERMONEY_ANS sendMsg;
BYTE byWorldID, byChannelID;
ServerSession *pOpServer = GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA 통화량 보고 불가능:No OpServer" );
return;
}
for( iterWorld = m_WorldMap.begin(); iterWorld != m_WorldMap.end(); ++iterWorld )
{
byWorldID = (*iterWorld).first;
if(! byWorldID )
continue;
CHANNEL_MAP& mapChannel = (*iterWorld).second;
for( iterChannel = mapChannel.begin(); iterChannel != mapChannel.end(); ++iterChannel )
{
byChannelID = (*iterChannel).first;
if( !byChannelID ) continue; // 0번 채널은 보내지 않는다
SERVERKEY_VEC& vecServer = (*iterChannel).second;
sendMsg.CirculateMoney = 0;
sendMsg.ui64TotalUserMoney = 0;
sendMsg.ui64TotalGetMoney = 0;
sendMsg.ui64TotalLoseMoney = 0;
sendMsg.ui64WarehouseAveMoney = 0;
sendMsg.ui64WarehousePlusMoney = 0;
sendMsg.ui64WarehouseMinusMoney = 0;
sendMsg.JackpotMoney = 0;
sendMsg.usJackpotCnt = 0;
for( iterServer = vecServer.begin(); iterServer != vecServer.end(); ++iterServer )
{
SERVER_KEY& serverKey = (*iterServer);
switch( serverKey.GetServerType() )
{
case GAME_SERVER:
case FIELD_SERVER:
case BATTLE_SERVER:
{
pGameServer = (GameServerSession*)ServerSessionManager::Instance()->FindServer( serverKey );
if( !pGameServer )
{
vecServer.erase( iterServer );
continue;
}
ui64TempUserMoney = pGameServer->GetAveUserMoney();
sendMsg.ui64TotalUserMoney = min( sendMsg.ui64TotalUserMoney + ui64TempUserMoney, ULLONG_MAX );
ui64TempUserMoney = pGameServer->GetAveWarehouseMoney();
sendMsg.ui64WarehouseAveMoney = min( sendMsg.ui64WarehouseAveMoney + ui64TempUserMoney, ULLONG_MAX );
// 필요한 통계만 수집
for( BYTE byCnt = 0; byCnt < eSTATISTIC_MAX; byCnt++ )
{
ui64TempUserMoney = pGameServer->GetStatisticsType( byCnt );
switch( byCnt )
{
case eSTATISTIC_MONEY_CIRCULATION:
{
sendMsg.CirculateMoney = min( sendMsg.CirculateMoney + ui64TempUserMoney, ULLONG_MAX );
pGameServer->ClearStatistics( byCnt );
break;
}
case eSTATISTIC_MONEY_PLUS:
{
sendMsg.ui64TotalGetMoney = min( sendMsg.ui64TotalGetMoney + ui64TempUserMoney, ULLONG_MAX );
pGameServer->ClearStatistics( byCnt );
break;
}
case eSTATISTIC_MONEY_MINUS:
{
sendMsg.ui64TotalLoseMoney = min( sendMsg.ui64TotalLoseMoney + ui64TempUserMoney, ULLONG_MAX );
pGameServer->ClearStatistics( byCnt );
break;
}
case eSTATISTIC_MONEY_WAREHOUSE_PLUS:
{
sendMsg.ui64WarehousePlusMoney = min( sendMsg.ui64WarehousePlusMoney + ui64TempUserMoney, ULLONG_MAX );
pGameServer->ClearStatistics( byCnt );
break;
}
case eSTATISTIC_MONEY_WAREHOUSE_MINUS:
{
sendMsg.ui64WarehouseMinusMoney = min( sendMsg.ui64WarehouseMinusMoney + ui64TempUserMoney, ULLONG_MAX );
pGameServer->ClearStatistics( byCnt );
break;
}
case eSTATISTIC_JACKPOT_COUNT:
{
sendMsg.usJackpotCnt = min( sendMsg.usJackpotCnt + ui64TempUserMoney, USHRT_MAX );
pGameServer->ClearStatistics( byCnt );
break;
}
case eSTATISTIC_JACKPOT_MONEY:
{
sendMsg.JackpotMoney = min( sendMsg.JackpotMoney + ui64TempUserMoney, ULLONG_MAX );
pGameServer->ClearStatistics( byCnt );
break;
}
}
}
// WOPS에 값 넘겨 줬으니 초기화
pGameServer->ClearMoney();
break;
}
default:
{
continue;
}
}
}
// 한 채널 통화량 다 구했으면 보고
sendMsg.byWorldID = byWorldID;
sendMsg.byChannelID = byChannelID;
sendMsg.byServerType = 0;
sendMsg.byServerID = 0;
pOpServer->Send( (BYTE*)&sendMsg, sizeof(sendMsg) );
#ifdef _DEBUG
MessageOut( eFULL_LOG, "UserMoney Send[%u/%u][Get:%I64u][Lose:%I64u][User:%I64u]", sendMsg.byWorldID,
sendMsg.byChannelID,
sendMsg.ui64TotalGetMoney,
sendMsg.ui64TotalLoseMoney,
sendMsg.ui64TotalUserMoney );
MessageOut( eFULL_LOG, "WareHouseMoney Send[%u/%u][Get:%I64u][Lose:%I64u][User:%I64u]", sendMsg.byWorldID,
sendMsg.byChannelID,
sendMsg.ui64WarehousePlusMoney,
sendMsg.ui64WarehouseMinusMoney,
sendMsg.ui64WarehouseAveMoney );
MessageOut( eFULL_LOG, "JackpotMoney Send[%u/%u][Cnt:%u][Money:%I64u]", sendMsg.byWorldID,
sendMsg.byChannelID,
sendMsg.usJackpotCnt, sendMsg.JackpotMoney );
#endif
}
}
}
// 기타 통계 집계
VOID MasterServer::CheckStatistics()
{
WORLD_MAP_ITER iterWorld;
CHANNEL_MAP_ITER iterChannel;
SERVERKEY_VEC_ITER iterServer;
GameServerSession* pGameServer = NULL;
MSG_MO_RTTG_GAMESTATISTICS_ANS sendMsg;
BYTE byWorldID, byChannelID;
ULONGLONG ui64Temp = 0;
ServerSession *pOpServer = GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA 통화량 보고 불가능:No OpServer" );
return;
}
for( iterWorld = m_WorldMap.begin(); iterWorld != m_WorldMap.end(); ++iterWorld )
{
byWorldID = (*iterWorld).first;
if(! byWorldID )
continue;
CHANNEL_MAP& mapChannel = (*iterWorld).second;
for( iterChannel = mapChannel.begin(); iterChannel != mapChannel.end(); ++iterChannel )
{
byChannelID = (*iterChannel).first;
if( !byChannelID ) continue; // 0번 채널은 보내지 않는다
memset( sendMsg.ui64Statitics, 0, sizeof(sendMsg.ui64Statitics) ); // 집계할 값 초기화(채널별)
SERVERKEY_VEC& vecServer = (*iterChannel).second;
for( iterServer = vecServer.begin(); iterServer != vecServer.end(); ++iterServer )
{
SERVER_KEY& serverKey = (*iterServer);
switch( serverKey.GetServerType() )
{
case GAME_SERVER:
case FIELD_SERVER:
case BATTLE_SERVER:
{
pGameServer = (GameServerSession*)ServerSessionManager::Instance()->FindServer( serverKey );
if( !pGameServer )
{
vecServer.erase( iterServer );
continue;
}
// 모든 통계 수집
for( BYTE byCnt = 0; byCnt < eSTATISTIC_MAX; byCnt++ )
{
switch( byCnt )
{
case eSTATISTIC_MONEY_CIRCULATION:
case eSTATISTIC_MONEY_PLUS:
case eSTATISTIC_MONEY_MINUS:
case eSTATISTIC_MONEY_WAREHOUSE_PLUS:
case eSTATISTIC_MONEY_WAREHOUSE_MINUS:
case eSTATISTIC_JACKPOT_COUNT:
case eSTATISTIC_JACKPOT_MONEY:
{
// 기존 통화량 정보에 포함되는 값들은 또 넣을 필요 없다
break;
}
default:
{
ui64Temp = pGameServer->GetStatisticsType( byCnt );
pGameServer->ClearStatistics( byCnt );
sendMsg.ui64Statitics[byCnt] = min( sendMsg.ui64Statitics[byCnt] + ui64Temp, ULLONG_MAX );
break;
}
}
}
}
}
}
// 한 채널 값 다 모았으면 전송
sendMsg.byWorldID = byWorldID;
sendMsg.byChannelID = byChannelID;
sendMsg.byServerType = 0;
sendMsg.byServerID = 0;
pOpServer->Send( (BYTE*)&sendMsg, sizeof(sendMsg) );
#ifdef _DEBUG
MessageOut( eFULL_LOG, "GameStatistics Send[%u/%u]", sendMsg.byWorldID, sendMsg.byChannelID );
#endif
}
}
}
// 창고 통화량
VOID MasterServer::CheckServerWarehouseMoney()
{
WORLD_MAP_ITER iterWorld;
CHANNEL_MAP_ITER iterChannel;
SERVERKEY_VEC_ITER iterServer;
GameServerSession* pGameServer = NULL;
ULONGLONG ui64TotalGetMoney, ui64TotalLoseMoney, ui64TotalUserMoney;
ULONGLONG ui64TempGetMoney, ui64TempLoseMoney, ui64TempUserMoney;
MSG_MO_RTTG_WAREHOUSEMONEY_ANS sendMsg;
BYTE byWorldID, byChannelID;
ServerSession *pOpServer = GetOpServer();
if( !pOpServer )
{
MessageOut( eCRITICAL_LOG, "RTTA 통화량 보고 불가능:No OpServer" );
return;
}
for( iterWorld = m_WorldMap.begin(); iterWorld != m_WorldMap.end(); ++iterWorld )
{
byWorldID = (*iterWorld).first;
if(! byWorldID )
continue;
CHANNEL_MAP& mapChannel = (*iterWorld).second;
for( iterChannel = mapChannel.begin(); iterChannel != mapChannel.end(); ++iterChannel )
{
ui64TotalGetMoney = 0;
ui64TotalLoseMoney = 0;
ui64TotalUserMoney = 0;
byChannelID = (*iterChannel).first;
if( !byChannelID ) continue; // 0번 채널은 보내지 않는다
SERVERKEY_VEC& vecServer = (*iterChannel).second;
for( iterServer = vecServer.begin(); iterServer != vecServer.end(); ++iterServer )
{
SERVER_KEY& serverKey = (*iterServer);
switch( serverKey.GetServerType() )
{
case GAME_SERVER:
case FIELD_SERVER:
case BATTLE_SERVER:
{
pGameServer = (GameServerSession*)ServerSessionManager::Instance()->FindServer( serverKey );
if( !pGameServer )
{
vecServer.erase( iterServer );
continue;
}
ui64TempGetMoney = pGameServer->GetTotalGetMoney();
ui64TempLoseMoney = pGameServer->GetTotalLoseMoney();
ui64TempUserMoney = pGameServer->GetAveUserMoney();
if( 0xffffffffffffffff - ui64TotalGetMoney < ui64TempGetMoney )
{
ui64TotalGetMoney = 0xffffffffffffffff;
}
else
{
ui64TotalGetMoney += pGameServer->GetTotalGetMoney();
}
if( 0xffffffffffffffff - ui64TotalLoseMoney < ui64TempLoseMoney )
{
ui64TotalLoseMoney = 0xffffffffffffffff;
}
else
{
ui64TotalLoseMoney += ui64TempLoseMoney;
}
if( 0xffffffffffffffff - ui64TotalUserMoney < ui64TempUserMoney )
{
ui64TotalUserMoney = 0xffffffffffffffff;
}
else
{
ui64TotalUserMoney += ui64TempUserMoney;
}
// WOPS에 값 넘겨 줬으니 초기화
pGameServer->ClearWarehouseMoney();
break;
}
default:
{
continue;
}
}
}
// 한 채널 통화량 다 구했으면 보고
sendMsg.byWorldID = byWorldID;
sendMsg.byChannelID = byChannelID;
sendMsg.byServerType = 0;
sendMsg.byServerID = 0;
sendMsg.ui64AveMoney = ui64TotalUserMoney;
sendMsg.ui64TotalPlusMoney = ui64TotalGetMoney;
sendMsg.ui64TotalMinusMoney = ui64TotalLoseMoney;
pOpServer->Send( (BYTE*)&sendMsg, sizeof(sendMsg) );
#ifdef _DEBUG
MessageOut( eFULL_LOG, "WareHouseMoney Send[%u/%u][Get:%u][Lose:%u][User:%u]", sendMsg.byWorldID,
sendMsg.byChannelID,
sendMsg.ui64TotalPlusMoney,
sendMsg.ui64TotalMinusMoney,
sendMsg.ui64AveMoney );
#endif
}
}
}
VOID MasterServer::SendMoneyReq()
{
// 모든 게임서버에 전달~
std::vector<ServerSession*> gameList;
std::vector<ServerSession*>::iterator iter;
gameList = ServerSessionManager::Instance()->GetGameServerList();
if( gameList.empty() )
{
// 비어 있으면 줄 값이 없다
return;
}
// MSG_GM_OPERATION_USERMONEY_REQ sendMsg;
MSG_GM_OPERATION_STATISTICS_REQ sendMsg;
ServerSession *pGameServer = NULL;
for( iter = gameList.begin(); iter < gameList.end(); iter++ )
{
// 전달~
pGameServer = (*iter);
pGameServer->Send( (BYTE*)&sendMsg, sizeof(sendMsg) );
}
}
#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__
VOID MasterServer::StartDataService()
{
MessageOut( eCRITICAL_LOG, "All Data Loaded!\n" );
//#ifdef __WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__TEST__
// MasterServer::Instance()->StartListen();
//#endif
// 연결된 모든 서버들에게 PHASE_SHIFT_CMD :: PHASE_EXCHANGE 명령을 보낸다.
// 연결된 서버들은 데이터 요청할 것이다.
// 이전 코드 <060802>
ServerSessionManager::Instance()->Send_PHASE_SHIFT_CMD_AllBroadcast( PHASE_EXCHANGE, PHASE_SERVICE );
// 위와 같이 하면, PHASE_SERVICE 상태에 있는 경우라도 일시적으로 PHASE_EXCHANGE로
// 전환되어 데이터 교환을 할 것이다. 하지만, 모두 읽은 경우라도 지정된 Control Flag만을
// 읽어들이고 다시 SERVICE 로 전환되게 하자.
// 마스터가 데이터를 읽어들이기 전에 요청할 경우는 대기 상태가 될 텐데, 이 경우도 적용받게
// 될 것이다.
// 이후 별도로 접속하는 경우라면, 개별 PHASE_EXCHANGE 명령을 통해 데이터를 받게 될 것이다.
// 완료 명령이 올 경우, Field, Battle, DB 등등은 바로 PHASE_SERVICE 로 전환한다.
// Agent, World 등의 Front 계열 또는 다른 서버들과의 연관도가 있는 경우
// 늦게 PHASE_SERVICE로 전환시킨다. <- 어떻게 찾아야 하는 지, 감시해야 하는지를 판단할 것.
}
#endif //__WAVERIX_SOLAR_AUTH_SERVER_IMPLEMENTATION__