[Bug Fix] Fix issue where you can set your title to titles you don't have. (#1917)

* [Bug Fix] Fix issue where you can set your title to titles you don't have.

* Fixes.

* Fix missing logic check for HasTitle

Co-authored-by: Natedog2012 <jwalters_06@yahoo.com>
This commit is contained in:
Kinglykrab 2022-01-29 21:09:02 -05:00 committed by GitHub
parent 58d5983ef1
commit 7b235a6ede
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 294 additions and 313 deletions

View File

@ -12838,17 +12838,15 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app)
SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer;
std::string Title;
if (!sts->is_suffix)
{
Title = title_manager.GetPrefix(sts->title_id);
SetAATitle(Title.c_str());
if (!title_manager.HasTitle(this, sts->title_id)) {
return;
}
else
{
Title = title_manager.GetSuffix(sts->title_id);
SetTitleSuffix(Title.c_str());
std::string title = !sts->is_suffix ? title_manager.GetPrefix(sts->title_id) : title_manager.GetSuffix(sts->title_id);
if (!sts->is_suffix) {
SetAATitle(title.c_str());
} else {
SetTitleSuffix(title.c_str());
}
}

View File

@ -19,6 +19,7 @@
#include "../common/eq_packet_structs.h"
#include "../common/string_util.h"
#include "../common/misc_functions.h"
#include "../common/repositories/titles_repository.h"
#include "client.h"
#include "entity.h"
@ -34,206 +35,191 @@ TitleManager::TitleManager() {
bool TitleManager::LoadTitles()
{
Titles.clear();
titles.clear();
std::string query = "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, "
"`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, "
"`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles";
auto results = database.QueryDatabase(query);
if (!results.Success()) {
"`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, "
"`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles";
auto results = database.QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
TitleEntry Title;
Title.TitleID = atoi(row[0]);
Title.SkillID = (EQ::skills::SkillType) atoi(row[1]);
Title.MinSkillValue = atoi(row[2]);
Title.MaxSkillValue = atoi(row[3]);
Title.MinAAPoints = atoi(row[4]);
Title.MaxAAPoints = atoi(row[5]);
Title.Class = atoi(row[6]);
Title.Gender = atoi(row[7]);
Title.CharID = atoi(row[8]);
Title.Status = atoi(row[9]);
Title.ItemID = atoi(row[10]);
Title.Prefix = row[11];
Title.Suffix = row[12];
Title.TitleSet = atoi(row[13]);
Titles.push_back(Title);
for (auto row : results) {
TitleEntry title;
title.title_id = std::stoi(row[0]);
title.skill_id = (EQ::skills::SkillType) std::stoi(row[1]);
title.min_skill_value = std::stoi(row[2]);
title.max_skill_value = std::stoi(row[3]);
title.min_aa_points = std::stoi(row[4]);
title.max_aa_points = std::stoi(row[5]);
title.class_id = std::stoi(row[6]);
title.gender_id = std::stoi(row[7]);
title.character_id = std::stoi(row[8]);
title.status = std::stoi(row[9]);
title.item_id = std::stoi(row[10]);
title.prefix = row[11];
title.suffix = row[12];
title.titleset = std::stoi(row[13]);
titles.push_back(title);
}
return true;
}
EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c)
EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *client)
{
std::vector<TitleEntry>::iterator Iterator;
std::vector<TitleEntry> AvailableTitles;
uint32 Length = 4;
Iterator = Titles.begin();
while(Iterator != Titles.end())
{
if(!IsClientEligibleForTitle(c, Iterator))
{
++Iterator;
std::vector<TitleEntry> available_titles;
uint32 length = 4;
for (const auto& title : titles) {
if (!IsClientEligibleForTitle(client, title)) {
continue;
}
AvailableTitles.push_back((*Iterator));
Length += Iterator->Prefix.length() + Iterator->Suffix.length() + 6;
++Iterator;
available_titles.push_back(title);
length += title.prefix.length() + title.suffix.length() + 6;
}
auto outapp = new EQApplicationPacket(OP_SendTitleList, Length);
char *Buffer = (char *)outapp->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, AvailableTitles.size());
Iterator = AvailableTitles.begin();
while(Iterator != AvailableTitles.end())
{
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, Iterator->TitleID);
VARSTRUCT_ENCODE_STRING(Buffer, Iterator->Prefix.c_str());
VARSTRUCT_ENCODE_STRING(Buffer, Iterator->Suffix.c_str());
++Iterator;
auto outapp = new EQApplicationPacket(OP_SendTitleList, length);
char *buffer = (char *)outapp->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, buffer, available_titles.size());
for (const auto& available_title : available_titles) {
VARSTRUCT_ENCODE_TYPE(uint32, buffer, available_title.title_id);
VARSTRUCT_ENCODE_STRING(buffer, available_title.prefix.c_str());
VARSTRUCT_ENCODE_STRING(buffer, available_title.suffix.c_str());
}
return(outapp);
}
int TitleManager::NumberOfAvailableTitles(Client *c)
int TitleManager::NumberOfAvailableTitles(Client *client)
{
int Count = 0;
std::vector<TitleEntry>::iterator Iterator;
Iterator = Titles.begin();
while(Iterator != Titles.end())
{
if(IsClientEligibleForTitle(c, Iterator))
++Count;
++Iterator;
}
return Count;
}
std::string TitleManager::GetPrefix(int TitleID)
{
std::vector<TitleEntry>::iterator Iterator;
Iterator = Titles.begin();
while(Iterator != Titles.end())
{
if((*Iterator).TitleID == TitleID)
return (*Iterator).Prefix;
++Iterator;
}
return "";
}
std::string TitleManager::GetSuffix(int TitleID)
{
std::vector<TitleEntry>::iterator Iterator;
Iterator = Titles.begin();
while(Iterator != Titles.end())
{
if((*Iterator).TitleID == TitleID)
return (*Iterator).Suffix;
++Iterator;
}
return "";
}
bool TitleManager::IsClientEligibleForTitle(Client *c, std::vector<TitleEntry>::iterator Title)
{
if((Title->CharID >= 0) && (c->CharacterID() != static_cast<uint32>(Title->CharID)))
return false;
if((Title->Status >= 0) && (c->Admin() < Title->Status))
return false;
if((Title->Gender >= 0) && (c->GetBaseGender() != Title->Gender))
return false;
if((Title->Class >= 0) && (c->GetBaseClass() != Title->Class))
return false;
if((Title->MinAAPoints >= 0) && (c->GetSpentAA() < static_cast<uint32>(Title->MinAAPoints)))
return false;
if((Title->MaxAAPoints >= 0) && (c->GetSpentAA() > static_cast<uint32>(Title->MaxAAPoints)))
return false;
if(Title->SkillID >= 0)
{
if ((Title->MinSkillValue >= 0) && (c->GetRawSkill(static_cast<EQ::skills::SkillType>(Title->SkillID)) < static_cast<uint32>(Title->MinSkillValue)))
return false;
if ((Title->MaxSkillValue >= 0) && (c->GetRawSkill(static_cast<EQ::skills::SkillType>(Title->SkillID)) > static_cast<uint32>(Title->MaxSkillValue)))
return false;
int count = 0;
for (const auto& title : titles) {
if (IsClientEligibleForTitle(client, title)) {
++count;
}
}
if ((Title->ItemID >= 1) && (c->GetInv().HasItem(Title->ItemID, 0, 0xFF) == INVALID_INDEX))
return false;
if((Title->TitleSet > 0) && (!c->CheckTitle(Title->TitleSet)))
return false;
return true;
return count;
}
bool TitleManager::IsNewAATitleAvailable(int AAPoints, int Class)
std::string TitleManager::GetPrefix(int title_id)
{
std::vector<TitleEntry>::iterator Iterator;
if (!title_id) {
return "";
}
Iterator = Titles.begin();
for (const auto& title : titles) {
if (title.title_id == title_id) {
return title.prefix;
}
}
while(Iterator != Titles.end())
{
if((((*Iterator).Class == -1) || ((*Iterator).Class == Class)) && ((*Iterator).MinAAPoints == AAPoints))
return true;
return "";
}
++Iterator;
std::string TitleManager::GetSuffix(int title_id)
{
if (!title_id) {
return "";
}
for (const auto& title : titles) {
if (title.title_id == title_id) {
return title.suffix;
}
}
return "";
}
bool TitleManager::HasTitle(Client* client, uint32 title_id)
{
if (!client || !title_id) {
return false;
}
for (const auto& title : titles) {
if (title.title_id == title_id) {
return IsClientEligibleForTitle(client, title);
}
}
return false;
}
bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue)
bool TitleManager::IsClientEligibleForTitle(Client *client, TitleEntry title)
{
std::vector<TitleEntry>::iterator Iterator;
if (!client) {
return false;
}
Iterator = Titles.begin();
if (title.character_id >= 0 && client->CharacterID() != static_cast<uint32>(title.character_id)) {
return false;
}
while(Iterator != Titles.end())
{
if(((*Iterator).SkillID == SkillID) && ((*Iterator).MinSkillValue == SkillValue))
if (title.status >= 0 && client->Admin() < title.status) {
return false;
}
if (title.gender_id >= 0 && client->GetBaseGender() != title.gender_id) {
return false;
}
if (title.class_id >= 0 && client->GetBaseClass() != title.class_id) {
return false;
}
if (title.min_aa_points >= 0 && client->GetSpentAA() < title.min_aa_points) {
return false;
}
if (title.max_aa_points >= 0 && client->GetSpentAA() > title.max_aa_points) {
return false;
}
if (title.skill_id >= 0) {
auto skill_id = static_cast<EQ::skills::SkillType>(title.skill_id);
if (title.min_skill_value >= 0 && client->GetRawSkill(skill_id) < static_cast<uint32>(title.min_skill_value)) {
return false;
}
if (title.max_skill_value >= 0 && client->GetRawSkill(skill_id) > static_cast<uint32>(title.max_skill_value)) {
return false;
}
}
if (title.item_id >= 1 && client->GetInv().HasItem(title.item_id) == INVALID_INDEX) {
return false;
}
if (title.titleset > 0 && !client->CheckTitle(title.titleset)) {
return false;
}
return true;
}
bool TitleManager::IsNewAATitleAvailable(int aa_points, int class_id)
{
for (const auto& title : titles) {
if (
(title.class_id == -1 || title.class_id == class_id) &&
title.min_aa_points == aa_points
) {
return true;
}
}
++Iterator;
return false;
}
bool TitleManager::IsNewTradeSkillTitleAvailable(int skill_id, int skill_value)
{
for (const auto& title : titles) {
if (title.skill_id == skill_id && title.min_skill_value == skill_value) {
return true;
}
}
return false;
@ -241,142 +227,138 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue)
void TitleManager::CreateNewPlayerTitle(Client *client, const char *title)
{
if(!client || !title)
if (!client || !title) {
return;
auto escTitle = new char[strlen(title) * 2 + 1];
}
client->SetAATitle(title);
database.DoEscapeString(escTitle, title, strlen(title));
auto query = StringFormat("SELECT `id` FROM titles "
"WHERE `prefix` = '%s' AND char_id = %i",
escTitle, client->CharacterID());
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0){
safe_delete_array(escTitle);
return;
auto query = fmt::format(
"SELECT `id` FROM titles WHERE `prefix` = '{}' AND char_id = {}",
EscapeString(title),
client->CharacterID()
);
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount()){
return;
}
query = StringFormat("INSERT INTO titles (`char_id`, `prefix`) VALUES(%i, '%s')",
client->CharacterID(), escTitle);
safe_delete_array(escTitle);
results = database.QueryDatabase(query);
if(!results.Success()) {
return;
}
query = fmt::format(
"INSERT INTO titles (`char_id`, `prefix`) VALUES ({}, '{}')",
client->CharacterID(),
EscapeString(title)
);
results = database.QueryDatabase(query);
if (!results.Success()) {
return;
}
auto pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
auto pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix)
{
if(!client || !suffix)
if (!client || !suffix) {
return;
client->SetTitleSuffix(suffix);
auto escSuffix = new char[strlen(suffix) * 2 + 1];
database.DoEscapeString(escSuffix, suffix, strlen(suffix));
std::string query = StringFormat("SELECT `id` FROM titles "
"WHERE `suffix` = '%s' AND char_id = %i",
escSuffix, client->CharacterID());
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0) {
safe_delete_array(escSuffix);
return;
}
query = StringFormat("INSERT INTO titles (`char_id`, `suffix`) VALUES(%i, '%s')",
client->CharacterID(), escSuffix);
safe_delete_array(escSuffix);
results = database.QueryDatabase(query);
if(!results.Success()) {
return;
}
auto pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Client::SetAATitle(const char *Title)
{
strn0cpy(m_pp.title, Title, sizeof(m_pp.title));
auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct));
SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer;
strn0cpy(strs->title, Title, sizeof(strs->title));
strs->entity_id = GetID();
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
void Client::SetTitleSuffix(const char *Suffix)
{
strn0cpy(m_pp.suffix, Suffix, sizeof(m_pp.suffix));
auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct));
SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer;
strs->is_suffix = 1;
strn0cpy(strs->title, Suffix, sizeof(strs->title));
strs->entity_id = GetID();
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
void Client::EnableTitle(int titleSet) {
if (CheckTitle(titleSet))
return;
std::string query = StringFormat("INSERT INTO player_titlesets "
"(char_id, title_set) VALUES (%i, %i)",
CharacterID(), titleSet);
auto results = database.QueryDatabase(query);
if(!results.Success())
LogError("Error in EnableTitle query for titleset [{}] and charid [{}]", titleSet, CharacterID());
}
bool Client::CheckTitle(int titleSet) {
std::string query = StringFormat("SELECT `id` FROM player_titlesets "
"WHERE `title_set`=%i AND `char_id`=%i LIMIT 1",
titleSet, CharacterID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
if (results.RowCount() == 0)
return false;
client->SetTitleSuffix(suffix);
std::string query = fmt::format(
"SELECT `id` FROM titles WHERE `suffix` = '{}' AND char_id = {}",
EscapeString(suffix),
client->CharacterID()
);
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount()) {
return;
}
query = fmt::format(
"INSERT INTO titles (`char_id`, `suffix`) VALUES ({}, '{}')",
client->CharacterID(),
EscapeString(suffix)
);
results = database.QueryDatabase(query);
if (!results.Success()) {
return;
}
auto pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Client::SetAATitle(const char *title)
{
strn0cpy(m_pp.title, title, sizeof(m_pp.title));
auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct));
SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer;
strn0cpy(strs->title, title, sizeof(strs->title));
strs->entity_id = GetID();
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
void Client::SetTitleSuffix(const char *suffix)
{
strn0cpy(m_pp.suffix, suffix, sizeof(m_pp.suffix));
auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct));
SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer;
strs->is_suffix = 1;
strn0cpy(strs->title, suffix, sizeof(strs->title));
strs->entity_id = GetID();
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
void Client::EnableTitle(int title_set)
{
if (CheckTitle(title_set)) {
return;
}
std::string query = fmt::format(
"INSERT INTO player_titlesets (char_id, title_set) VALUES ({}, {})",
CharacterID(),
title_set
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogError("Error in EnableTitle query for titleset [{}] and charid [{}]", title_set, CharacterID());
}
}
bool Client::CheckTitle(int title_set)
{
std::string query = fmt::format(
"SELECT `id` FROM player_titlesets WHERE `title_set` = {} AND `char_id` = {} LIMIT 1",
title_set,
CharacterID()
);
auto results = database.QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return false;
}
return true;
}
void Client::RemoveTitle(int titleSet) {
if (!CheckTitle(titleSet))
void Client::RemoveTitle(int title_set)
{
if (!CheckTitle(title_set)) {
return;
}
std::string query = StringFormat("DELETE FROM player_titlesets "
"WHERE `title_set` = %i AND `char_id` = %i",
titleSet, CharacterID());
database.QueryDatabase(query);
TitlesRepository::DeleteWhere(
database,
fmt::format(
"`title_set` = {} AND `char_id` = {}",
title_set,
CharacterID()
)
);
}

View File

@ -25,20 +25,20 @@ class EQApplicationPacket;
struct TitleEntry
{
int TitleID;
int SkillID;
int MinSkillValue;
int MaxSkillValue;
int MinAAPoints;
int MaxAAPoints;
int Class;
int Gender;
int CharID;
int Status;
int ItemID;
std::string Prefix;
std::string Suffix;
int TitleSet;
int title_id;
int skill_id;
int min_skill_value;
int max_skill_value;
int min_aa_points;
int max_aa_points;
int class_id;
int gender_id;
int character_id;
int status;
int item_id;
std::string prefix;
std::string suffix;
int titleset;
};
class TitleManager
@ -48,18 +48,19 @@ public:
bool LoadTitles();
EQApplicationPacket *MakeTitlesPacket(Client *c);
std::string GetPrefix(int TitleID);
std::string GetSuffix(int TitleID);
int NumberOfAvailableTitles(Client *c);
bool IsClientEligibleForTitle(Client *c, std::vector<TitleEntry>::iterator Title);
bool IsNewAATitleAvailable(int AAPoints, int Class);
bool IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue);
void CreateNewPlayerTitle(Client *c, const char *Title);
void CreateNewPlayerSuffix(Client *c, const char *Suffix);
EQApplicationPacket *MakeTitlesPacket(Client *client);
std::string GetPrefix(int title_id);
std::string GetSuffix(int title_id);
int NumberOfAvailableTitles(Client *client);
bool IsClientEligibleForTitle(Client *client, TitleEntry title);
bool IsNewAATitleAvailable(int aa_points, int class_id);
bool IsNewTradeSkillTitleAvailable(int skill_id, int skill_value);
void CreateNewPlayerTitle(Client *client, const char *title);
void CreateNewPlayerSuffix(Client *client, const char *suffix);
bool HasTitle(Client* client, uint32 title_id);
protected:
std::vector<TitleEntry> Titles;
std::vector<TitleEntry> titles;
};
extern TitleManager title_manager;