Files
Client/Server/RylServerProject/RylGameServer/RylGameServerMainLoop.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

531 lines
16 KiB
C++

#include "stdafx.h"
#include <Creature/CreatureManager.h>
#include <Creature/Character/Character.h>
#include <Creature/Character/CharRespawnMgr.h>
#include <Creature/Character/SphereTree/CharSphereTree.h>
#include <Creature/Monster/Monster.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Castle/CastleMgr.h>
#include <Map/FieldMap/Cell.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Network/Session/Session.h>
#include <Network/Session/LimitUserByIP.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharLoginOut.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentPacketParse.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Network/Packet/PacketStruct/CharLoginOutPacketStruct.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/IOCP/IOCPNet.h>
#include <Quest/QuestMgr.h>
#include <GameTime/GameTimeMgr.h>
#include <Pattern/Command.h>
#include <Utility/Time/Pulse/Pulse.h>
#include <Log/GameLog.h>
#include <Skill/Spell/GlobalSpellMgr.h>
#include <Community/Party/PartyMgr.h>
#include <algorithm>
#include "mmsystem.h"
#include "RylGameServer.h"
#include "./Commands/DummyCharacters.h"
struct FnDisconnectCharacter
{
bool operator() (CCharacter* lpCharacter)
{
if (NULL != lpCharacter)
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
lpDispatch->Disconnect();
}
}
return true;
}
};
struct FnRegenHPAndMP
{
bool operator() (CAggresiveCreature* lpAggresiveCreature)
{
if (NULL != lpAggresiveCreature)
{
lpAggresiveCreature->RegenHPAndMP(0, 0, true);
}
return true;
}
};
struct FnCSAuth
{
bool operator() (CCharacter* lpCharacter)
{
if (NULL != lpCharacter)
{
// edith 2009.08.11 게임가드 테스트
if (0 == lpCharacter->GetAdminLevel())
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
/*
// edith 2009.08.11 게임가드 2.5 업그레이드
if (false == lpDispatch->IsAuth())
{
ERRLOG1(g_Log, "CID:0x%08x 게임 가드 인증 코드를 보내지않아 연결을 끊습니다.", lpCharacter->GetCID());
lpDispatch->Disconnect();
return true;
}
*/
GG_AUTH_DATA* lpAuthData = NULL;
if (false == lpDispatch->GetAuthQuery(&lpAuthData))
{
ERRLOG1(g_Log, "CID:0x%08x 게임 가드 인증 코드(2) 체크에 실패하여 연결을 끊습니다.", lpCharacter->GetCID());
lpDispatch->Disconnect();
return true;
}
// edith 2009.08.11 게임가드 2.5 업그레이드
// MessageBox(NULL, "게임가드 인증 시간 체크 결과", "GG", MB_OK);
return GameClientSendPacket::SendCSAuth(lpDispatch->GetSendStream(),
lpCharacter->GetCID(), lpDispatch->GetAuthCode(), lpAuthData, PktBase::NO_SERVER_ERR);
}
}
}
return true;
}
};
struct InstrumentInfo
{
InstrumentInfo(unsigned long dwIndex, char* szName) : m_dwIndex(dwIndex), m_szName(szName) { }
unsigned long m_dwIndex;
char* m_szName;
};
struct PrepareBroadcastData
{
void operator() (CCharacter* lpCharacter)
{ if(0 != lpCharacter) { lpCharacter->GetSerializeData().PrepareData(*lpCharacter); } }
void operator() (CMonster* lpMonster)
{ if(0 != lpMonster) { lpMonster->GetSerializeData().PrepareData(*lpMonster); } }
};
enum InstrumentsType
{
INSTRUMENT_TOTAL_LOOP,
INSTRUMENT_CHECK_DELETE_ITEM,
INSTRUMENT_REGEN_HP_MP,
INSTRUMENT_DBUPDATE,
INSTRUMENT_CELLBROADCASTING,
INSTRUMENT_GLOBALSPELLMGR,
INSTRUMENT_PROCESS_ALL_MONSTER,
INSTRUMENT_CHARACTER_LOGOUT,
INSTRUMENT_PROCESS_RESPAWN_QUEUE,
INSTRUMENT_AUTO_RESPAWN,
INSTRUMENT_VIRTUALAREA,
INSTRUMENT_RESPAWN,
INSTRUMENT_CHAR_SPHERE_TREE,
INSTRUMENT_WORLDWEAPON_FIRE,
MAX_PERFORMANCE_INSTRUMENTS
};
#define DECLARE_INSTRUMENT_INFO(name) InstrumentInfo(name, #name)
const InstrumentInfo g_InstrumentInfo[MAX_PERFORMANCE_INSTRUMENTS] =
{
DECLARE_INSTRUMENT_INFO(INSTRUMENT_TOTAL_LOOP),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_CHECK_DELETE_ITEM),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_REGEN_HP_MP),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_DBUPDATE),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_CELLBROADCASTING),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_GLOBALSPELLMGR),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_PROCESS_ALL_MONSTER),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_CHARACTER_LOGOUT),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_PROCESS_RESPAWN_QUEUE),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_AUTO_RESPAWN),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_VIRTUALAREA),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_RESPAWN),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_CHAR_SPHERE_TREE),
DECLARE_INSTRUMENT_INFO(INSTRUMENT_WORLDWEAPON_FIRE)
};
#define INSTRUMENT_GAMESERVER(object, name) PERFORMANCE_CHECK(CAutoInstrument (name)((object)[(name)]));
class CGameServerProcessThread : public CProcessThread
{
public:
enum
{
GAME_PROCESS_TICK = 100,
TICKS_PER_SECOND = 1000 / GAME_PROCESS_TICK
};
CGameServerProcessThread(CRylGameServer& RylGameServer);
virtual void InternalRun(CPulse& Pulse);
virtual void Cleanup(CPulse& Pulse);
private:
CRylGameServer& m_RylGameServer;
CCreatureManager& m_CreatureManager;
CCellManager& m_CellManager;
CDuelCellManager& m_DuelCellManager;
CGlobalSpellMgr& m_GlobalSpellMgr;
CGameLog& m_GameLog;
Castle::CCastleMgr& m_CastleMgr;
CSiegeObjectMgr& m_SiegeObjectMgr;
CCharRespawnMgr& m_CharRespawnMgr;
CCharSphereTree& m_CharSphereTree;
VirtualArea::CVirtualAreaMgr& m_VirtualAreaMgr;
CPerformanceInstrument m_Instruments[MAX_PERFORMANCE_INSTRUMENTS];
std::mem_fun_t<bool, CMonster> m_processMonster;
std::mem_fun_ref_t<void, CCell> m_processPrepareBroadCast;
std::mem_fun1_ref_t<void, CCell, unsigned long> m_processBroadCast;
};
bool CRylGameServer::AddGameProcessThread()
{
return AddProcessThread(new CGameServerProcessThread(*this));
}
CGameServerProcessThread::CGameServerProcessThread(CRylGameServer& RylGameServer)
: CProcessThread(RylGameServer, GAME_PROCESS_TICK),
m_RylGameServer(RylGameServer),
m_CreatureManager(CCreatureManager::GetInstance()),
m_CellManager(CCellManager::GetInstance()),
m_VirtualAreaMgr(VirtualArea::CVirtualAreaMgr::GetInstance()),
m_DuelCellManager(CDuelCellManager::GetInstance()),
m_GlobalSpellMgr(CGlobalSpellMgr::GetInstance()),
m_GameLog(CGameLog::GetInstance()),
m_CastleMgr(Castle::CCastleMgr::GetInstance()),
m_SiegeObjectMgr(CSiegeObjectMgr::GetInstance()),
m_processMonster(&CMonster::Process),
m_processPrepareBroadCast(&CCell::PrepareBroadCast),
m_processBroadCast(&CCell::BroadCast),
m_CharRespawnMgr(CCharRespawnMgr::GetInstance()),
m_CharSphereTree(CCharSphereTree::GetInstance())
{
for(unsigned int nCount = 0; nCount < MAX_PERFORMANCE_INSTRUMENTS; ++nCount)
{
m_Instruments[nCount].SetName(g_InstrumentInfo[nCount].m_szName);
}
}
void CGameServerProcessThread::Cleanup(CPulse& Pulse)
{
CLimitUserByIP* lpLimitByIP = m_RylGameServer.GetClientLimit();
if(0 != lpLimitByIP)
{
// 이제부터 접속 금지!
lpLimitByIP->OperateMode(CLimitUserByIP::DENY_ALL);
}
// 길드 요새 상점 정보 업데이트
m_SiegeObjectMgr.ProcessCampShopUpdate(true);
// 클라이언트 접속을 전부 끊고, DBAgent로 Logout을 보낸다.
m_CreatureManager.ProcessAllCharacter(FnDisconnectCharacter());
m_CreatureManager.ProcessAllCharacter(std::bind2nd(std::mem_fun1(&CCharacter::DBUpdateForce), DBUpdateData::LOGOUT));
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if(0 != lpDBAgentDispatch)
{
DBAgentPacketParse::SendServerLogout(*lpDBAgentDispatch);
SERLOG1(g_Log, "this:0x%p/try client logout process", this);
CIOCPNet* lpIOCPNet = m_RylGameServer.GetIOCPNet();
if(0 != lpIOCPNet)
{
// 10초간 대기.
unsigned long dwTotalWaitPulse = 10 * Pulse.GetTicksPerSec();
unsigned long dwStartPulse = Pulse.GetCurrentPulse();
// 서버 로그아웃을 받으면 중계 접속을 끊기 때문에, 중계 접속이 끊길때까지 루프를 돈다.
while(!CDBAgentDispatch::GetDispatchTable().IsEmpty() &&
dwTotalWaitPulse < Pulse.GetCurrentPulse() - dwStartPulse)
{
Pulse.CheckSleep();
lpIOCPNet->Process();
}
}
SERLOG2(g_Log, "this:0x%p/client logout process finish (%s)", this,
CDBAgentDispatch::GetDispatchTable().IsEmpty() ? "Finish complete" : "Timeout");
}
// 모든 크리쳐를 제거한다.
CCreatureManager::GetInstance().DestroyAll();
CDummyCharacterList::GetInstance().Destroy();
// 셀을 제거한다.
CCellManager::GetInstance().Destroy();
GetFunctionTimingResult("RowGameServer");
}
void CGameServerProcessThread::InternalRun(CPulse& Pulse)
{
unsigned long dwCurrentPulse = Pulse.GetCurrentPulse();
// 게임 로그 시간을 업데이트
m_GameLog.UpdateLogTime();
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_TOTAL_LOOP);
// 스펠(챈트&인챈트) 처리 (1초단위로 처리)
if (0 == (dwCurrentPulse % (1 * TICKS_PER_SECOND)))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_GLOBALSPELLMGR);
m_GlobalSpellMgr.Process();
}
// 일정 시간 단위로 맵에서 아이템을 지우는 처리
assert(1 < CCell::CHECK_TIME && "펄스 세팅에 문제가 있습니다.");
if (1 == (dwCurrentPulse % CCell::CHECK_TIME))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_CHECK_DELETE_ITEM);
m_CellManager.ProcessAllCell(std::mem_fun_ref(&CCell::CheckDeleteItem));
m_VirtualAreaMgr.ProcessDeleteItem();
}
// 몬스터 처리 (0.2 초에 한번 처리)
if (1 == (dwCurrentPulse % 2))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_PROCESS_ALL_MONSTER);
m_CreatureManager.ProcessAllMonster(m_processMonster);
m_SiegeObjectMgr.ProcessAllSiegeObject();
m_VirtualAreaMgr.ProcessAllMonster();
}
// 몬스터와 플레이어의 Regen 처리
assert(2 < CAggresiveCreature::REGEN_TIME && "펄스 세팅에 문제가 있습니다.");
if (2 == (dwCurrentPulse % CAggresiveCreature::REGEN_TIME))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_REGEN_HP_MP);
m_CreatureManager.ProcessAllMonster(FnRegenHPAndMP());
m_CreatureManager.ProcessAllCharacter(FnRegenHPAndMP());
m_VirtualAreaMgr.ProcessMonsterRegenHPAndMP();
m_CastleMgr.ProcessEmblemRegenHPAndMP();
m_SiegeObjectMgr.ProcessCampRegenHPAndMP();
}
// 가상맵 처리
assert(2 < VirtualArea::CVirtualAreaMgr::VIRTUALAREA_PULSE && "펄스 세팅에 문제가 있습니다.");
if (2 == (dwCurrentPulse % VirtualArea::CVirtualAreaMgr::VIRTUALAREA_PULSE))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_VIRTUALAREA);
m_VirtualAreaMgr.ProcessAllVirtualArea();
}
// DBUpdate 처리
assert(3 < CCharacter::DBUPDATE_PULSE && "펄스 세팅에 문제가 있습니다.");
if (3 == (dwCurrentPulse % (CCharacter::DBUPDATE_PULSE)))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_DBUPDATE);
// 캐릭터 정보
m_CreatureManager.ProcessAllCharacter(
std::bind2nd(std::mem_fun1(&CCharacter::DBUpdate), DBUpdateData::UPDATE));
// 길드 요새 상점 정보
m_SiegeObjectMgr.ProcessCampShopUpdate(false);
}
// 캐릭터 로그아웃 처리, 소환 몬스터의 사망 처리
assert(3 < CCharacter::LOGOUT_PULSE && "펄스 세팅에 문제가 있습니다.");
if (3 == (dwCurrentPulse % CCharacter::LOGOUT_PULSE))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_CHARACTER_LOGOUT);
m_CreatureManager.ProcessCharacterLogout();
m_CreatureManager.ProcessSummonMonsterDead();
m_VirtualAreaMgr.ProcessSummonMonsterDead();
}
// 공헌 메달 나눠주기
// 배틀 그라운드, 배틀 서버에서 잠자는 시체 자동 리스폰 처리
// 배틀 그라운드 석상 상태 처리 (전투 상태, 휴식 상태)
assert(3 < CCharacter::BATTLE_GROUND_PULSE && "펄스 세팅에 문제가 있습니다.");
if (3 == (dwCurrentPulse % CCharacter::BATTLE_GROUND_PULSE))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_AUTO_RESPAWN);
m_CreatureManager.ProcessBattleGround();
}
// 클라이언트 브로드 캐스팅
assert(4 < CCell::BROADCASTING_TIME && "펄스 세팅에 문제가 있습니다.");
if (4 == (dwCurrentPulse % CCell::BROADCASTING_TIME))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_CELLBROADCASTING);
CCreatureManager::GetInstance().ProcessAllCharacter(PrepareBroadcastData());
CCreatureManager::GetInstance().ProcessAllMonster(PrepareBroadcastData());
m_CellManager.ProcessAllCell(m_processPrepareBroadCast);
m_CellManager.ProcessAllCell(m_processBroadCast, dwCurrentPulse);
m_DuelCellManager.ProcessAllCell(m_processPrepareBroadCast);
m_DuelCellManager.ProcessAllCell(m_processBroadCast, dwCurrentPulse);
m_VirtualAreaMgr.ProcessAllCellPrepareBroadCast();
m_VirtualAreaMgr.ProcessAllCellBroadCast(dwCurrentPulse);
m_SiegeObjectMgr.PrepareBroadCast();
m_SiegeObjectMgr.BroadCast();
}
// 리스폰 큐 프로세스 (1 초단위로 배틀그라운드만 적용)
assert(7 < (1 * TICKS_PER_SECOND) && "펄스 세팅에 문제가 있습니다.");
if (7 == (dwCurrentPulse % (1 * TICKS_PER_SECOND)))
{
if (SERVER_ID::ZONE3 == CServerSetup::GetInstance().GetServerZone())
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_PROCESS_RESPAWN_QUEUE);
m_CreatureManager.ProcessRespawnQueue();
}
}
// 리스폰 프로세스 (1 초단위로)
assert(7 < (1 * TICKS_PER_SECOND) && "펄스 세팅에 문제가 있습니다.");
if (7 == (dwCurrentPulse % (1 * TICKS_PER_SECOND)))
{
if (SERVER_ID::ZONE3 != CServerSetup::GetInstance().GetServerZone())
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_RESPAWN);
m_CharRespawnMgr.ProcessRespawn();
}
}
// 캐릭터 스피어 트리 갱신
assert(8 < (1 * TICKS_PER_SECOND) && "펄스 세팅에 문제가 있습니다.");
if (8 == (dwCurrentPulse % (1 * TICKS_PER_SECOND)))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_CHAR_SPHERE_TREE);
m_CharSphereTree.Process();
}
// 1초 단위로 월드 웨폰 발사 처리
assert(9 < (1 * TICKS_PER_SECOND) && "펄스 세팅에 문제가 있습니다.");
if (9 == (dwCurrentPulse % (1 * TICKS_PER_SECOND)))
{
INSTRUMENT_GAMESERVER(m_Instruments, INSTRUMENT_WORLDWEAPON_FIRE);
m_SiegeObjectMgr.ProcessWorldWeaponFire();
}
// 3초단위로 파티 전부 돌면서 파티 정보 업데이트
if (0 == (dwCurrentPulse % (3 * TICKS_PER_SECOND)))
{
CPartyMgr::GetInstance().UpdatePartyData();
}
// 3초단위로 콘솔 정보 업데이트
if (0 == (dwCurrentPulse % (3 * TICKS_PER_SECOND)))
{
m_RylGameServer.PrintServerInfo();
m_RylGameServer.PrintStatistics();
}
// 2초 단위로 게임 시간 정보 업데이트
if (0 == (dwCurrentPulse % (2 *TICKS_PER_SECOND)))
{
CGameTimeMgr::GetInstance().UpdateGameTimeInfo();
}
// 퍼포먼스 로그 출력 (1시간에 한 번씩)
if (0 == (dwCurrentPulse % (60 * 60 * TICKS_PER_SECOND)))
{
GetFunctionTimingResult("RowGameServer");
}
// 연결 정보 세팅 (DB중계, 채팅)
if (0 == (dwCurrentPulse % (5 * TICKS_PER_SECOND)))
{
unsigned long dwStatusFlag = 0;
if(!CDBAgentDispatch::GetDispatchTable().IsEmpty())
{
dwStatusFlag |= (1 << CServerSetup::AgentServer);
}
if(!CChatDispatch::GetDispatchTable().IsEmpty())
{
dwStatusFlag |= (1 << CServerSetup::ChatServer);
}
m_RylGameServer.SetStatusFlag(dwStatusFlag);
}
#ifdef AUTH_MY
m_RylGameServer.Update( dwCurrentPulse );
#endif
#ifndef NO_GAMEGUARD
// 게임가드 인증 (3분에 한번씩)
if (0 == (dwCurrentPulse % (3 * 60 * TICKS_PER_SECOND)))
{
// edith 2009.08.11 게임가드 2.5 업그레이드
if (true == CServerSetup::GetInstance().GetHackCheck())
{
m_CreatureManager.ProcessAllCharacter(FnCSAuth());
}
}
#endif
// 퀘스트 처리.
CQuestMgr::GetInstance().ProcessPendingQuest();
/*
// 반응성을 높이기 위해서, 시간이 남으면 패킷 처리좀 더 해 준다.
CIOCPNet* lpIOCPNet = m_RylGameServer.GetIOCPNet();
if(0 != lpIOCPNet)
{
const unsigned long dwMinSleepTime = 10;
while(dwMinSleepTime < Pulse.GetRemainTime())
{
lpIOCPNet->Process();
}
}
*/
}