[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; SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer;
std::string Title; if (!title_manager.HasTitle(this, sts->title_id)) {
return;
if (!sts->is_suffix)
{
Title = title_manager.GetPrefix(sts->title_id);
SetAATitle(Title.c_str());
} }
else
{ std::string title = !sts->is_suffix ? title_manager.GetPrefix(sts->title_id) : title_manager.GetSuffix(sts->title_id);
Title = title_manager.GetSuffix(sts->title_id); if (!sts->is_suffix) {
SetTitleSuffix(Title.c_str()); SetAATitle(title.c_str());
} else {
SetTitleSuffix(title.c_str());
} }
} }

View File

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

View File

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