Esse script é para consertar um bug comum em servidores 97d, que não possui muita influência em servidores fáceis e medianos, mas sim, em servidores hards.
Um video de demonstração do bug:
Segue abaixo:
Skill.h:
Código:
#pragma once
namespace Network
{
struct PMSG_USEITEM
{
PBMSG_HEAD h;
unsigned char Pos;
unsigned char Target;
unsigned char Type;
};
struct SkillStruct
{
unsigned short Item;
unsigned short Level;
unsigned short Strength;
unsigned short Dexterity;
unsigned short Energy;
unsigned char Class[4];
};
class Skill
{
public:
static bool Init();
static void Register(PMSG_USEITEM* lpMsg, DWORD dwIndex);
public:
static SkillStruct Struct[30];
};
}
Skill.cpp:
Código:
#include "Skill.h"
namespace Network
{
SkillStruct Skill::Struct[30];
bool Skill::Init()
{
DarKTeaM::TokenizerEx _skill("..\\Data\\Lang\\Kor\\Item(kor).txt");
if(_skill.Loaded)
{
if(!_skill.SectionExists(12) || !_skill.SectionExists(15))
{
return false;
}
else
{
for(int i = 0; i < 30; i++)
{
Struct[i].Item = 0;
}
for(int i = 7, n = 0; i < 20; i++, n++)
{
if(i == 15)
{
continue;
}
Struct[n].Item = ITEMGET(12, i);
Struct[n].Level = (unsigned short)_skill[12][i][10].ToInteger();
Struct[n].Strength = (unsigned short)_skill[12][i][12].ToInteger();
Struct[n].Dexterity = (unsigned short)_skill[12][i][13].ToInteger();
Struct[n].Energy = (unsigned short)_skill[12][i][11].ToInteger();
Struct[n].Class[0] = (unsigned char)_skill[12][i][15].ToInteger();
Struct[n].Class[1] = (unsigned char)_skill[12][i][16].ToInteger();
Struct[n].Class[2] = (unsigned char)_skill[12][i][17].ToInteger();
Struct[n].Class[3] = (unsigned char)_skill[12][i][18].ToInteger();
}
for(int i = 0, n = 13; i < 16; i++, n++)
{
Struct[n].Item = ITEMGET(15, i);
Struct[n].Level = (unsigned short)_skill[15][i][8].ToInteger();
Struct[n].Strength = 0;
Struct[n].Dexterity = 0;
Struct[n].Energy = (unsigned short)_skill[15][i][9].ToInteger();
Struct[n].Class[0] = (unsigned char)_skill[15][i][11].ToInteger();
Struct[n].Class[1] = (unsigned char)_skill[15][i][12].ToInteger();
Struct[n].Class[2] = (unsigned char)_skill[15][i][13].ToInteger();
Struct[n].Class[3] = (unsigned char)_skill[15][i][14].ToInteger();
}
return true;
}
}
return false;
}
void Skill::Register(PMSG_USEITEM* lpMsg, DWORD dwIndex)
{
BYTE Pos = lpMsg->Pos;
bool szProceed = true;
if(Object[dwIndex].pInventory[Pos].m_Type >= ITEMGET(12, 7) && Object[dwIndex].pInventory[Pos].m_Type <= ITEMGET(12, 14) || Object[dwIndex].pInventory[Pos].m_Type >= ITEMGET(12, 16) && Object[dwIndex].pInventory[Pos].m_Type <= ITEMGET(12, 19) || Object[dwIndex].pInventory[Pos].m_Type >= ITEMGET(15, 0))
{
for(BYTE i = 0; i < 30; i++)
{
if(Struct[i].Item == 0)
{
break;
}
if(Object[dwIndex].pInventory[Pos].m_Type == Struct[i].Item)
{
if(Object[dwIndex].Level < Struct[i].Level)
{
szProceed = false;
}
else if(Object[dwIndex].Strength < Struct[i].Strength)
{
szProceed = false;
}
else if(Object[dwIndex].Agility < Struct[i].Dexterity)
{
szProceed = false;
}
else if(Object[dwIndex].Energy < Struct[i].Energy)
{
szProceed = false;
}
else
{
BYTE szClass, szRequire[2];
szClass = Struct[i].Class[Object[dwIndex].Class];
szRequire[0] = (Object[dwIndex].Class == 0) ? 0 : (Object[dwIndex].Class == 1) ? 16 : (Object[dwIndex].Class == 2) ? 32 : 48;
szRequire[1] = (Object[dwIndex].Class == 0) ? 1 : (Object[dwIndex].Class == 1) ? 17 : (Object[dwIndex].Class == 2) ? 33 : 48;
if(szClass == 1)
{
if(Object[dwIndex].DbClass != szRequire[0] && Object[dwIndex].DbClass != szRequire[1])
{
szProceed = false;
}
}
else if(szClass == 2)
{
if(Object[dwIndex].DbClass != szRequire[1])
{
szProceed = false;
}
}
else
{
szProceed = false;
}
}
break;
}
}
}
if(!szProceed)
{
ServerReFillSend(dwIndex, (WORD)Object[dwIndex].Life, 0xFD, 1);
return;
}
ServerUseItemRecv(lpMsg, dwIndex);
}
}
Hook:
Código:
Hook((DWORD)&Network::Skill::Register, 0x4012BC);
Defines:
Código:
#define ServerUseItemRecv ((void(*)(Network::PMSG_USEITEM*, DWORD)) 0x427A70)
#define ServerReFillSend ((void(*)(DWORD, WORD, BYTE, BYTE)) 0x428FD0)
Função Tokenizer (Créditos totalmente do WoLf (DarK TeaM), fiz pequenas adptações para a leitura do item(kor).txt [Lembrando, é necessário usar a library boost, disponíveis para downloads aqui: [Somente usuários registrados podem vem os links. ]):
Tokenizer.h:
Código:
#pragma once
#include <map>
#include <iostream>
#include <fstream>
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>
namespace DarKTeaM
{
class TokenizerField
{
protected:
std::string Value;
public:
TokenizerField();
TokenizerField(std::string Val);
int ToInteger();
long ToLong();
double ToDouble();
float ToFloat();
std::string ToString();
char* ToStringPtr();
};
class TokenizerRow
{
public:
std::map<int, TokenizerField> Fields;
int FieldCount;
public:
TokenizerRow();
TokenizerRow(std::string strLine);
TokenizerField& operator[] (int FieldIndex);
char* GetField(int FieldIndex, char* Default);
std::string GetField(int FieldIndex, std::string Default);
float GetField(int FieldIndex, float Default);
int GetField(int FieldIndex, int Default);
long GetField(int FieldIndex, long Default);
double GetField(int FieldIndex, double Default);
bool FieldExists(int FieldIndex);
};
class TokenizerSection
{
public:
std::map<int, TokenizerRow> Rows;
int RowCount;
public:
TokenizerSection();
TokenizerRow& operator[] (int RowIndex);
bool RowExists(int RowIndex);
};
class Tokenizer
{
public:
std::map<int, TokenizerSection> Sections;
bool Loaded;
public:
Tokenizer(std::string szFile);
TokenizerSection& operator[](int SectionIndex);
bool SectionExists(int SectionIndex);
};
TokenizerField::TokenizerField() {
this->Value = "";
}
TokenizerField::TokenizerField(std::string Val) {
this->Value = Val;
}
int TokenizerField::ToInteger() {
return atoi(this->Value.c_str());
}
long TokenizerField::ToLong() {
return atol(this->Value.c_str());
}
double TokenizerField::ToDouble() {
return atof(this->Value.c_str());
}
float TokenizerField::ToFloat() {
return (float)(atof(this->Value.c_str()));
}
std::string TokenizerField::ToString() {
return this->Value;
}
char* TokenizerField::ToStringPtr() {
return (char*)this->Value.c_str();
}
TokenizerRow::TokenizerRow()
{
this->FieldCount = 0;
}
TokenizerField& TokenizerRow::operator [](int FieldIndex)
{
return this->Fields[FieldIndex];
}
char* TokenizerRow::GetField (int FieldIndex, char* Default)
{
if(!this->FieldExists(FieldIndex)) return Default;
return this->Fields[FieldIndex].ToStringPtr();
}
std::string TokenizerRow::GetField (int FieldIndex, std::string Default)
{
if(!this->FieldExists(FieldIndex)) return Default;
return this->Fields[FieldIndex].ToString();
}
float TokenizerRow::GetField (int FieldIndex, float Default)
{
if(!this->FieldExists(FieldIndex)) return Default;
return this->Fields[FieldIndex].ToFloat();
}
int TokenizerRow::GetField (int FieldIndex, int Default)
{
if(!this->FieldExists(FieldIndex)) return Default;
return this->Fields[FieldIndex].ToInteger();
}
long TokenizerRow::GetField (int FieldIndex, long Default)
{
if(!this->FieldExists(FieldIndex)) return Default;
return this->Fields[FieldIndex].ToLong();
}
double TokenizerRow::GetField (int FieldIndex, double Default)
{
if(!this->FieldExists(FieldIndex)) return Default;
return this->Fields[FieldIndex].ToDouble();
}
TokenizerRow::TokenizerRow(std::string strLine)
{
this->FieldCount = 0;
std::string sep1 = "\\";
std::string sep2 = " \t";
std::string sep3 = "\"";
boost::escaped_list_separator<char> sep(sep1, sep2, sep3);
boost::tokenizer<boost::escaped_list_separator<char> > tok(strLine, sep);
for (boost::tokenizer<boost::escaped_list_separator<char> >::iterator it = tok.begin();
it != tok.end(); ++it)
{
if(*it == "") continue;
this->Fields[this->FieldCount++] = TokenizerField(std::string(*it));
}
}
bool TokenizerRow::FieldExists(int FieldIndex)
{
std::map<int, TokenizerField>::iterator it = this->Fields.find(FieldIndex);
return (bool)(it != this->Fields.end());
}
TokenizerSection::TokenizerSection()
{
this->Rows.clear();
this->RowCount = 0;
}
TokenizerRow& TokenizerSection::operator[] (int RowIndex)
{
return this->Rows[RowIndex];
}
bool TokenizerSection::RowExists(int RowIndex)
{
std::map<int, TokenizerRow>::iterator it = this->Rows.find(RowIndex);
return (bool)(it != this->Rows.end());
}
TokenizerEx::TokenizerEx(std::string szFile)
{
this->Loaded = false;
std::fstream f(szFile.c_str(), std::ios::in);
if(f.is_open() && f.good())
{
TokenizerSection sec = TokenizerSection();
int CurrentSection = 0;
int RowIndex = 0;
bool SectionOpen = false;
while(!f.eof())
{
char temp[4096];
char* dump = NULL;
std::string line = "";
ZeroMemory(&temp[0], 4096);
f.getline(&temp[0], 4095);
line.assign(&temp[0]);
dump = (char*)line.c_str();
bool finishstart = false;
bool finishend = false;
while(true)
{
if(line.length() > 0)
{
if(line.at(0) == ' ' || line.at(0) == '\t')
{
line.erase(0, 1);
}
else
{
if(finishend)
{
break;
}
finishstart = true;
}
}
else
{
break;
}
if(line.length() > 0)
{
if(line.at((line.length() - 1)) == ' ' || line.at((line.length() - 1)) == '\t')
{
line.erase((line.length() - 1), 1);
}
else
{
if(finishstart)
{
break;
}
finishend = true;
}
}
else
{
break;
}
}
if(line.substr(0, 2) == "//" || line.substr(0, 1) == "#" || line.substr(0, 1) == ";" || line == "")
{
continue;
}
TokenizerRow row(line);
if(!SectionOpen)
{
if(row.FieldCount == 1)
{
if(row[0].ToString().length() < 3)
{
//MessageBox(0, "Abrindo seção", "Seção", MB_OK);
CurrentSection = row[0].ToInteger();
SectionOpen = true;
RowIndex = 0;
}
}
}
else
{
if(row.FieldCount == 1)
{
if(row[0].ToString() == "end" && SectionOpen)
{
//MessageBox(0, "Fechando", "Seção", MB_OK);
this->Sections[CurrentSection] = sec;
sec = TokenizerSection();
SectionOpen = false;
continue;
}
}
sec[RowIndex++] = row;
sec.RowCount++;
}
}
this->Loaded = true;
}
}
TokenizerSection& TokenizerEx::operator[](int SectionIndex)
{
return this->Sections[SectionIndex];
}
bool TokenizerEx::SectionExists(int SectionIndex)
{
std::map<int, TokenizerSection>::iterator it = this->Sections.find(SectionIndex);
return (bool)(this->Sections.end() != it);
}
}
Créditos:
Network.