Files
Sun1602/Server/MasterServer/MasterServer.cpp
T
git 9cb257e7ae Add GameClients drops and keep current 1602 source work.
CN1602 is the local client. KR is the official tree with unpacked Data, System, and Sound. Packed .wpk files stay off git.
2026-08-30 23:24:31 +03: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 report failed: 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, "Received shutdown request [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 concurrent-user report failed: 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, "World concurrent users: 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 report impossible: 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 report impossible: 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 report impossible: 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__