DragonNest/Server/DNGameServer/DNGameServerManager.cpp
2024-12-20 16:56:44 +08:00

542 lines
No EOL
30 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.


#include "stdafx.h"
#include "DNIocpManager.h"
#include "DNGameServerManager.h"
#include "DNRUDPGameServer.h"
#include "DNUserSession.h"
#include "DNGameRoom.h"
#include "DNServiceConnection.h"
#include "DNUserTcpConnection.h"
#include "DNBackGroundLoader.h"
#include "log.h"
extern TGameConfig g_Config;
CDNGameServerManager * g_pGameServerManager = NULL;
CDNGameServerManager::CDNGameServerManager()
{
if (InitializeRUDP() == false)
g_Log.Log(LogType::_ERROR, L"UDP Init Err\n");
#ifndef _SKIP_BLOCK
m_nLastCheckTick = 0;
#endif
MAGAReqRoomID Packet;
memset( &Packet, 0, sizeof(Packet) );
m_pRootRoom = new CDNGameRoom( NULL, 0, &Packet );
m_GameRoomList.reserve( MAX_SESSION_COUNT );
m_RoomCountInfo.reserve( MAX_SESSION_COUNT );
CDNGameServerManager::sRoomstate Room;
for( UINT i=0 ; i<MAX_SESSION_COUNT ; ++i )
{
m_GameRoomList.push_back(Room);
m_RoomCountInfo.push_back( std::make_pair(0,0) );
}
m_uiRoomCount = 0;
m_bZeroPopulation = false;
m_iThreadCount = 0;
}
CDNGameServerManager::~CDNGameServerManager()
{
g_Log.Log(LogType::_NORMAL, L"graceful close server\n");
for (unsigned int i = 0; i < m_GameServerList.size(); i++)
delete m_GameServerList[i];
m_GameServerList.clear();
m_pRootRoom->FinalizeGameRoom();
SAFE_DELETE( m_pRootRoom );
SAFE_DELETE(g_pBackLoader);
}
bool CDNGameServerManager::InitializeRUDP()
{
WSADATA WSAData;
if (WSAStartup(MAKEWORD(1, 1), &WSAData))
return false;
return true;
}
void CDNGameServerManager::CalcOpenCount(int nProcessorCount, int nCreateCount, int nCreateIndex, int &nOpenCount, int &nStartAffinityCount, int &nBackLoaderIndex)
{
int nDivine = nProcessorCount/(nCreateCount == 0 ? 1 : nCreateCount);
//int nOpenMinus = nDivine > 4 ? 2 : 1;
int nOpenMinus = 1;
nOpenCount = (nDivine - nOpenMinus) <= 0 ? 1 : nDivine - nOpenMinus;
nStartAffinityCount = nDivine * nCreateIndex;
nBackLoaderIndex = (nDivine * (nCreateIndex + 1)) - nOpenMinus;
}
void CDNGameServerManager::PreOpenGameServer()
{
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
int nOpenThreadCount, nAffinityStartCount, nBackLoaderAffinityIndex;
CalcOpenCount(SysInfo.dwNumberOfProcessors, g_Config.nCreateCount, g_Config.nCreateIndex, nOpenThreadCount, nAffinityStartCount, nBackLoaderAffinityIndex);
m_iThreadCount = nOpenThreadCount;
}
bool CDNGameServerManager::StartGameServer(USHORT nBeginAcceptPort, const char * pExternalProbeIP, unsigned short nExternalProbePort)
{
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
int nOpenThreadCount, nAffinityStartCount, nBackLoaderAffinityIndex;
CalcOpenCount(SysInfo.dwNumberOfProcessors, g_Config.nCreateCount, g_Config.nCreateIndex, nOpenThreadCount, nAffinityStartCount, nBackLoaderAffinityIndex);
m_iThreadCount = nOpenThreadCount;
int nThreadCount;
for (nThreadCount = 0; nThreadCount < m_iThreadCount; nThreadCount++, nAffinityStartCount++)
{//우훙~ 고고슁!
CDNRUDPGameServer * pServer = new CDNRUDPGameServer(this, pExternalProbeIP, nExternalProbePort);
if (pServer->Start(nBeginAcceptPort+nThreadCount, nThreadCount, nAffinityStartCount) == false)
{
_tprintf(_T("Cant Open RUDP Port [cnt:%d][%d]\n"), nThreadCount, nBeginAcceptPort+nThreadCount);
return false;
}
m_GameServerList.push_back(pServer);
Sleep(10);
}
g_pBackLoader = new CDNBackGroundLoader;
if (!g_pBackLoader)
return false;
if( !g_pBackLoader->SetThreadAffinityMask( 1 << nBackLoaderAffinityIndex ) )
{
std::cout << "SetThreadAffinityMask() Failed!!!" << std::endl;
return false;
}
return true;
}
void CDNGameServerManager::GetGameServerInfo(unsigned long * iIP, unsigned short * iPort, unsigned char * cIdx, bool * margin)
{
if (iIP == NULL || iPort == NULL)
{
//나오면 안되겠지요? 어따구로 짠거야아~
_DANGER_POINT();
return;
}
//정보가 늘어나면(예를 들어 특별한 게임타입을 지원하는 서버라든가 하는거) 구조체로~ 일단은 후후
for (int i = 0; i < (int)m_GameServerList.size(); i++)
{
m_GameServerList[i]->GetAddress(&iIP[i], &iPort[i]);
cIdx[i] = m_GameServerList[i]->GetServerID();
margin[i] = m_GameServerList[i]->IsAcceptRoom();
}
}
CDNRUDPGameServer * CDNGameServerManager::GetGameServer()
{
//각각의 udp server의 프로세스 호출되는 타이밍을 체킹해서 적은 부하로 돌고 있는 곳에 배정 하는 식으로 하자
//이 펑션으로 서버를 Get하는 상황은 최초 생성 타이밍만 있다!
ScopeLock<CSyncLock> Lock( m_Lock );
//랜덤하게 뽑아서 받을 수 있는지 확인하자!
int nCnt = 0;
CDNRUDPGameServer* pServer = NULL;
while (nCnt < 24)
{
unsigned int id;
errno_t err = rand_s(&id);
if (err == 0)
{
pServer = m_GameServerList[(unsigned int)((double)id / (double)UINT_MAX * m_GameServerList.size())];
if (pServer && pServer->IsAcceptRoom())
return pServer;
}
else _DANGER_POINT();
nCnt++;
}
_DANGER_POINT();
return pServer;
}
CDNRUDPGameServer * CDNGameServerManager::GetGameServerByUID(unsigned int iUID)
{
ScopeLock<CSyncLock> Lock( m_Lock );
CDNRUDPGameServer * pServer = NULL;
std::map <unsigned int, CDNRUDPGameServer*>::iterator ii = m_UserConnectionInfoList.find(iUID);
if (ii != m_UserConnectionInfoList.end())
pServer = (*ii).second;
else
_DANGER_POINT();
return pServer;
}
CDNRUDPGameServer * CDNGameServerManager::GetGameServerIndex(unsigned int index)
{
ScopeLock<CSyncLock> Lock( m_Lock );
CDNRUDPGameServer * pServer = NULL;
if ( index < m_GameServerList.size() )
pServer = m_GameServerList[index];
return pServer;
}
CDNRUDPGameServer * CDNGameServerManager::GetGameServerByAID(unsigned int iAccountDBID)
{
ScopeLock<CSyncLock> Lock( m_UserLock );
CDNRUDPGameServer * pServer = NULL;
std::map <unsigned int, CDNRUDPGameServer*>::iterator ii = m_UserConnectionInfoListByAccountDBID.find(iAccountDBID);
if (ii != m_UserConnectionInfoListByAccountDBID.end())
pServer = (*ii).second;
return pServer;
}
CDNRUDPGameServer * CDNGameServerManager::GetGameServerByRoomID(unsigned int iRoomID)
{
ScopeLock<CSyncLock> Lock( m_Lock );
if( iRoomID < m_GameRoomList.size() )
{
return m_GameRoomList[iRoomID].bCrashed == false ? m_GameRoomList[iRoomID].pServer : NULL;
}
else
{
_DANGER_POINT();
return NULL;
}
}
unsigned int CDNGameServerManager::GenRoomID(CDNRUDPGameServer * pServer)
{
ScopeLock<CSyncLock> Lock( m_Lock );
if( GetRoomCount()+1 >= MAX_SESSION_COUNT )
{
g_Log.Log(LogType::_ERROR, L"[%d] CDNGameServerManager::GenRoomID() Room OverFlow %d/%d\n", g_Config.nManagedID, GetRoomCount()+1, MAX_SESSION_COUNT );
return 0;
}
while(true)
{
unsigned int id;
errno_t err = rand_s(&id);
if( err == 0 )
{
unsigned int nRoomID = (unsigned int)((double)id / (double)UINT_MAX * (MAX_SESSION_COUNT-1)) + 1;
if( nRoomID < m_GameRoomList.size() && m_GameRoomList[nRoomID].bCrashed == false && m_GameRoomList[nRoomID].pServer == NULL )
{
m_GameRoomList[nRoomID].bCrashed = false;
m_GameRoomList[nRoomID].pServer = pServer;
++m_uiRoomCount;
return nRoomID;
}
}
else
_DANGER_POINT();
}
return 0;
}
bool CDNGameServerManager::VerifyUserIDs(UINT nAccountDBID, UINT nSessionID)
{
std::map <unsigned int, CDNRUDPGameServer*>:: iterator ii = m_UserConnectionInfoList.find(nSessionID);
if (ii != m_UserConnectionInfoList.end()) return false;
ii = m_UserConnectionInfoListByAccountDBID.find(nAccountDBID);
if (ii != m_UserConnectionInfoListByAccountDBID.end()) return false;
return true;
}
void CDNGameServerManager::AddGameUser(UINT nRoomID, UINT nAccountDBID, UINT nSessionID, CDNRUDPGameServer * pServer)
{
{
ScopeLock<CSyncLock> Lock( m_UserLock );
std::map <unsigned int, CDNRUDPGameServer*>::iterator ii = m_UserConnectionInfoList.find(nSessionID);
if (ii != m_UserConnectionInfoList.end())
g_Log.Log(LogType::_ERROR, L"GameServer AddUser Err [SID:%d]\n", nSessionID);
else
{
m_UserConnectionInfoList[nSessionID] = pServer;
m_UserConnectionInfoListByAccountDBID[nAccountDBID] = pServer;
}
}
{
ScopeLock<CSyncLock> Lock( m_Lock );
if( nRoomID < m_RoomCountInfo.size() )
++m_RoomCountInfo[nRoomID].second;
else
_DANGER_POINT();
}
}
void CDNGameServerManager::RemoveGameRoom(unsigned int nRoomID, CDNRUDPGameServer * pServer, bool bCrashed/* = false*/)
{
ScopeLock<CSyncLock> Lock( m_Lock );
if( nRoomID < m_GameRoomList.size() && m_GameRoomList[nRoomID].pServer )
{
#if defined( PRE_THREAD_ROOMDESTROY )
g_pBackLoader->DestroyConfirm( nRoomID );
#endif // #if defined( PRE_THREAD_ROOMDESTROY )
m_GameRoomList[nRoomID].bCrashed = bCrashed;
m_GameRoomList[nRoomID].pServer = NULL;
if(m_uiRoomCount)
--m_uiRoomCount;
}
else
_DANGER_POINT();
if( nRoomID < m_RoomCountInfo.size() )
m_RoomCountInfo[nRoomID] = std::make_pair(0,0);
else
_DANGER_POINT();
}
void CDNGameServerManager::RemoveGameUser(unsigned int nRoomID, unsigned int nAccountDBID, unsigned int nSessionID, CDNRUDPGameServer * pServer)
{
{
ScopeLock<CSyncLock> Lock( m_UserLock );
std::map <unsigned int, CDNRUDPGameServer*>::iterator ii = m_UserConnectionInfoList.find(nSessionID);
if (ii != m_UserConnectionInfoList.end())
{
m_UserConnectionInfoList.erase(ii);
if (m_UserConnectionInfoListByAccountDBID.find(nAccountDBID) != m_UserConnectionInfoListByAccountDBID.end())
m_UserConnectionInfoListByAccountDBID.erase(m_UserConnectionInfoListByAccountDBID.find(nAccountDBID));
else
_DANGER_POINT();
} else
g_Log.Log(LogType::_ERROR, L"GameServer RemoveUser Err [SID:%d]\n", nSessionID);
}
{
ScopeLock<CSyncLock> Lock( m_Lock );
if( nRoomID < m_RoomCountInfo.size() )
{
if( m_RoomCountInfo[nRoomID].second )
--m_RoomCountInfo[nRoomID].second;
}
else
_DANGER_POINT();
}
}
void CDNGameServerManager::UpdateRoomCountInfo(unsigned int nRoomID, int nMapIdx)
{
ScopeLock<CSyncLock> Lock( m_Lock );
if( nRoomID < m_RoomCountInfo.size() )
m_RoomCountInfo[nRoomID].first = nMapIdx;
else
_DANGER_POINT();
}
void CDNGameServerManager::GetRoomUserCount(UINT &nUserCount, UINT &nRoomCount, UINT &nTotalRoomCount)
{
ScopeLock<CSyncLock> Lock( m_Lock );
nUserCount = (UINT)m_UserConnectionInfoList.size();
nTotalRoomCount = (UINT)m_RoomCountInfo.size();
nRoomCount = m_uiRoomCount;
}
int CDNGameServerManager::GetRoomCount()
{
ScopeLock<CSyncLock> Lock( m_Lock );
return static_cast<int>(m_uiRoomCount);
}
void CDNGameServerManager::DestroyAllGameRoom()
{
std::vector <CDNRUDPGameServer*>::iterator ii;
for (ii = m_GameServerList.begin(); ii != m_GameServerList.end(); ii++)
(*ii)->StoreExternalBuffer(0, IN_DESTROY, 0, NULL, 0, EXTERNALTYPE_SERVICEMANAGER);
}
void CDNGameServerManager::CheckCloseGameServer(CDNRUDPGameServer * pServer)
{
//각 서버 쓰레드들이 모든 게임룸을 지우고 각유저의 모든 데이터를 센딩했으면
bool bCheck = true;
std::vector <CDNRUDPGameServer*>::iterator ii;
for (ii = m_GameServerList.begin(); ii != m_GameServerList.end(); ii++)
{
if ((*ii)->GetFlushSaveData() == false)
bCheck = false;
}
if (bCheck)
{
if (g_pServiceConnection) //서비스매니저에게 처리할꺼 다 끝났다고 알린다.
g_pServiceConnection->SendServiceClosed();
}
}
void CDNGameServerManager::MasterDisConnected(int nWorldSetID)
{
std::vector <CDNRUDPGameServer*>::iterator ii;
for (ii = m_GameServerList.begin(); ii != m_GameServerList.end(); ii++)
#if defined( PRE_WORLDCOMBINE_PARTY )
(*ii)->StoreExternalBuffer(0, IN_DESTROY, 0, NULL, 0, EXTERNALTYPE_MASTER, 0, nWorldSetID );
#else
(*ii)->StoreExternalBuffer(0, IN_DESTROY, 0, NULL, 0, EXTERNALTYPE_MASTER);
#endif // #if defined( PRE_WORLDCOMBINE_PARTY )
}
#ifdef _SKIP_BLOCK
CDNTcpConnection * CDNGameServerManager::CreateTcpConnection(const char * pIp, const int nPort)
{
CDNTcpConnection * pTcpCon = new CDNTcpConnection(this);
if( pTcpCon == NULL )
return NULL;
pTcpCon->SetIp(pIp);
pTcpCon->SetPort(nPort);
return pTcpCon;
}
#else
CDNTcpConnection * CDNGameServerManager::CreateTcpConnection(const char * pIp, const int nPort)
{
ScopeLock<CSyncLock> Lock( m_ConSync );
CDNTcpConnection * pTcpCon = new CDNTcpConnection(this);
if( pTcpCon == NULL )
return NULL;
_TCPCON * pCon = new _TCPCON;
if( pCon == NULL )
return NULL;
pCon->nCreateTick = timeGetTime();
pCon->pCon = pTcpCon;
m_TcpConnectionList.push_back(pCon);
pTcpCon->SetIp(pIp);
pTcpCon->SetPort(nPort);
return pTcpCon;
}
void CDNGameServerManager::PushToEjectTcpConnection(CDNTcpConnection * pCon, CDNUserSession * pSession) //with Async
{
m_EjectTcpConnectionList.push_back(std::make_pair(pCon, pSession));
}
void CDNGameServerManager::PushOrphanPtr(CDNTcpConnection * pCon)
{
m_OrphanTcpConnectionList.push_back(pCon);
}
void CDNGameServerManager::FlushConnectionBuffer(ULONG nCurTick)
{
//요그에 계속 남아 계시면....짜증나는 싱글 병목발생 가능.... 여기는 초기에 잠시만 담고 관리및 삭제는 세팅이되면 GameServer단이 관장
ScopeLock<CSyncLock> Lock( m_ConSync );
for( std::list<_TCPCON*>::iterator ii = m_TcpConnectionList.begin(); ii != m_TcpConnectionList.end(); )
{
if ((*ii)->pCon->FlushRecvData(0) == false)
{
//머냐 tcp 컨넥해서 옮겨지기도 전에.....-_-;
//이건 그냥 임시 컨테이너에서 지우는 것뿐 실재적인 Meber객체는 삭제는 처리해야함...
//여기서는 방법이 없삼.....각룸단 또는 서버단의 idle에서 연결판단해서 지우는 좀비 처리 합쉬다.
g_pIocpManager->ClearSocketContext((*ii)->pCon->GetSocketContext());
(*ii)->pCon->SetSocketContext(NULL, NULL);
SAFE_DELETE((*ii));
ii = m_TcpConnectionList.erase(ii);
}
else
ii++;
}
EjectTcpConnection();
OrphanPtr();
//넘후 자주 불릴 필요는 없어요~
if (m_nLastCheckTick == 0)
m_nLastCheckTick = nCurTick;
if (m_nLastCheckTick != 0 && nCurTick - m_nLastCheckTick > (5*1000*60)) //대략 5분에 한번씩 체킹해보아요
{
CheckOrphan(nCurTick);
m_nLastCheckTick = nCurTick;
}
}
#endif
void CDNGameServerManager::ConnectedWorld(char cWorldID)
{
std::vector <CDNRUDPGameServer*>::iterator ii;
for (ii = m_GameServerList.begin(); ii != m_GameServerList.end(); ii++)
(*ii)->SendRestoreMaster(cWorldID);
}
#ifndef _SKIP_BLOCK
void CDNGameServerManager::CheckOrphan(ULONG nCurTick)
{
//tcp connect처리는 되었지만 connect msg를 보내지 않아(거나 또는 매칭이 안되어서) 미아가된 녀석을 찾아서 지워주기
for( std::list<_TCPCON*>::iterator ii = m_TcpConnectionList.begin(); ii != m_TcpConnectionList.end();)
{
if (nCurTick - (*ii)->nCreateTick > (3*60*1000)) //한 삼분쯤?...
{
g_pIocpManager->ClearSocketContext((*ii)->pCon->GetSocketContext());
(*ii)->pCon->SetSocketContext(NULL, NULL);
SAFE_DELETE((*ii));
ii = m_TcpConnectionList.erase(ii);
}
else
ii++;
}
}
void CDNGameServerManager::OrphanPtr()
{
std::vector <CDNTcpConnection*>::iterator ii;
for (ii = m_OrphanTcpConnectionList.begin(); ii != m_OrphanTcpConnectionList.end(); ii++)
{
g_pIocpManager->ClearSocketContext((*ii)->GetSocketContext());
(*ii)->SetSocketContext(NULL, NULL);
SAFE_DELETE((*ii));
}
m_OrphanTcpConnectionList.clear();
}
void CDNGameServerManager::EjectTcpConnection()
{
DNVector(std::pair<CDNTcpConnection*, CDNUserSession*>)::iterator ii;
for (ii = m_EjectTcpConnectionList.begin(); ii != m_EjectTcpConnectionList.end(); ii++)
{
for ( std::list<_TCPCON*>::iterator hi = m_TcpConnectionList.begin(); hi != m_TcpConnectionList.end();)
{
if ((*ii).first == (*hi)->pCon)
{
_TCPCON * pTcpCon = (*hi);
if (pTcpCon->pCon->SetSession((*ii).second) == false)
_DANGER_POINT();
hi = m_TcpConnectionList.erase(hi);
}
else
hi++;
}
}
m_EjectTcpConnectionList.clear();
}
#endif