Files
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

440 lines
13 KiB
C++

#include "stdafx.h"
#include "GameEventDBMgr.h"
#include <DB/DBComponent.h>
#include <DB/GameDBComponent.h>
#include <Utility/Math/Math.h>
#include <Utility/Setup/ServerSetup.h>
#include <Network/Dispatch/GameDispatch.h>
#include <Network/Stream/SendStream.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Packet/PacketStruct/GameEventPacket.h>
#include <Network/SendPacket/SendAdminTool.h>
#include <Log/ServerLog.h>
#pragma pack(1)
struct LimitDropItemDB
{
unsigned long m_dwItemIndex; // 아이템 아이디
unsigned long m_dwItemAmount; // 현재 드랍 수량
unsigned long m_dwMaxAmount; // 최대 드랍 수량
TIME m_StartTime; // 드랍 시작 시각
TIME m_EndTime; // 드랍 종료 시각
};
#pragma pack()
CGameEventDBMgr::LimitDropItem::LimitDropItem()
: m_wItemID(0), m_wNowNum(0), m_wMaxNum(0)
{
ZeroMemory(&m_StartTime, sizeof(tm));
ZeroMemory(&m_EndTime, sizeof(tm));
ZeroMemory(&m_NextDropTime, sizeof(tm));
}
CGameEventDBMgr::LimitDropItem::LimitDropItem(unsigned short wItemID, unsigned short wNowNum,
unsigned short wMaxNum, tm StartTime, tm EndTime)
: m_wItemID(wItemID), m_wNowNum(wNowNum), m_wMaxNum(wMaxNum),
m_StartTime(StartTime), m_EndTime(EndTime), m_NextDropTime(StartTime)
{
}
CGameEventDBMgr& CGameEventDBMgr::GetInstance()
{
static CGameEventDBMgr gameEventMgr;
return gameEventMgr;
}
CGameEventDBMgr::CGameEventDBMgr()
: m_lpDBComponent(0)
{
}
CGameEventDBMgr::~CGameEventDBMgr()
{
}
bool CGameEventDBMgr::Initialize(CDBComponent& DBComponent)
{
m_lpDBComponent = &DBComponent;
const int MAX_ROWS = 1024;
int nGetRows = 0;
if (!DBComponent.ExecuteQuery(
"SELECT nItemIndex, nItemAmount, nMaxAmount, StartTime, EndTime FROM TblEventItem"))
{
ERRLOG0(g_Log, "게임 이벤트 데이터 얻어오기 실패");
return false;
}
LimitDropItemDB* dropItemDB = new LimitDropItemDB[MAX_ROWS];
memset(dropItemDB, 0, sizeof(LimitDropItemDB) * MAX_ROWS);
while (DBComponent.GetData((void**)dropItemDB, sizeof(LimitDropItemDB), MAX_ROWS, &nGetRows))
{
if (0 == nGetRows)
{
break;
}
for (LimitDropItemDB* lpDropItemDB = dropItemDB; nGetRows > 0; --nGetRows, ++lpDropItemDB)
{
tm StartTime, EndTime;
memset(&StartTime, 0, sizeof(tm));
memset(&EndTime, 0, sizeof(tm));
StartTime.tm_year = lpDropItemDB->m_StartTime.Year - 1900;
StartTime.tm_mon = lpDropItemDB->m_StartTime.Month - 1;
StartTime.tm_mday = lpDropItemDB->m_StartTime.Day;
StartTime.tm_hour = lpDropItemDB->m_StartTime.Hour;
StartTime.tm_min = lpDropItemDB->m_StartTime.Minute;
StartTime.tm_sec = lpDropItemDB->m_StartTime.Second;
EndTime.tm_year = lpDropItemDB->m_EndTime.Year - 1900;
EndTime.tm_mon = lpDropItemDB->m_EndTime.Month - 1;
EndTime.tm_mday = lpDropItemDB->m_EndTime.Day;
EndTime.tm_hour = lpDropItemDB->m_EndTime.Hour;
EndTime.tm_min = lpDropItemDB->m_EndTime.Minute;
EndTime.tm_sec = lpDropItemDB->m_EndTime.Second;
LimitDropItem dropItem(static_cast<unsigned short>(lpDropItemDB->m_dwItemIndex),
static_cast<unsigned short>(lpDropItemDB->m_dwItemAmount),
static_cast<unsigned short>(lpDropItemDB->m_dwMaxAmount),
StartTime, EndTime);
m_lstItem.push_back(dropItem);
}
memset(dropItemDB, 0, sizeof(LimitDropItemDB) * MAX_ROWS);
}
delete [] dropItemDB;
return true;
}
void CGameEventDBMgr::CheckGameEvent()
{
if (0 != m_lpDBComponent && !m_lstItem.empty())
{
LimitDropItemList::iterator pos = m_lstItem.begin();
LimitDropItemList::iterator end = m_lstItem.end();
for (; pos != end; ++pos)
{
LimitDropItem& dropItem = *pos;
// 정해진 수량을 다 떨군 경우
if (dropItem.m_wNowNum >= dropItem.m_wMaxNum)
{
continue;
}
time_t localTime;
time(&localTime);
// 이벤트 기간이 아닌 경우
if (0 >= difftime(localTime, mktime(&dropItem.m_StartTime)) ||
0 <= difftime(localTime, mktime(&dropItem.m_EndTime)))
{
continue;
}
if (0 <= difftime(localTime, mktime(&dropItem.m_NextDropTime)))
{
// 차회 드랍 시각이 결정되어 있는 경우에만 드랍한다.
if (dropItem.m_NextDropTime.tm_year != dropItem.m_StartTime.tm_year ||
dropItem.m_NextDropTime.tm_mon != dropItem.m_StartTime.tm_mon ||
dropItem.m_NextDropTime.tm_mday != dropItem.m_StartTime.tm_mday ||
dropItem.m_NextDropTime.tm_hour != dropItem.m_StartTime.tm_hour ||
dropItem.m_NextDropTime.tm_min != dropItem.m_StartTime.tm_min ||
dropItem.m_NextDropTime.tm_sec != dropItem.m_StartTime.tm_sec)
{
if (SendDropItemEvent(dropItem.m_wItemID))
{
++dropItem.m_wNowNum;
DETLOG4(g_Log, "아이템 수량 변동 : ID:%d/Qty:%d/StartTime:%d/EndTime:%d",
dropItem.m_wItemID, dropItem.m_wNowNum,
mktime(&dropItem.m_StartTime), mktime(&dropItem.m_EndTime));
if(!DBComponent::GameDB::UpdateEventItem(
*m_lpDBComponent, dropItem.m_wItemID, dropItem.m_wNowNum))
{
ERRLOG2(g_Log, "ItemID:%6u / NowNum:%u / 변경된 수량 DB에 업데이트 실패",
dropItem.m_wItemID, dropItem.m_wNowNum);
}
else if(DBAgent::SendPacket::ItemQtyCheck(dropItem.m_wItemID,
dropItem.m_wMaxNum, dropItem.m_wNowNum,
mktime(&dropItem.m_StartTime), mktime(&dropItem.m_EndTime),
PktItemQtyControl::STATUS_ACK))
{
DETLOG4(g_Log, "아이템 수량 변동 전송 성공 : ID:%d/Qty:%d/StartTime:%d/EndTime:%d",
dropItem.m_wItemID, dropItem.m_wNowNum,
mktime(&dropItem.m_StartTime), mktime(&dropItem.m_EndTime));
}
}
}
dropItem.m_NextDropTime = *localtime(&localTime);
UpdateTimeDropItemEvent(dropItem);
}
}
}
}
bool CGameEventDBMgr::InsertDropItem(unsigned short wItemID, unsigned short wMaxNum, tm StartTime, tm EndTime)
{
if(0 != m_lpDBComponent)
{
LimitDropItemList::iterator pos = m_lstItem.begin();
LimitDropItemList::iterator end = m_lstItem.end();
for (; pos != end; ++pos)
{
// 기존에 존재하는 것에 대한 수정
LimitDropItem& dropItem = *pos;
if (dropItem.m_wItemID == wItemID)
{
dropItem.m_wMaxNum = wMaxNum;
dropItem.m_StartTime = StartTime;
dropItem.m_EndTime = EndTime;
dropItem.m_NextDropTime = StartTime;
return DBComponent::GameDB::UpdateEventItem(
*m_lpDBComponent, wItemID, wMaxNum, StartTime, EndTime);
}
}
// 새로 추가되는 경우
m_lstItem.push_back(LimitDropItem(wItemID, 0, wMaxNum, StartTime, EndTime));
return DBComponent::GameDB::InsertEventItem(
*m_lpDBComponent, wItemID, wMaxNum, StartTime, EndTime);
}
return false;
}
bool CGameEventDBMgr::EraseDropItem(unsigned short wItemID)
{
if(0 != m_lpDBComponent)
{
LimitDropItemList::iterator pos = m_lstItem.begin();
LimitDropItemList::iterator end = m_lstItem.end();
for (; pos != end; ++pos)
{
if (pos->m_wItemID == wItemID)
{
m_lstItem.erase(pos);
return DBComponent::GameDB::DeleteEventItem(
*m_lpDBComponent, wItemID);
}
}
}
return false;
}
void CGameEventDBMgr::UpdateTimeDropItemEvent(LimitDropItem& ItemInfo)
{
tm startTime, endTime;
// 날짜 간격 계산 (일 단위)
ZeroMemory(&startTime, sizeof(tm));
ZeroMemory(&endTime, sizeof(tm));
startTime.tm_year = ItemInfo.m_NextDropTime.tm_year;
startTime.tm_mon = ItemInfo.m_NextDropTime.tm_mon;
startTime.tm_mday = ItemInfo.m_NextDropTime.tm_mday;
endTime.tm_year = ItemInfo.m_EndTime.tm_year;
endTime.tm_mon = ItemInfo.m_EndTime.tm_mon;
endTime.tm_mday = ItemInfo.m_EndTime.tm_mday;
double dateDuration = difftime(mktime(&endTime), mktime(&startTime));
int nDay = static_cast<int>(dateDuration) / 60 / 60 / 24 + 1;
// 시간 간격 계산 (분 단위)
ZeroMemory(&startTime, sizeof(tm));
ZeroMemory(&endTime, sizeof(tm));
// mktime() 실패 방지를 위한 초기화
{
// day of the month - [1,31]
startTime.tm_mday = 1;
endTime.tm_mday = 1;
// time_t 는 1970년부터 표기한다.
startTime.tm_year = 70;
endTime.tm_year = 70;
}
startTime.tm_hour = ItemInfo.m_NextDropTime.tm_hour;
startTime.tm_min = ItemInfo.m_NextDropTime.tm_min;
startTime.tm_sec = ItemInfo.m_NextDropTime.tm_sec;
endTime.tm_hour = ItemInfo.m_EndTime.tm_hour;
endTime.tm_min = ItemInfo.m_EndTime.tm_min;
endTime.tm_sec = ItemInfo.m_EndTime.tm_sec;
double timeDuration = difftime(mktime(&endTime), mktime(&startTime));
int nTime = static_cast<int>(timeDuration) / 60;
if (nTime <= 0)
{
ERRLOG1(g_Log, "드랍 아이템 이벤트 시간이 이상합니다. 시간차:%d", nTime);
return;
}
// Rodin : 계산 방식을 변경해야 한다.
// m_NextDropTime에 저장한 현재 시각이...
// 날짜는 범위내지만, 시간은 범위밖이라면 곤란하잖아~
// 차후 드랍 시각까지 흘러야할 총 시간
if (ItemInfo.m_wMaxNum <= ItemInfo.m_wNowNum) { return; }
int nTotalDuration = nDay * nTime / (ItemInfo.m_wMaxNum - ItemInfo.m_wNowNum);
int nNextTotal = Math::Random::ComplexRandom(nTotalDuration);
int nNextDate = nNextTotal / nTime;
int nNextTime = nNextTotal % nTime;
time_t nextDropTime = mktime(&ItemInfo.m_NextDropTime);
nextDropTime = nextDropTime + nNextDate * 24 * 60 * 60 + nNextTime * 60;
ItemInfo.m_NextDropTime = *localtime(&nextDropTime);
// 드랍이 결정된 날짜의 최후 시각 (이 시각을 넘긴 시각으로 결정되면 다음 날로 변경해준다.)
tm DropDateLastTime = ItemInfo.m_NextDropTime;
DropDateLastTime.tm_hour = ItemInfo.m_EndTime.tm_hour;
DropDateLastTime.tm_min = ItemInfo.m_EndTime.tm_min;
DropDateLastTime.tm_sec = ItemInfo.m_EndTime.tm_sec;
if (0 < difftime(mktime(&DropDateLastTime), nextDropTime))
{
nextDropTime = static_cast<time_t>(nextDropTime + 24 * 60 * 60 - timeDuration);
ItemInfo.m_NextDropTime = *localtime(&nextDropTime);
}
}
class CGetGameServerDispatch
{
public:
typedef std::vector<DBAgent::CGameDispatch*> GameDispatchList;
CGetGameServerDispatch(GameDispatchList& list) : m_list(list) { m_list.reserve(30); }
bool operator () (unsigned long, CPacketDispatch& packetDispatch)
{
DBAgent::CGameDispatch& GameDispatch = static_cast<DBAgent::CGameDispatch&>(packetDispatch);
SERVER_ID serverID;
serverID.dwID = GameDispatch.GetServerID();
switch(serverID.GetZone())
{
case SERVER_ID::ZONE1:
case SERVER_ID::ZONE2:
case SERVER_ID::ZONE3:
case SERVER_ID::ZONE4:
case SERVER_ID::ZONE5:
case SERVER_ID::CAPITAL:
case SERVER_ID::ZONE9:
case SERVER_ID::ZONE12:
m_list.push_back(&GameDispatch);
break;
default:
// if(( (SERVER_ID::STONE_WAR1 == serverID.GetZone() || SERVER_ID::STONE_WAR2 == serverID.GetZone() || SERVER_ID::STONE_WAR3 == serverID.GetZone()) && true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE)) ||
// (SERVER_ID::ZONE9 == serverID.GetZone() && true == CServerSetup::GetInstance().UseContents(GameRYL::NEWZONE_ZONE9)))
if(( (SERVER_ID::STONE_WAR1 == serverID.GetZone() || SERVER_ID::STONE_WAR2 == serverID.GetZone() || SERVER_ID::STONE_WAR3 == serverID.GetZone()) && true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE) ))
{
m_list.push_back(&GameDispatch);
}
break;
}
return true;
}
private:
GameDispatchList& m_list;
};
bool CGameEventDBMgr::SendDropItemEvent(unsigned short wItemID)
{
CGetGameServerDispatch::GameDispatchList gameDispatchList;
// 원하는 서버군들을 얻어온다.
DBAgent::CGameDispatch::GetDispatchTable().Process(
CGetGameServerDispatch(gameDispatchList));
// 랜덤으로 한 서버를 골라 정보를 보낸다.
if (!gameDispatchList.empty())
{
std::random_shuffle(gameDispatchList.begin(), gameDispatchList.end());
DBAgent::CGameDispatch* lpGameDispatch = *(gameDispatchList.begin());
if (0 != lpGameDispatch)
{
CSendStream& SendStream = lpGameDispatch->GetSendStream();
char* lpBuffer = SendStream.GetBuffer(sizeof(PktEventDropItem));
if(0 != lpBuffer)
{
PktEventDropItem* lpPktEventDropItem =
reinterpret_cast<PktEventDropItem*>(lpBuffer);
lpPktEventDropItem->m_usItemID = wItemID;
if(SendStream.WrapHeader(sizeof(PktEventDropItem), CmdEventDropItem, 0, 0))
{
DETLOG2(g_Log, "ServerID:0x%08X / ItemID:%6u / 게임 서버로 이벤트 아이템 정보 전송",
lpGameDispatch->GetServerID(), wItemID);
return true;
}
}
}
}
return false;
}
void CGameEventDBMgr::SendDropItemInfo()
{
LimitDropItemList::iterator pos = m_lstItem.begin();
LimitDropItemList::iterator end = m_lstItem.end();
for (; pos != end; ++pos)
{
LimitDropItem& dropItem = *pos;
DBAgent::SendPacket::ItemQtyCheck(dropItem.m_wItemID, dropItem.m_wMaxNum, dropItem.m_wNowNum,
mktime(&dropItem.m_StartTime), mktime(&dropItem.m_EndTime), PktItemQtyControl::STATUS_ACK);
}
DBAgent::SendPacket::ItemQtyCheck(0, 0, 0, 0, 0, PktItemQtyControl::STATUS_ACK_END);
}