I explain development as just balancing different software engineering principles and ideas.
project.h
Code:
struct TeleportInfo
{
#if defined(__WORLDSERVER)
unsigned long dwWorldID;
D3DXVECTOR3 vPos;
#elif defined(__CLIENT)
TCHAR szName[64];
TCHAR szDesc[256];
unsigned long coolDown;
struct MobInfo
{
using List = std::vector<unsigned long>;
int minLevel;
int maxLevel;
List Mobs;
} mInfo;
#endif
};
So, I changed a few things. When working with structs, you have to understand what memory alignment is and how structures are padded. It's not required, it's just if you want to optimize some memory foot printing especially for POD (plain old data). When compiling on x86, the compiler will want to optimize memory alignment to 4 bytes for faster memory access, so it will pad structures.
project.h
Code:
using TeleportData = std::vector<TeleportInfo>;
TeleportData TPData;
void LoadTeleporterData();
project.cpp
Code:
void CProject::LoadTeleporterData()
{
CScript s;
if (!s.Load("TeleporterData.inc"))
return false;
s.GetToken();
while (s.tok != FINISHED)
{
TeleportInfo tpInf{};
#ifdef __WORLDSERVER
tpInf.dwWorldId = atoi(s.token);
tpInf.vPos.x = s.GetFloat();
tpInf.vPos.y= s.GetFloat();
tpInf.vPos.z = s.GetFloat();
s.GetToken();
s.GetToken();
s.GetNumber();
s.GetNumber();
s.GetNumber();
s.GetToken();
if (s.Token == "{")
{
while (s.Token != "}")
s.GetNumber();
s.GetToken();
}
#else
s.GetFloat();
s.GetFloat();
s.GetFloat();
s.GetToken();
_tcsncpy(tpInf.szName, s.token, sizeof(tpInf.szName) / sizeof(tpInf.szName[0]) -2);
tpInf.szName[sizeof(tpInf.szName) / sizeof(tpInf.szName[0]) - 1] = '\0';
_tcsncpy(tpInf.szDesc, s.token, sizeof(tpInf.szDesc) / sizeof(tpInf.szDesc[0]) -2);
tpInf.szDesc[sizeof(tpInf.szDesc) / sizeof(tpInf.szDesc[0]) - 1] = '\0';
tpInf.coolDown = s.GetNumber();
tpInf.mInfo.minLevel = s.GetNumber();
tpInf.mInfo.maxLevel = s.GetNumber();
s.GetToken();
if (s.Token == "{")
{
unsigned long mobid = s.GetNumber();
while (s.Token != "}")
tpInfo.mInfo.Mobs.emplace_back(mobid);
mobid = s.GetNumber();
s.GetToken();
}
TPData.emplace_back(std::move(tpInf));
}
}
You could separate the resource files which would save the extra "get" calls that aren't stored, but then you need to keep track of both files being in the same order. Not only that, but if you do that for like a system extension on... let's say item prop... where it's still on every item, then it'd be a bad idea due to the fact you're opening another stream for the same amount of information that can be acquired from one stream.
You're able to keep it as one file by just calling the 'Get' functions, as shown here. Now, something that is a little 'overboard' nowadays, is that you can change it to stream over the data completely rather than storing the read token within the scanner.
If the vectors are read as such and not sorted, then they should match the client. you'd be able to send the selected cursor to the world server.
// cdpsrvr
Code:
void CDPSrvr::OnTeleport(CAr & ar, DPID dpidCache, DPID dpidUser, LPBYTE, u_long)
{
unsigned int selection;
ar >> selection;
if (selection >= prj.TPData.size())
return false;
CUser* pUser = g_UserMng.GetUser(dpidCache, dpidUser);
if (IsValidObj(pUser))
{
if (pUser->IsDie() || pUser->IsCollecting() || pUser->m_vtInfo.VendorIsVendor() || pUser->m_vtInfo.IsVendorOpen())
return;
const TeleportInfo& inf = TPData[selection];
if (!pUser->IsAuthHigher(AUTH_GAMEMASTER))
{
if(pUser->IsAttackMode() || pUser->GetWorld() && pUser->GetWorld()->GetID() == WI_WORLD_GUILDWAR)
{
pUser->AddText("Can't teleport while fighting.");
return;
}
}
// You could also add like gold requirment, or actual level requirment to tp. There's been a tp system
// that you had to learn locations to tp to, that's another thing thats possible.
pUser->REPLACE(g_uIdofMulti, inf.dwWorldId, inf.vPos, REPLACE_NORMAL, nDefaultLayer);//<---
}
}