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>
353 lines
10 KiB
C++
353 lines
10 KiB
C++
// QuestChange.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
|
|
//
|
|
|
|
/*
|
|
스크립트 구조
|
|
|
|
REMOVE 퀘스트ID
|
|
CHANGE 퀘스트ID 퀘스트ID
|
|
|
|
퀘스트ID 는 10진수 혹은 0x로 시작하는 16진수이다.
|
|
주석은 C스타일의 주석 // 만 지원한다
|
|
*/
|
|
|
|
#pragma warning(disable:4800)
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include <RylDBLibrary/RylDBLibrary.h>
|
|
#include <RylDBLibrary/RylDBCharCommand.h>
|
|
|
|
#include <Network/Packet/PacketStruct/CharQuestPacket.h>
|
|
#include <Log/ServerLog.h>
|
|
|
|
#include <boost/pool/pool_alloc.hpp>
|
|
#include <Creature/CreatureStructure.h>
|
|
#include <Creature/Character/CharacterClass.h>
|
|
|
|
// Quest Script Command
|
|
|
|
class CProcessQuest : public IDBCharQuestProcess
|
|
{
|
|
public:
|
|
|
|
bool LoadScript(const char* szScriptName);
|
|
|
|
private:
|
|
|
|
typedef std::pair<unsigned short, unsigned short> QuestChangePair;
|
|
|
|
typedef std::vector<unsigned short> QuestRemoveVector;
|
|
typedef std::vector<QuestChangePair> QuestChangeVector;
|
|
|
|
virtual ConvertResult operator()(RylDBCommand::CCharQuest& charQuest_InOut);
|
|
|
|
QuestRemoveVector m_QuestRemoveVector;
|
|
QuestChangeVector m_QuestChangeVector;
|
|
};
|
|
|
|
template<class Data, class Pair>
|
|
struct match_first
|
|
{
|
|
bool operator () (const Data& lhs, const Data& rhs) { return lhs < rhs; }
|
|
bool operator () (const Pair& lhs, const Pair& rhs) { return lhs.first < rhs.first; }
|
|
bool operator () (const Data& lhs, const Pair& rhs) { return lhs < rhs.first; }
|
|
bool operator () (const Pair& lhs, const Data& rhs) { return lhs.first < rhs; }
|
|
};
|
|
|
|
void PrintUsage()
|
|
{
|
|
printf("usage : QuestChange.exe DBAddress DBName DBAccount DBPassword QuestChangeScriptName \n");
|
|
}
|
|
|
|
unsigned short ConvertStringToQuestID(const char* szQuestID)
|
|
{
|
|
unsigned short wQuestID = 0;
|
|
|
|
if (0 != szQuestID)
|
|
{
|
|
const char* szHexHeader = "0X";
|
|
size_t nHexHeaderLen = strlen(szHexHeader);
|
|
|
|
char* szStopPtr = 0;
|
|
int nBase = (0 == strncmp(szQuestID, szHexHeader, nHexHeaderLen)) ? 16 : 10;
|
|
wQuestID = static_cast<unsigned short>(strtoul(szQuestID, &szStopPtr, nBase));
|
|
}
|
|
|
|
return wQuestID;
|
|
}
|
|
|
|
int _tmain(int argc, _TCHAR* argv[])
|
|
{
|
|
if(6 != argc)
|
|
{
|
|
PrintUsage();
|
|
return -1;
|
|
}
|
|
|
|
const char* szProcessQuestScript = argv[5];
|
|
|
|
CProcessQuest processQuest;
|
|
|
|
if(FAILED(processQuest.LoadScript(szProcessQuestScript)))
|
|
{
|
|
printf("%s 스크립트 로드 실패", szProcessQuestScript);
|
|
return -1;
|
|
}
|
|
|
|
CoInitialize(0);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ATL::CDataSource dataSource;
|
|
ATL::CSession Session;
|
|
|
|
#define LOG_CONVERT0(str) { ERRLOG0(g_Log, (str)); printf(str "\n"); }
|
|
#define LOG_CONVERT1(str, arg1) { ERRLOG1(g_Log, (str), (arg1)); printf(str "\n", (arg1)); }
|
|
#define LOG_CONVERT2(str, arg1, arg2) { ERRLOG2(g_Log, (str), (arg1), (arg2)); printf(str "\n", (arg1), (arg2)); }
|
|
|
|
if(FAILED(hr = CRylDBProcess::ConnectDB(dataSource, argv[1], argv[2], argv[3], argv[4])))
|
|
{
|
|
LOG_CONVERT1("Connect DB failed : hr:0x%08X", hr);
|
|
}
|
|
else if(FAILED(hr = Session.Open(dataSource)))
|
|
{
|
|
LOG_CONVERT1("Open session failed : hr:0x%08X", hr);
|
|
}
|
|
else
|
|
{
|
|
CRylDBProcess rylDBProcess(Session);
|
|
CConsoleCounter consoleCounter(1000);
|
|
|
|
if (!rylDBProcess.CharQuest(processQuest, consoleCounter))
|
|
{
|
|
LOG_CONVERT1("CharQuest process failed : hr:0x%08X", hr);
|
|
}
|
|
}
|
|
|
|
INFLOG0(g_Log, "Remove Complete");
|
|
|
|
Session.Close();
|
|
dataSource.Close();
|
|
|
|
CoUninitialize();
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool CProcessQuest::LoadScript(const char* szScriptName)
|
|
{
|
|
bool bResult = true;
|
|
|
|
FILE* lpFile = fopen(szScriptName, "rt");
|
|
|
|
if (0 != lpFile)
|
|
{
|
|
const int MAX_BUFFER = 1024;
|
|
char szBuffer[MAX_BUFFER];
|
|
|
|
const char* szDelimiter = "\r\n\t: ";
|
|
|
|
int nLine = 0;
|
|
|
|
const char* szComment = "//";
|
|
size_t nCommentLen = strlen(szComment);
|
|
|
|
char* szStopPtr = 0;
|
|
int nBase = 10;
|
|
|
|
while(fgets(szBuffer, MAX_BUFFER - 1, lpFile))
|
|
{
|
|
++nLine;
|
|
|
|
char* szCommand = strtok(szBuffer, szDelimiter);
|
|
char* szQuestID = strtok(0, szDelimiter);
|
|
char* szChangeID = strtok(0, szDelimiter);
|
|
|
|
if (0 != szCommand && 0 != szQuestID)
|
|
{
|
|
strupr(szCommand);
|
|
strupr(szQuestID);
|
|
|
|
unsigned short wQuestID = ConvertStringToQuestID(szQuestID);
|
|
|
|
if (0 != wQuestID && 0 == strcmp(szCommand, "REMOVE"))
|
|
{
|
|
m_QuestRemoveVector.push_back(wQuestID);
|
|
}
|
|
else if (0 != szChangeID && 0 == strcmp(szCommand, "CHANGE"))
|
|
{
|
|
strupr(szChangeID);
|
|
|
|
unsigned short wChangeID = ConvertStringToQuestID(szChangeID);
|
|
|
|
if (0 != wChangeID)
|
|
{
|
|
m_QuestChangeVector.push_back(
|
|
QuestChangePair(wQuestID, wChangeID));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
fclose(lpFile);
|
|
}
|
|
else
|
|
{
|
|
LOG_CONVERT1("파일 열기에 실패했습니다 : %s", szScriptName);
|
|
bResult = false;
|
|
}
|
|
|
|
std::sort(m_QuestRemoveVector.begin(), m_QuestRemoveVector.end());
|
|
std::sort(m_QuestChangeVector.begin(), m_QuestChangeVector.end());
|
|
|
|
return bResult;
|
|
}
|
|
|
|
ConvertResult CProcessQuest::operator() (RylDBCommand::CCharQuest& charQuest_InOut)
|
|
{
|
|
unsigned long dwCID = charQuest_InOut.GetCID();
|
|
unsigned long dwTotalChangedCount = 0;
|
|
|
|
const QUEST& quest_In = charQuest_InOut.GetQuest();
|
|
const HISTORY history_In = charQuest_InOut.GetHistory();
|
|
|
|
QuestRemoveVector::iterator remove_pos = m_QuestRemoveVector.begin();
|
|
QuestRemoveVector::iterator remove_end = m_QuestRemoveVector.end();
|
|
|
|
QuestChangeVector::iterator change_pos = m_QuestChangeVector.begin();
|
|
QuestChangeVector::iterator change_end = m_QuestChangeVector.end();
|
|
QuestChangeVector::iterator change_find;
|
|
|
|
if (0 < quest_In.dwSize)
|
|
{
|
|
unsigned long dwChangedCount = 0;
|
|
|
|
QUEST quest_Out;
|
|
memset(&quest_Out, 0, sizeof(QUEST));
|
|
|
|
const PktQuestDB::ExecutingQuest* lpExecutingQuestPos =
|
|
reinterpret_cast<const PktQuestDB::ExecutingQuest*>(quest_In.Data);
|
|
|
|
const PktQuestDB::ExecutingQuest* lpExecutingQuestEnd =
|
|
reinterpret_cast<const PktQuestDB::ExecutingQuest*>(quest_In.Data + quest_In.dwSize);
|
|
|
|
PktQuestDB::ExecutingQuest* lpDst = reinterpret_cast<PktQuestDB::ExecutingQuest*>(quest_Out.Data);
|
|
|
|
for(; lpExecutingQuestPos < lpExecutingQuestEnd; ++lpExecutingQuestPos)
|
|
{
|
|
if (std::binary_search(remove_pos, remove_end, lpExecutingQuestPos->m_wQuestID))
|
|
{
|
|
// 퀘스트를 복사하지 않는다.
|
|
INFLOG2(g_Log, "CID:%10u / QuestID:0x%04X / 진행중인 퀘스트를 제거했습니다",
|
|
dwCID, lpExecutingQuestPos->m_wQuestID);
|
|
|
|
++dwChangedCount;
|
|
}
|
|
else
|
|
{
|
|
change_find = std::lower_bound(
|
|
m_QuestChangeVector.begin(), m_QuestChangeVector.end(),
|
|
lpExecutingQuestPos->m_wQuestID, match_first<unsigned short, QuestChangePair>());
|
|
|
|
if (change_find != change_end && change_find->first == lpExecutingQuestPos->m_wQuestID)
|
|
{
|
|
// 진행중인 퀘스트의 ID가 변경되었다.
|
|
*lpDst = *lpExecutingQuestPos;
|
|
lpDst->m_wQuestID = change_find->second;
|
|
|
|
INFLOG4(g_Log, "CID:%10u / QuestID:0x%04X / Phase:%d / ChangedID:0x%04X / "
|
|
"진행중인 퀘스트의 ID를 변경했습니다",
|
|
dwCID, lpExecutingQuestPos->m_wQuestID,
|
|
lpExecutingQuestPos->m_cPhase, lpDst->m_wQuestID);
|
|
|
|
++dwChangedCount;
|
|
}
|
|
else
|
|
{
|
|
// 목록에 없는 경우에만 복사한다.
|
|
*lpDst = *lpExecutingQuestPos;
|
|
}
|
|
|
|
++lpDst;
|
|
}
|
|
}
|
|
|
|
if (0 < dwChangedCount)
|
|
{
|
|
quest_Out.dwSize = static_cast<unsigned long>(
|
|
reinterpret_cast<char*>(lpDst) - quest_Out.Data);
|
|
|
|
charQuest_InOut.SetQuest(quest_Out);
|
|
|
|
dwTotalChangedCount += dwChangedCount;
|
|
}
|
|
}
|
|
|
|
if (0 < history_In.dwSize)
|
|
{
|
|
unsigned long dwChangedCount = 0;
|
|
|
|
HISTORY history_Out;
|
|
memset(&history_Out, 0, sizeof(HISTORY));
|
|
|
|
const unsigned short* lpHistoryPos = reinterpret_cast<const unsigned short*>(history_In.Data);
|
|
const unsigned short* lpHistoryEnd = reinterpret_cast<const unsigned short*>(history_In.Data + history_In.dwSize);
|
|
|
|
unsigned short* lpDst = reinterpret_cast<unsigned short*>(history_Out.Data);
|
|
|
|
for(; lpHistoryPos < lpHistoryEnd; ++lpHistoryPos)
|
|
{
|
|
if (std::binary_search(remove_pos, remove_end, *lpHistoryPos))
|
|
{
|
|
// 퀘스트를 복사하지 않는다.
|
|
INFLOG2(g_Log, "CID:%10u / QuestID:0x%04X / 수행 완료된 퀘스트를 제거했습니다",
|
|
dwCID, *lpHistoryPos);
|
|
|
|
++dwChangedCount;
|
|
}
|
|
else
|
|
{
|
|
change_find = std::lower_bound(
|
|
m_QuestChangeVector.begin(), m_QuestChangeVector.end(),
|
|
*lpHistoryPos, match_first<unsigned short, QuestChangePair>());
|
|
|
|
if (change_find != change_end && change_find->first == *lpHistoryPos)
|
|
{
|
|
// 수행 완료된 퀘스트의 ID가 변경되었다. 맞춰서 변경해준다.
|
|
*lpDst = change_find->second;
|
|
|
|
INFLOG3(g_Log, "CID:%10u / QuestID:0x%04X / ChangedID:0x%04X / 수행 완료된 퀘스트 ID 변경",
|
|
dwCID, *lpHistoryPos, *lpDst);
|
|
|
|
++dwChangedCount;
|
|
}
|
|
else
|
|
{
|
|
// 목록에 없는 경우에만 복사한다.
|
|
*lpDst = *lpHistoryPos;
|
|
}
|
|
|
|
++lpDst;
|
|
}
|
|
}
|
|
|
|
if (0 < dwChangedCount)
|
|
{
|
|
history_Out.dwSize = static_cast<unsigned long>(
|
|
reinterpret_cast<char*>(lpDst) - history_Out.Data);
|
|
|
|
charQuest_InOut.SetHistory(history_Out);
|
|
|
|
dwTotalChangedCount += dwChangedCount;
|
|
}
|
|
}
|
|
|
|
return (0 < dwTotalChangedCount) ? CONVERT_SUCCEEDED : CONVERT_DO_NOT_WRITE;
|
|
}
|
|
|