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>
461 lines
14 KiB
C++
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;
|
|
}
|