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>
This commit is contained in:
460
Server/ToolProject/NewItemMapAnalyzer/NewItemMapAnalyzer.cpp
Normal file
460
Server/ToolProject/NewItemMapAnalyzer/NewItemMapAnalyzer.cpp
Normal file
@@ -0,0 +1,460 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user