Files
Client/Server/ToolProject/NewItemMapAnalyzer/NewItemMapAnalyzer.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

461 lines
14 KiB
C++

#include <BaseLibrary/Utility/Math/Math.h>
#include <RylServerLibrary/DB/DBComponent.h>
#include <RylGameLibrary/Item/ItemFactory.h>
#include <RylGameLibrary/Item/ItemMgr.h>
#include <RylGameLibrary/Item/Item.h>
#include <list>
#include <string>
#include <set>
#include <map>
// 퍼포 측정 클래스
class CheckPerformance
{
#define HOUR(Tick) ((int)((Tick) / 1000 / 60 / 60))
#define MIN(Tick) ((int)((Tick) / 1000 / 60) - HOUR(Tick) * 60)
#define SEC(Tick) ((int)((Tick) / 1000) - HOUR(Tick) * 60 * 60 - MIN(Tick) * 60)
private:
FILE* m_pFile;
DWORD m_Start;
public:
CheckPerformance(FILE* pFile) : m_pFile(pFile) { m_Start = GetTickCount(); }
~CheckPerformance()
{
DWORD Total = GetTickCount() - m_Start;
fprintf(m_pFile, "\n총 시간 %2d시간 %2d분 %2d초\n", HOUR(Total), MIN(Total), SEC(Total));
}
};
const char* GetGradeString(Item::EquipType::Grade eGrade)
{
const char* szResult = "Unknown Grade";
switch(eGrade)
{
case Item::EquipType::AAA_GRADE: szResult = "AAA"; break;
case Item::EquipType::AA_GRADE: szResult = "AA"; break;
case Item::EquipType::A_GRADE: szResult = "A"; break;
case Item::EquipType::B_GRADE: szResult = "B"; break;
case Item::EquipType::C_GRADE: szResult = "C"; break;
case Item::EquipType::D_GRADE: szResult = "D"; break;
case Item::EquipType::F_GRADE: szResult = "E"; break;
case Item::EquipType::MAX_GRADE: szResult = "F"; break;
}
return szResult;
}
#pragma pack(1)
typedef struct ItemMapInfo* LPItemMapInfo;
struct ItemMapInfo
{
unsigned __int64 m_dwUID;
int m_nProtoTypeID;
int m_nCount;
};
struct ItemGradeMapInfo
{
unsigned __int64 m_dwUID;
int m_nProtoTypeID;
int m_nGrade;
int m_nGradeNum;
};
struct ItemGradeInfo
{
unsigned char m_nGrade;
int m_nGradeNum;
};
typedef struct OwnerInfo* LPOwnerInfo;
struct OwnerInfo
{
unsigned long m_dwUID;
unsigned long m_dwCID;
unsigned long m_dwCount;
};
#pragma pack()
const unsigned long MAX_ITEMMAP_INFO = 2000;
const unsigned long MAX_OWNER_INFO = 1000;
// 함수
bool DupMapAnalyzer(FILE* pOutStd, FILE* pOutFile, char* szTableName);
bool UpgMapAnalyzer(FILE* pOutStd, FILE* pOutFile, char* szTableName);
bool PrintItem(FILE* pOutFile, ItemGradeMapInfo& MapInfo, char* Param);
bool PrintItem(FILE* pOutFile, ItemMapInfo& MapInfo, char* Param);
// 변수/상수
const char ScriptFileName[MAX_PATH] = "ItemScript.txt";
static ItemMapInfo g_ItemMapInfo[MAX_ITEMMAP_INFO];
static ItemGradeMapInfo g_ItemGradeMapInfo[MAX_ITEMMAP_INFO];
static OwnerInfo g_OwnerInfo[MAX_OWNER_INFO];
typedef std::map<unsigned __int64, ItemMapInfo> ItemList; // Key == Serial
typedef std::map<unsigned __int64, ItemGradeMapInfo> ItemGradeList; // Key == Serial
typedef std::multimap<unsigned __int64, OwnerInfo> OwnerList; // Key == Serial
ItemGradeList g_DupItemList;
ItemList g_AskOwnerList;
OwnerList g_OwnerList;
int main(int argc, char *argv[])
{
// 파일 오픈
FILE *pOutStd = stdout;
char szTableName[MAX_PATH] = "";
if(argc == 6)
{
// DB 연결
if(!CDBSingleObject::GetInstance().Connect(argv[1], argv[2], argv[4], argv[5]))
{
fprintf(pOutStd, "\n[DB 연결 실패]\n");
return -1;
}
sprintf(szTableName, argv[3]);
}
else
{
printf("Using Like this : NewItemMapAnalyzer DBServerName DBName TableName Account Password");
return -1;
}
std::string dupFileName = "DupLog";
std::string upgFileName = "UpgLog";
dupFileName += szTableName; dupFileName += ".txt";
upgFileName += szTableName; upgFileName += ".txt";
FILE* pDupOutFile = fopen(dupFileName.c_str(), "wt");
FILE* pUpgOutFile = fopen(upgFileName.c_str(), "wt");
if(NULL == pDupOutFile || NULL == pUpgOutFile)
{
if(NULL != pDupOutFile) { fclose(pDupOutFile); }
if(NULL != pUpgOutFile) { fclose(pUpgOutFile); }
return -1;
}
fprintf(pOutStd, "\n[DB 연결 성공]\n");
// 스크립트 읽기
if(!Item::CItemMgr::GetInstance().LoadItemProtoType(ScriptFileName))
{
fprintf(pOutStd, "\n[스크립트 로드 실패]\n");
fclose(pDupOutFile);
fclose(pUpgOutFile);
return -1;
}
fprintf(pOutStd, "\n[스크립트 로드 성공]\n\n[맵 분석 시작]\n");
DupMapAnalyzer(pOutStd, pDupOutFile, szTableName); // 복사 검사
UpgMapAnalyzer(pOutStd, pUpgOutFile, szTableName); // 업글 검사
fprintf(pOutStd, "\n[맵 분석 완료]\n");
fclose(pDupOutFile);
fclose(pUpgOutFile);
getchar();
return 0;
}
// 복사 아이템 분석
bool DupMapAnalyzer(FILE* pOutStd, FILE* pOutFile, char* szTableName)
{
CheckPerformance CheckPerformance(pOutFile);
char Query[MAX_PATH] = "";
int Rows = 0;
fprintf(pOutFile, "[중복 아이템 검사 + _+]\n\n");
CDBSingleObject& DBSingleObject = CDBSingleObject::GetInstance();
DWORD TotalNum = 0;
sprintf(Query, "SELECT COUNT(ITEM_SERIAL) FROM %s", szTableName);
if(!DBSingleObject.Select(Query, (void**)&TotalNum, sizeof(DWORD), 0, 1, &Rows))
{
fprintf(pOutFile, "아이템 총 수 얻기 실패 : ErrorString:%s\n", DBSingleObject.GetErrorString());
return false;
}
int DuplicatedSerial = 0;
std::vector<ItemMapInfo> itemMaps;
itemMaps.reserve(10000);
sprintf(Query, "SELECT ITEM_SERIAL, CAST(PROTOTYPE_ID AS INT), CAST(COUNT(ITEM_SERIAL) AS INT) FROM %s "
"GROUP BY ITEM_SERIAL, PROTOTYPE_ID HAVING COUNT(ITEM_SERIAL) > 1 ORDER BY COUNT(ITEM_SERIAL) DESC", szTableName);
for(int StartRow = 0;; StartRow += MAX_ITEMMAP_INFO)
{
if(DBSingleObject.Select(Query, (void**)&g_ItemMapInfo,
sizeof(ItemMapInfo), StartRow, MAX_ITEMMAP_INFO, &Rows))
{
for(int RowCounter = 0; RowCounter < Rows; ++RowCounter)
{
++DuplicatedSerial;
g_AskOwnerList.insert(
std::make_pair(g_ItemMapInfo[RowCounter].m_dwUID, g_ItemMapInfo[RowCounter]));
itemMaps.push_back(g_ItemMapInfo[RowCounter]);
}
}
else
{
fprintf(pOutFile, "데이터 로딩중 에러 발생 : ErrorCode:%s\n", DBSingleObject.GetErrorString());
break;
}
fprintf(pOutStd, "%d\n", StartRow + Rows);
if(Rows < MAX_ITEMMAP_INFO)
{
break;
}
}
std::vector<ItemMapInfo>::iterator begin = itemMaps.begin();
std::vector<ItemMapInfo>::iterator end = itemMaps.end();
char szSerial[MAX_PATH];
unsigned __int64 dwSerial = 0;
ItemMapInfo itemInfo;
int nCount = 0;
for(;begin != end; ++begin)
{
dwSerial = begin->m_dwUID;
itemInfo = *begin;
sprintf(Query, "SELECT OWNER_UID, OWNER_CID, COUNT(ITEM_SERIAL) FROM %s"
" WHERE ITEM_SERIAL = %s GROUP BY OWNER_UID, OWNER_CID"
" ORDER BY OWNER_UID ASC, OWNER_CID ASC, COUNT(ITEM_SERIAL) DESC",
szTableName, _ui64toa(dwSerial, szSerial, 10));
for(int StartRow = 0;; StartRow += MAX_OWNER_INFO)
{
if(CDBSingleObject::GetInstance().Select(Query, (void**)&g_OwnerInfo,
sizeof(OwnerInfo), StartRow, MAX_OWNER_INFO, &Rows))
{
PrintItem(pOutFile, itemInfo, "");
for(int RowCounter = 0; RowCounter < Rows; ++RowCounter)
{
fprintf(pOutFile, "\tUID:%10d CID:%10d 소유개수:%4d\n",
g_OwnerInfo[RowCounter].m_dwUID,
g_OwnerInfo[RowCounter].m_dwCID,
g_OwnerInfo[RowCounter].m_dwCount);
g_OwnerList.insert(std::make_pair(dwSerial, g_OwnerInfo[RowCounter]));
}
}
else
{
fprintf(pOutFile, "데이터 로딩중 에러 발생 : ErrorCode:%s\n",
CDBSingleObject::GetInstance().GetErrorString());
break;
}
if(Rows < MAX_ITEMMAP_INFO)
break;
}
++nCount;
if(0 == nCount % 2000)
{
fprintf(pOutStd, "%d\n", nCount);
}
}
fprintf(pOutStd, "%d\n", nCount);
fprintf(pOutFile, "\n중복 아이템: %d/%d [ %2.2f% ] \n\n\n",
DuplicatedSerial, TotalNum, (float)DuplicatedSerial / TotalNum * 100);
return true;
}
// 업그레이드 아이템 분석
bool UpgMapAnalyzer(FILE* pOutStd, FILE* pOutFile, char* szTableName)
{
CheckPerformance CheckPerformance(pOutFile);
char Query[MAX_PATH] = "";
int Rows = 0;
CDBSingleObject& DBSingleObject = CDBSingleObject::GetInstance();
const Item::EquipType::Grade eMinFindGrade = Item::EquipType::A_GRADE;
fprintf(pOutFile, "[등급 아이템 검사(%s 등급 이상) + _+]"
"\nSerial\tProtoTypeID\tItemName(Grade(+)))\n",
GetGradeString(eMinFindGrade));
DWORD TotalNum = 0;
sprintf(Query, "SELECT COUNT(ITEM_SERIAL) FROM %s WHERE ITEM_GRADE <= %d", szTableName, eMinFindGrade);
if(!DBSingleObject.Select(Query, (void**)&TotalNum, sizeof(DWORD), 0, 1, &Rows))
{
fprintf(pOutFile, "아이템 총 수 얻기 실패 : ErrorString:%s\n",
DBSingleObject.GetErrorString());
return false;
}
int DuplicatedSerial = 0;
sprintf(Query, "SELECT ITEM_SERIAL, CAST(PROTOTYPE_ID AS INT), "
"CAST(ITEM_GRADE AS INT), CAST(ITEM_GRADE_PLUS AS INT) FROM %s "
"WHERE ITEM_GRADE <= %d ORDER BY ITEM_GRADE ASC, PROTOTYPE_ID ASC, ITEM_SERIAL ASC",
szTableName, eMinFindGrade);
for(int StartRow = 0;; StartRow += MAX_ITEMMAP_INFO)
{
if(DBSingleObject.Select(Query, (void**)&g_ItemGradeMapInfo,
sizeof(ItemGradeMapInfo), StartRow, MAX_ITEMMAP_INFO, &Rows))
{
for(int RowCounter = 0; RowCounter < Rows; ++RowCounter)
{
ItemGradeList::iterator find = g_DupItemList.find(g_ItemGradeMapInfo[RowCounter].m_dwUID);
if(find != g_DupItemList.end())
{
++DuplicatedSerial;
PrintItem(pOutFile, g_ItemGradeMapInfo[RowCounter], "[복사] ");
}
else
{
PrintItem(pOutFile, g_ItemGradeMapInfo[RowCounter], "");
}
fprintf(pOutStd, "#");
}
}
else
{
fprintf(pOutFile, "데이터 로딩중 에러 발생 : ErrorCode:%s\n",
CDBSingleObject::GetInstance().GetErrorString());
}
if(Rows < MAX_ITEMMAP_INFO)
break;
}
fprintf(pOutFile, "\n[등급별 아이템 수]\n");
ItemGradeInfo gradeInfo[Item::EquipType::MAX_GRADE + 1];
memset(&gradeInfo, 0, sizeof(ItemGradeInfo) * (Item::EquipType::MAX_GRADE + 1));
sprintf(Query, "SELECT ITEM_GRADE, CAST(COUNT(ITEM_GRADE) AS INT) FROM %s "
"GROUP BY ITEM_GRADE ORDER BY ITEM_GRADE ASC", szTableName);
if(!DBSingleObject.ExecuteQuery(Query))
{
fprintf(pOutFile, "데이터 로딩중 에러 발생 : ErrorCode:%s\n",
CDBSingleObject::GetInstance().GetErrorString());
}
else
{
int nGetNum = 0;
while(DBSingleObject.GetData((void**)&gradeInfo,
sizeof(ItemGradeInfo), Item::EquipType::MAX_GRADE + 1, &nGetNum))
{
if(0 == nGetNum)
{
break;
}
for(int nCount = 0; nCount < Item::EquipType::MAX_GRADE + 1; ++nCount)
{
fprintf(pOutFile, "%5s 등급 아이템 수 : %d\n",
GetGradeString(static_cast<Item::EquipType::Grade>(gradeInfo[nCount].m_nGrade)),
gradeInfo[nCount].m_nGradeNum);
}
}
}
fprintf(pOutFile, "\n%s 등급 이상 중복 아이템: %d/%d [ %2.2f% ] \n\n\n",
GetGradeString(eMinFindGrade), DuplicatedSerial,
TotalNum, (float)DuplicatedSerial / TotalNum * 100);
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 아이템 출력
bool PrintItem(FILE* pOutFile, ItemGradeMapInfo& MapInfo, char* Param)
{
static char Query[2000] = "";
char strUID[256] = "";
if(0 == MapInfo.m_nProtoTypeID)
return false;
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(MapInfo.m_nProtoTypeID);
if(NULL == lpItem)
{
fprintf(pOutFile, "[ERROR] 아이템 생성 실패 ProtoTypeID: %d\n", MapInfo.m_nProtoTypeID);
return false;
}
Item::ItemInfo ItemInfo = lpItem->GetItemInfo();
Math::Convert::Hex64ToStr(strUID, MapInfo.m_dwUID);
fprintf(pOutFile, "%s0x%s\t%4d\t%s(%s(%d))\n",
Param, strUID, MapInfo.m_nProtoTypeID, ItemInfo.m_SpriteData.m_szName,
GetGradeString(static_cast<Item::EquipType::Grade>(MapInfo.m_nGrade)),
MapInfo.m_nGradeNum);
return true;
}
bool PrintItem(FILE* pOutFile, ItemMapInfo& MapInfo, char* Param)
{
static char Query[2000] = "";
char strUID[256] = "";
if(0 == MapInfo.m_nProtoTypeID)
return false;
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(MapInfo.m_nProtoTypeID);
if(NULL == lpItem)
{
fprintf(pOutFile, "[ERROR] 아이템 생성 실패 ProtoTypeID: %d\n", MapInfo.m_nProtoTypeID);
return false;
}
Item::ItemInfo ItemInfo = lpItem->GetItemInfo();
Math::Convert::Hex64ToStr(strUID, MapInfo.m_dwUID);
fprintf(pOutFile, "%s0x%s\t%4d\t%s(%d)\n",
Param, strUID, MapInfo.m_nProtoTypeID, ItemInfo.m_SpriteData.m_szName, MapInfo.m_nCount);
return true;
}