Compare commits

..

2 Commits

Author SHA1 Message Date
David Markowitz
ea2ef0aea3 Merge branch 'main' into fix-issue 2024-02-20 05:03:13 -08:00
David Markowitz
202ac1e204 fix: incorrectly inverted statement
if we DONT find it, we want to kill/delete it.  not the other way around where if we find it we try to delete it again.

tested that you no longer crash when trying to login
tested that bird monument issues are fixed
2024-02-20 02:34:36 -08:00
18 changed files with 187 additions and 321 deletions

View File

@@ -41,13 +41,12 @@ template <typename ValueType>
class AMFValue : public AMFBaseValue { class AMFValue : public AMFBaseValue {
public: public:
AMFValue() = default; AMFValue() = default;
AMFValue(const ValueType value) : m_Data{ value } {} AMFValue(const ValueType value) { m_Data = value; }
virtual ~AMFValue() override = default; virtual ~AMFValue() override = default;
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override; [[nodiscard]] constexpr eAmf GetValueType() const noexcept override;
[[nodiscard]] const ValueType& GetValue() const { return m_Data; } [[nodiscard]] const ValueType& GetValue() const { return m_Data; }
void SetValue(const ValueType value) { m_Data = value; } void SetValue(const ValueType value) { m_Data = value; }
protected: protected:
@@ -55,7 +54,7 @@ protected:
}; };
// Explicit template class instantiations // Explicit template class instantiations
template class AMFValue<std::nullptr_t>; template class AMFValue<std::nullptr_t>;
template class AMFValue<bool>; template class AMFValue<bool>;
template class AMFValue<int32_t>; template class AMFValue<int32_t>;
template class AMFValue<uint32_t>; template class AMFValue<uint32_t>;
@@ -111,7 +110,7 @@ public:
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; } [[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; }
~AMFArrayValue() override { ~AMFArrayValue() override {
for (const auto* valueToDelete : GetDense()) { for (auto valueToDelete : GetDense()) {
if (valueToDelete) { if (valueToDelete) {
delete valueToDelete; delete valueToDelete;
valueToDelete = nullptr; valueToDelete = nullptr;
@@ -128,12 +127,12 @@ public:
/** /**
* Returns the Associative portion of the object * Returns the Associative portion of the object
*/ */
[[nodiscard]] inline const AMFAssociative& GetAssociative() const noexcept { return m_Associative; } [[nodiscard]] inline const AMFAssociative& GetAssociative() const noexcept { return this->associative; }
/** /**
* Returns the dense portion of the object * Returns the dense portion of the object
*/ */
[[nodiscard]] inline const AMFDense& GetDense() const noexcept { return m_Dense; } [[nodiscard]] inline const AMFDense& GetDense() const noexcept { return this->dense; }
/** /**
* Inserts an AMFValue into the associative portion with the given key. * Inserts an AMFValue into the associative portion with the given key.
@@ -151,12 +150,12 @@ public:
*/ */
template <typename ValueType> template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, const ValueType value) { [[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, const ValueType value) {
const auto element = m_Associative.find(key); auto element = associative.find(key);
AMFValue<ValueType>* val = nullptr; AMFValue<ValueType>* val = nullptr;
bool found = true; bool found = true;
if (element == m_Associative.cend()) { if (element == associative.end()) {
val = new AMFValue<ValueType>(value); val = new AMFValue<ValueType>(value);
m_Associative.emplace(key, val); associative.insert(std::make_pair(key, val));
} else { } else {
val = dynamic_cast<AMFValue<ValueType>*>(element->second); val = dynamic_cast<AMFValue<ValueType>*>(element->second);
found = false; found = false;
@@ -166,12 +165,12 @@ public:
// Associates an array with a string key // Associates an array with a string key
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string& key) { [[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string& key) {
const auto element = m_Associative.find(key); auto element = associative.find(key);
AMFArrayValue* val = nullptr; AMFArrayValue* val = nullptr;
bool found = true; bool found = true;
if (element == m_Associative.cend()) { if (element == associative.end()) {
val = new AMFArrayValue(); val = new AMFArrayValue();
m_Associative.emplace(key, val); associative.insert(std::make_pair(key, val));
} else { } else {
val = dynamic_cast<AMFArrayValue*>(element->second); val = dynamic_cast<AMFArrayValue*>(element->second);
found = false; found = false;
@@ -183,13 +182,13 @@ public:
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) { [[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) {
AMFArrayValue* val = nullptr; AMFArrayValue* val = nullptr;
bool inserted = false; bool inserted = false;
if (index >= m_Dense.size()) { if (index >= dense.size()) {
m_Dense.resize(index + 1); dense.resize(index + 1);
val = new AMFArrayValue(); val = new AMFArrayValue();
m_Dense.at(index) = val; dense.at(index) = val;
inserted = true; inserted = true;
} }
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index)), inserted); return std::make_pair(dynamic_cast<AMFArrayValue*>(dense.at(index)), inserted);
} }
/** /**
@@ -206,13 +205,13 @@ public:
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) { [[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
AMFValue<ValueType>* val = nullptr; AMFValue<ValueType>* val = nullptr;
bool inserted = false; bool inserted = false;
if (index >= m_Dense.size()) { if (index >= this->dense.size()) {
m_Dense.resize(index + 1); this->dense.resize(index + 1);
val = new AMFValue<ValueType>(value); val = new AMFValue<ValueType>(value);
m_Dense.at(index) = val; this->dense.at(index) = val;
inserted = true; inserted = true;
} }
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index)), inserted); return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(this->dense.at(index)), inserted);
} }
/** /**
@@ -225,12 +224,12 @@ public:
* @param value The value to insert * @param value The value to insert
*/ */
void Insert(const std::string& key, AMFBaseValue* const value) { void Insert(const std::string& key, AMFBaseValue* const value) {
const auto element = m_Associative.find(key); auto element = associative.find(key);
if (element != m_Associative.cend() && element->second) { if (element != associative.end() && element->second) {
delete element->second; delete element->second;
element->second = value; element->second = value;
} else { } else {
m_Associative.emplace(key, value); associative.insert(std::make_pair(key, value));
} }
} }
@@ -244,13 +243,13 @@ public:
* @param value The value to insert * @param value The value to insert
*/ */
void Insert(const size_t index, AMFBaseValue* const value) { void Insert(const size_t index, AMFBaseValue* const value) {
if (index < m_Dense.size()) { if (index < dense.size()) {
const AMFDense::const_iterator itr = m_Dense.cbegin() + index; AMFDense::iterator itr = dense.begin() + index;
if (*itr) delete m_Dense.at(index); if (*itr) delete dense.at(index);
} else { } else {
m_Dense.resize(index + 1); dense.resize(index + 1);
} }
m_Dense.at(index) = value; dense.at(index) = value;
} }
/** /**
@@ -265,7 +264,7 @@ public:
*/ */
template <typename ValueType> template <typename ValueType>
[[maybe_unused]] inline AMFValue<ValueType>* Push(const ValueType value) { [[maybe_unused]] inline AMFValue<ValueType>* Push(const ValueType value) {
return Insert(m_Dense.size(), value).first; return Insert(this->dense.size(), value).first;
} }
/** /**
@@ -276,10 +275,10 @@ public:
* @param key The key to remove from the associative portion * @param key The key to remove from the associative portion
*/ */
void Remove(const std::string& key, const bool deleteValue = true) { void Remove(const std::string& key, const bool deleteValue = true) {
const AMFAssociative::const_iterator it = m_Associative.find(key); AMFAssociative::iterator it = this->associative.find(key);
if (it != m_Associative.cend()) { if (it != this->associative.end()) {
if (deleteValue) delete it->second; if (deleteValue) delete it->second;
m_Associative.erase(it); this->associative.erase(it);
} }
} }
@@ -287,24 +286,27 @@ public:
* Pops the last element in the dense portion, deleting it in the process. * Pops the last element in the dense portion, deleting it in the process.
*/ */
void Remove(const size_t index) { void Remove(const size_t index) {
if (!m_Dense.empty() && index < m_Dense.size()) { if (!this->dense.empty() && index < this->dense.size()) {
const auto itr = m_Dense.cbegin() + index; auto itr = this->dense.begin() + index;
if (*itr) delete (*itr); if (*itr) delete (*itr);
m_Dense.erase(itr); this->dense.erase(itr);
} }
} }
void Pop() { void Pop() {
if (!m_Dense.empty()) Remove(m_Dense.size() - 1); if (!this->dense.empty()) Remove(this->dense.size() - 1);
} }
[[nodiscard]] AMFArrayValue* GetArray(const std::string& key) const { [[nodiscard]] AMFArrayValue* GetArray(const std::string& key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key); AMFAssociative::const_iterator it = this->associative.find(key);
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second) : nullptr; if (it != this->associative.end()) {
return dynamic_cast<AMFArrayValue*>(it->second);
}
return nullptr;
} }
[[nodiscard]] AMFArrayValue* GetArray(const size_t index) const { [[nodiscard]] AMFArrayValue* GetArray(const size_t index) const {
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index)) : nullptr; return index >= this->dense.size() ? nullptr : dynamic_cast<AMFArrayValue*>(this->dense.at(index));
} }
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string& key) { [[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string& key) {
@@ -316,7 +318,7 @@ public:
} }
[[maybe_unused]] inline AMFArrayValue* PushArray() { [[maybe_unused]] inline AMFArrayValue* PushArray() {
return static_cast<AMFArrayValue*>(Insert(m_Dense.size()).first); return static_cast<AMFArrayValue*>(Insert(this->dense.size()).first);
} }
/** /**
@@ -330,16 +332,16 @@ public:
*/ */
template <typename AmfType> template <typename AmfType>
[[nodiscard]] AMFValue<AmfType>* Get(const std::string& key) const { [[nodiscard]] AMFValue<AmfType>* Get(const std::string& key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key); AMFAssociative::const_iterator it = this->associative.find(key);
return it != m_Associative.cend() ? return it != this->associative.end() ?
dynamic_cast<AMFValue<AmfType>*>(it->second) : dynamic_cast<AMFValue<AmfType>*>(it->second) :
nullptr; nullptr;
} }
// Get from the array but dont cast it // Get from the array but dont cast it
[[nodiscard]] AMFBaseValue* Get(const std::string& key) const { [[nodiscard]] AMFBaseValue* Get(const std::string& key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key); AMFAssociative::const_iterator it = this->associative.find(key);
return it != m_Associative.cend() ? it->second : nullptr; return it != this->associative.end() ? it->second : nullptr;
} }
/** /**
@@ -353,27 +355,27 @@ public:
*/ */
template <typename AmfType> template <typename AmfType>
[[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const { [[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const {
return index < m_Dense.size() ? return index < this->dense.size() ?
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index)) : dynamic_cast<AMFValue<AmfType>*>(this->dense.at(index)) :
nullptr; nullptr;
} }
// Get from the dense but dont cast it // Get from the dense but dont cast it
[[nodiscard]] AMFBaseValue* Get(const size_t index) const { [[nodiscard]] AMFBaseValue* Get(const size_t index) const {
return index < m_Dense.size() ? m_Dense.at(index) : nullptr; return index < this->dense.size() ? this->dense.at(index) : nullptr;
} }
private: private:
/** /**
* The associative portion. These values are key'd with strings to an AMFValue. * The associative portion. These values are key'd with strings to an AMFValue.
*/ */
AMFAssociative m_Associative; AMFAssociative associative;
/** /**
* The dense portion. These AMFValue's are stored one after * The dense portion. These AMFValue's are stored one after
* another with the most recent addition being at the back. * another with the most recent addition being at the back.
*/ */
AMFDense m_Dense; AMFDense dense;
}; };
#endif //!__AMF3__H__ #endif //!__AMF3__H__

View File

@@ -294,50 +294,28 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream* inStream) {
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& folder) { std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& folder) {
// Because we dont know how large the initial number before the first _ is we need to make it a map like so. // Because we dont know how large the initial number before the first _ is we need to make it a map like so.
std::map<uint32_t, std::string> filenames{}; std::map<uint32_t, std::string> filenames{};
for (auto& t : std::filesystem::directory_iterator(folder)) { for (auto& t : std::filesystem::directory_iterator(folder)) {
auto filename = t.path().filename().string(); auto filename = t.path().filename().string();
auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0)); auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.insert(std::make_pair(index, filename)); filenames.insert(std::make_pair(index, filename));
} }
// Now sort the map by the oldest migration. // Now sort the map by the oldest migration.
std::vector<std::string> sortedFiles{}; std::vector<std::string> sortedFiles{};
auto fileIterator = filenames.begin(); auto fileIterator = filenames.begin();
std::map<uint32_t, std::string>::iterator oldest = filenames.begin(); std::map<uint32_t, std::string>::iterator oldest = filenames.begin();
while (!filenames.empty()) { while (!filenames.empty()) {
if (fileIterator == filenames.end()) { if (fileIterator == filenames.end()) {
sortedFiles.push_back(oldest->second); sortedFiles.push_back(oldest->second);
filenames.erase(oldest); filenames.erase(oldest);
fileIterator = filenames.begin(); fileIterator = filenames.begin();
oldest = filenames.begin(); oldest = filenames.begin();
continue; continue;
} }
if (oldest->first > fileIterator->first) oldest = fileIterator; if (oldest->first > fileIterator->first) oldest = fileIterator;
fileIterator++; fileIterator++;
} }
return sortedFiles; return sortedFiles;
} }
#ifdef DARKFLAME_PLATFORM_MACOS
// MacOS floating-point parse function specializations
namespace GeneralUtils::details {
template <>
[[nodiscard]] float _parse<float>(const std::string_view str, size_t& parseNum) {
return std::stof(std::string{ str }, &parseNum);
}
template <>
[[nodiscard]] double _parse<double>(const std::string_view str, size_t& parseNum) {
return std::stod(std::string{ str }, &parseNum);
}
template <>
[[nodiscard]] long double _parse<long double>(const std::string_view str, size_t& parseNum) {
return std::stold(std::string{ str }, &parseNum);
}
}
#endif

View File

@@ -168,10 +168,25 @@ namespace GeneralUtils {
#ifdef DARKFLAME_PLATFORM_MACOS #ifdef DARKFLAME_PLATFORM_MACOS
// MacOS floating-point parse helper function specializations // Anonymous namespace containing MacOS floating-point parse function specializations
namespace details { namespace {
template <std::floating_point T> template <std::floating_point T>
[[nodiscard]] T _parse(const std::string_view str, size_t& parseNum); [[nodiscard]] T Parse(const std::string_view str, size_t* parseNum);
template <>
[[nodiscard]] float Parse<float>(const std::string_view str, size_t* parseNum) {
return std::stof(std::string{ str }, parseNum);
}
template <>
[[nodiscard]] double Parse<double>(const std::string_view str, size_t* parseNum) {
return std::stod(std::string{ str }, parseNum);
}
template <>
[[nodiscard]] long double Parse<long double>(const std::string_view str, size_t* parseNum) {
return std::stold(std::string{ str }, parseNum);
}
} }
/** /**
@@ -181,10 +196,9 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired value if it is equivalent to the string * @returns An std::optional containing the desired value if it is equivalent to the string
*/ */
template <std::floating_point T> template <std::floating_point T>
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) noexcept [[nodiscard]] std::optional<T> TryParse(const std::string_view str) noexcept try {
try {
size_t parseNum; size_t parseNum;
const T result = details::_parse<T>(str, parseNum); const T result = Parse<T>(str, &parseNum);
const bool isParsed = str.length() == parseNum; const bool isParsed = str.length() == parseNum;
return isParsed ? result : std::optional<T>{}; return isParsed ? result : std::optional<T>{};

View File

@@ -106,7 +106,7 @@ enum class eReplicaComponentType : uint32_t {
INTERACTION_MANAGER, INTERACTION_MANAGER,
DONATION_VENDOR, DONATION_VENDOR,
COMBAT_MEDIATOR, COMBAT_MEDIATOR,
ACHIEVEMENT_VENDOR, COMMENDATION_VENDOR,
GATE_RUSH_CONTROL, GATE_RUSH_CONTROL,
RAIL_ACTIVATOR, RAIL_ACTIVATOR,
ROLLER, ROLLER,

View File

@@ -1,15 +0,0 @@
#ifndef __EVENDORTRANSACTIONRESULT__
#define __EVENDORTRANSACTIONRESULT__
#include <cstdint>
enum class eVendorTransactionResult : uint32_t {
SELL_SUCCESS = 0,
SELL_FAIL,
PURCHASE_SUCCESS,
PURCHASE_FAIL,
DONATION_FAIL,
DONATION_FULL
};
#endif // !__EVENDORTRANSACTIONRESULT__

View File

@@ -79,6 +79,7 @@ void CDMissionsTable::LoadValuesFromDatabase() {
entries.push_back(entry); entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize(); tableData.finalize();
Default.id = -1; Default.id = -1;
@@ -117,12 +118,3 @@ const CDMissions& CDMissionsTable::GetByMissionID(uint32_t missionID, bool& foun
return Default; return Default;
} }
const std::set<int32_t> CDMissionsTable::GetMissionsForReward(LOT lot) {
std::set<int32_t> toReturn {};
for (const auto& entry : GetEntries()) {
if (lot == entry.reward_item1 || lot == entry.reward_item2 || lot == entry.reward_item3 || lot == entry.reward_item4) {
toReturn.insert(entry.id);
}
}
return toReturn;
}

View File

@@ -70,9 +70,6 @@ public:
const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const; const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const;
const std::set<int32_t> GetMissionsForReward(LOT lot);
static CDMissions Default; static CDMissions Default;
}; };

View File

@@ -82,7 +82,6 @@
#include "CollectibleComponent.h" #include "CollectibleComponent.h"
#include "ItemComponent.h" #include "ItemComponent.h"
#include "GhostComponent.h" #include "GhostComponent.h"
#include "AchievementVendorComponent.h"
// Table includes // Table includes
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
@@ -616,8 +615,6 @@ void Entity::Initialize() {
AddComponent<VendorComponent>(); AddComponent<VendorComponent>();
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) { } else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) {
AddComponent<DonationVendorComponent>(); AddComponent<DonationVendorComponent>();
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ACHIEVEMENT_VENDOR, -1) != -1)) {
AddComponent<AchievementVendorComponent>();
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) {
@@ -1194,11 +1191,6 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate); donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
AchievementVendorComponent* achievementVendorComponent;
if (TryGetComponent(eReplicaComponentType::ACHIEVEMENT_VENDOR, achievementVendorComponent)) {
achievementVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
}
BouncerComponent* bouncerComponent; BouncerComponent* bouncerComponent;
if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) { if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) {
bouncerComponent->Serialize(outBitStream, bIsInitialUpdate); bouncerComponent->Serialize(outBitStream, bIsInitialUpdate);

View File

@@ -1,72 +0,0 @@
#include "AchievementVendorComponent.h"
#include "MissionComponent.h"
#include "InventoryComponent.h"
#include "eMissionState.h"
#include "CDComponentsRegistryTable.h"
#include "CDItemComponentTable.h"
#include "eVendorTransactionResult.h"
#include "CheatDetection.h"
#include "UserManager.h"
#include "CDMissionsTable.h"
bool AchievementVendorComponent::SellsItem(Entity* buyer, const LOT lot) {
auto* missionComponent = buyer->GetComponent<MissionComponent>();
if (!missionComponent) return false;
if (m_PlayerPurchasableItems[buyer->GetObjectID()].contains(lot)){
return true;
}
CDMissionsTable* missionsTable = CDClientManager::GetTable<CDMissionsTable>();
const auto missions = missionsTable->GetMissionsForReward(lot);
for (const auto mission : missions) {
if (missionComponent->GetMissionState(mission) == eMissionState::COMPLETE) {
m_PlayerPurchasableItems[buyer->GetObjectID()].insert(lot);
return true;
}
}
return false;
}
void AchievementVendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
// get the item Comp from the item LOT
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
int itemCompID = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::ITEM);
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
uint32_t costLOT = itemComp.commendationLOT;
if (costLOT == -1 || !SellsItem(buyer, lot)) {
auto* user = UserManager::Instance()->GetUser(buyer->GetSystemAddress());
CheatDetection::ReportCheat(user, buyer->GetSystemAddress(), "Attempted to buy item %i from achievement vendor %i that is not purchasable", lot, m_Parent->GetLOT());
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
auto* inventoryComponent = buyer->GetComponent<InventoryComponent>();
if (!inventoryComponent) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
if (costLOT == 13763) { // Faction Token Proxy
auto* missionComponent = buyer->GetComponent<MissionComponent>();
if (!missionComponent) return;
if (missionComponent->GetMissionState(545) == eMissionState::COMPLETE) costLOT = 8318; // "Assembly Token"
if (missionComponent->GetMissionState(556) == eMissionState::COMPLETE) costLOT = 8321; // "Venture League Token"
if (missionComponent->GetMissionState(567) == eMissionState::COMPLETE) costLOT = 8319; // "Sentinels Token"
if (missionComponent->GetMissionState(578) == eMissionState::COMPLETE) costLOT = 8320; // "Paradox Token"
}
const uint32_t altCurrencyCost = itemComp.commendationCost * count;
if (inventoryComponent->GetLotCount(costLOT) < altCurrencyCost) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
inventoryComponent->RemoveItem(costLOT, altCurrencyCost);
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
}

View File

@@ -1,23 +0,0 @@
#ifndef __ACHIEVEMENTVENDORCOMPONENT__H__
#define __ACHIEVEMENTVENDORCOMPONENT__H__
#include "VendorComponent.h"
#include "eReplicaComponentType.h"
#include <set>
#include <map>
class Entity;
class AchievementVendorComponent final : public VendorComponent {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {};
bool SellsItem(Entity* buyer, const LOT lot);
void Buy(Entity* buyer, LOT lot, uint32_t count);
private:
std::map<LWOOBJID,std::set<LOT>> m_PlayerPurchasableItems;
};
#endif //!__ACHIEVEMENTVENDORCOMPONENT__H__

View File

@@ -208,8 +208,9 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity, bool ignoreRefCount) { void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity, bool ignoreRefCount) {
const auto& iter = m_Buffs.find(id); const auto& iter = m_Buffs.find(id);
// If the buff is already scheduled to be removed, don't do it again if (iter == m_Buffs.end()) {
if (iter == m_Buffs.end() || m_BuffsToRemove.contains(id)) return; return;
}
if (!ignoreRefCount && !iter->second.cancelOnRemoveBuff) { if (!ignoreRefCount && !iter->second.cancelOnRemoveBuff) {
iter->second.refCount--; iter->second.refCount--;
@@ -221,7 +222,7 @@ void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id); GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
m_BuffsToRemove.insert(id); m_BuffsToRemove.push_back(id);
RemoveBuffEffect(id); RemoveBuffEffect(id);
} }

View File

@@ -141,7 +141,7 @@ private:
std::map<int32_t, Buff> m_Buffs; std::map<int32_t, Buff> m_Buffs;
// Buffs to remove at the end of the update frame. // Buffs to remove at the end of the update frame.
std::set<int32_t> m_BuffsToRemove; std::vector<int32_t> m_BuffsToRemove;
/** /**
* Parameters (=effects) for each buff * Parameters (=effects) for each buff

View File

@@ -1,5 +1,4 @@
set(DGAME_DCOMPONENTS_SOURCES set(DGAME_DCOMPONENTS_SOURCES
"AchievementVendorComponent.cpp"
"ActivityComponent.cpp" "ActivityComponent.cpp"
"BaseCombatAIComponent.cpp" "BaseCombatAIComponent.cpp"
"BouncerComponent.cpp" "BouncerComponent.cpp"

View File

@@ -8,11 +8,6 @@
#include "CDLootMatrixTable.h" #include "CDLootMatrixTable.h"
#include "CDLootTableTable.h" #include "CDLootTableTable.h"
#include "CDItemComponentTable.h" #include "CDItemComponentTable.h"
#include "InventoryComponent.h"
#include "Character.h"
#include "eVendorTransactionResult.h"
#include "UserManager.h"
#include "CheatDetection.h"
VendorComponent::VendorComponent(Entity* parent) : Component(parent) { VendorComponent::VendorComponent(Entity* parent) : Component(parent) {
m_HasStandardCostItems = false; m_HasStandardCostItems = false;
@@ -156,60 +151,3 @@ void VendorComponent::HandleMrReeCameras(){
m_Inventory.push_back(SoldItem(camera, 0)); m_Inventory.push_back(SoldItem(camera, 0));
} }
} }
void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
if (!SellsItem(lot)) {
auto* user = UserManager::Instance()->GetUser(buyer->GetSystemAddress());
CheatDetection::ReportCheat(user, buyer->GetSystemAddress(), "Attempted to buy item %i from achievement vendor %i that is not purchasable", lot, m_Parent->GetLOT());
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
auto* inventoryComponent = buyer->GetComponent<InventoryComponent>();
if (!inventoryComponent) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
int itemCompID = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::ITEM);
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
// Extra currency that needs to be deducted in case of crafting
auto craftingCurrencies = CDItemComponentTable::ParseCraftingCurrencies(itemComp);
for (const auto& [crafintCurrencyLOT, crafintCurrencyCount]: craftingCurrencies) {
if (inventoryComponent->GetLotCount(crafintCurrencyLOT) < (crafintCurrencyCount * count)) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
}
for (const auto& [crafintCurrencyLOT, crafintCurrencyCount]: craftingCurrencies) {
inventoryComponent->RemoveItem(crafintCurrencyLOT, crafintCurrencyCount * count);
}
float buyScalar = GetBuyScalar();
const auto coinCost = static_cast<uint32_t>(std::floor((itemComp.baseValue * buyScalar) * count));
Character* character = buyer->GetCharacter();
if (!character || character->GetCoins() < coinCost) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
if (Inventory::IsValidItem(itemComp.currencyLOT)) {
const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * buyScalar) * count;
if (inventoryComponent->GetLotCount(itemComp.currencyLOT) < altCurrencyCost) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
inventoryComponent->RemoveItem(itemComp.currencyLOT, altCurrencyCost);
}
character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR);
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
}

View File

@@ -47,7 +47,6 @@ public:
m_DirtyVendor = true; m_DirtyVendor = true;
} }
void Buy(Entity* buyer, LOT lot, uint32_t count);
private: private:
void SetupMaxCustomVendor(); void SetupMaxCustomVendor();

View File

@@ -78,7 +78,6 @@
#include "LevelProgressionComponent.h" #include "LevelProgressionComponent.h"
#include "DonationVendorComponent.h" #include "DonationVendorComponent.h"
#include "GhostComponent.h" #include "GhostComponent.h"
#include "AchievementVendorComponent.h"
// Message includes: // Message includes:
#include "dZoneManager.h" #include "dZoneManager.h"
@@ -98,7 +97,6 @@
#include "ePetAbilityType.h" #include "ePetAbilityType.h"
#include "ActivityManager.h" #include "ActivityManager.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "eVendorTransactionResult.h"
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
#include "CDObjectsTable.h" #include "CDObjectsTable.h"
@@ -1325,14 +1323,15 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s
SEND_PACKET; SEND_PACKET;
} }
void GameMessages::SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr, eVendorTransactionResult result) { void GameMessages::SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr) {
CBITSTREAM; CBITSTREAM;
CMSGHEADER; CMSGHEADER;
int iResult = 0x02; // success, seems to be the only relevant one
bitStream.Write(entity->GetObjectID()); bitStream.Write(entity->GetObjectID());
bitStream.Write(eGameMessageType::VENDOR_TRANSACTION_RESULT); bitStream.Write(eGameMessageType::VENDOR_TRANSACTION_RESULT);
bitStream.Write(result); bitStream.Write(iResult);
SEND_PACKET; SEND_PACKET;
} }
@@ -4665,27 +4664,94 @@ void GameMessages::HandleBuyFromVendor(RakNet::BitStream* inStream, Entity* enti
if (!user) return; if (!user) return;
Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar()); Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
if (!player) return; if (!player) return;
// handle buying normal items
auto* vendorComponent = entity->GetComponent<VendorComponent>();
if (vendorComponent) {
vendorComponent->Buy(player, item, count);
return;
}
// handle buying achievement items auto* propertyVendorComponent = static_cast<PropertyVendorComponent*>(entity->GetComponent(eReplicaComponentType::PROPERTY_VENDOR));
auto* achievementVendorComponent = entity->GetComponent<AchievementVendorComponent>();
if (achievementVendorComponent) {
achievementVendorComponent->Buy(player, item, count);
return;
}
// Handle buying properties if (propertyVendorComponent != nullptr) {
auto* propertyVendorComponent = entity->GetComponent<PropertyVendorComponent>();
if (propertyVendorComponent) {
propertyVendorComponent->OnBuyFromVendor(player, bConfirmed, item, count); propertyVendorComponent->OnBuyFromVendor(player, bConfirmed, item, count);
return; return;
} }
const auto isCommendationVendor = entity->GetLOT() == 13806;
auto* vend = entity->GetComponent<VendorComponent>();
if (!vend && !isCommendationVendor) return;
auto* inv = player->GetComponent<InventoryComponent>();
if (!inv) return;
if (!isCommendationVendor && !vend->SellsItem(item)) {
LOG("User %llu %s tried to buy an item %i from a vendor when they do not sell said item", player->GetObjectID(), user->GetUsername().c_str(), item);
return;
}
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
int itemCompID = compRegistryTable->GetByIDAndType(item, eReplicaComponentType::ITEM);
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
Character* character = player->GetCharacter();
if (!character) return;
// Extra currency that needs to be deducted in case of crafting
auto craftingCurrencies = CDItemComponentTable::ParseCraftingCurrencies(itemComp);
for (const auto& craftingCurrency : craftingCurrencies) {
inv->RemoveItem(craftingCurrency.first, craftingCurrency.second * count);
}
if (isCommendationVendor) {
if (itemComp.commendationLOT != 13763) {
return;
}
auto* missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent == nullptr) {
return;
}
LOT tokenId = -1;
if (missionComponent->GetMissionState(545) == eMissionState::COMPLETE) tokenId = 8318; // "Assembly Token"
if (missionComponent->GetMissionState(556) == eMissionState::COMPLETE) tokenId = 8321; // "Venture League Token"
if (missionComponent->GetMissionState(567) == eMissionState::COMPLETE) tokenId = 8319; // "Sentinels Token"
if (missionComponent->GetMissionState(578) == eMissionState::COMPLETE) tokenId = 8320; // "Paradox Token"
const uint32_t altCurrencyCost = itemComp.commendationCost * count;
if (inv->GetLotCount(tokenId) < altCurrencyCost) {
return;
}
inv->RemoveItem(tokenId, altCurrencyCost);
inv->AddItem(item, count, eLootSourceType::VENDOR);
} else {
float buyScalar = vend->GetBuyScalar();
const auto coinCost = static_cast<uint32_t>(std::floor((itemComp.baseValue * buyScalar) * count));
if (character->GetCoins() < coinCost) {
return;
}
if (Inventory::IsValidItem(itemComp.currencyLOT)) {
const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * buyScalar) * count;
if (inv->GetLotCount(itemComp.currencyLOT) < altCurrencyCost) {
return;
}
inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost);
}
character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR);
inv->AddItem(item, count, eLootSourceType::VENDOR);
}
GameMessages::SendVendorTransactionResult(entity, sysAddr);
} }
void GameMessages::HandleSellToVendor(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleSellToVendor(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
@@ -4719,10 +4785,7 @@ void GameMessages::HandleSellToVendor(RakNet::BitStream* inStream, Entity* entit
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID); CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
// Items with a base value of 0 or max int are special items that should not be sold if they're not sub items // Items with a base value of 0 or max int are special items that should not be sold if they're not sub items
if (itemComp.baseValue == 0 || itemComp.baseValue == UINT_MAX) { if (itemComp.baseValue == 0 || itemComp.baseValue == UINT_MAX) return;
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::SELL_FAIL);
return;
}
float sellScalar = vend->GetSellScalar(); float sellScalar = vend->GetSellScalar();
if (Inventory::IsValidItem(itemComp.currencyLOT)) { if (Inventory::IsValidItem(itemComp.currencyLOT)) {
@@ -4730,9 +4793,11 @@ void GameMessages::HandleSellToVendor(RakNet::BitStream* inStream, Entity* entit
inv->AddItem(itemComp.currencyLOT, std::floor(altCurrency), eLootSourceType::VENDOR); // Return alt currencies like faction tokens. inv->AddItem(itemComp.currencyLOT, std::floor(altCurrency), eLootSourceType::VENDOR); // Return alt currencies like faction tokens.
} }
//inv->RemoveItem(count, -1, iObjID);
inv->MoveItemToInventory(item, eInventoryType::VENDOR_BUYBACK, count, true, false, true); inv->MoveItemToInventory(item, eInventoryType::VENDOR_BUYBACK, count, true, false, true);
character->SetCoins(std::floor(character->GetCoins() + (static_cast<uint32_t>(itemComp.baseValue * sellScalar) * count)), eLootSourceType::VENDOR); character->SetCoins(std::floor(character->GetCoins() + (static_cast<uint32_t>(itemComp.baseValue * sellScalar) * count)), eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::SELL_SUCCESS); //Game::entityManager->SerializeEntity(player); // so inventory updates
GameMessages::SendVendorTransactionResult(entity, sysAddr);
} }
void GameMessages::HandleBuybackFromVendor(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleBuybackFromVendor(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
@@ -4774,16 +4839,16 @@ void GameMessages::HandleBuybackFromVendor(RakNet::BitStream* inStream, Entity*
const auto cost = static_cast<uint32_t>(std::floor(((itemComp.baseValue * sellScalar) * count))); const auto cost = static_cast<uint32_t>(std::floor(((itemComp.baseValue * sellScalar) * count)));
if (character->GetCoins() < cost) { if (character->GetCoins() < cost) {
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_FAIL);
return; return;
} }
if (Inventory::IsValidItem(itemComp.currencyLOT)) { if (Inventory::IsValidItem(itemComp.currencyLOT)) {
const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * sellScalar) * count; const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * sellScalar) * count;
if (inv->GetLotCount(itemComp.currencyLOT) < altCurrencyCost) { if (inv->GetLotCount(itemComp.currencyLOT) < altCurrencyCost) {
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_FAIL);
return; return;
} }
inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost); inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost);
} }
@@ -4791,7 +4856,7 @@ void GameMessages::HandleBuybackFromVendor(RakNet::BitStream* inStream, Entity*
inv->MoveItemToInventory(item, Inventory::FindInventoryTypeForLot(item->GetLot()), count, true, false); inv->MoveItemToInventory(item, Inventory::FindInventoryTypeForLot(item->GetLot()), count, true, false);
character->SetCoins(character->GetCoins() - cost, eLootSourceType::VENDOR); character->SetCoins(character->GetCoins() - cost, eLootSourceType::VENDOR);
//Game::entityManager->SerializeEntity(player); // so inventory updates //Game::entityManager->SerializeEntity(player); // so inventory updates
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_SUCCESS); GameMessages::SendVendorTransactionResult(entity, sysAddr);
} }
void GameMessages::HandleParseChatMessage(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleParseChatMessage(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {

View File

@@ -38,7 +38,6 @@ enum class eUseItemResponse : uint32_t;
enum class eQuickBuildFailReason : uint32_t; enum class eQuickBuildFailReason : uint32_t;
enum class eQuickBuildState : uint32_t; enum class eQuickBuildState : uint32_t;
enum class BehaviorSlot : int32_t; enum class BehaviorSlot : int32_t;
enum class eVendorTransactionResult : uint32_t;
namespace GameMessages { namespace GameMessages {
class PropertyDataMessage; class PropertyDataMessage;
@@ -136,7 +135,7 @@ namespace GameMessages {
void SendVendorOpenWindow(Entity* entity, const SystemAddress& sysAddr); void SendVendorOpenWindow(Entity* entity, const SystemAddress& sysAddr);
void SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr, bool bUpdateOnly = false); void SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr, bool bUpdateOnly = false);
void SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr, eVendorTransactionResult result); void SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr);
void SendRemoveItemFromInventory(Entity* entity, const SystemAddress& sysAddr, LWOOBJID iObjID, LOT templateID, int inventoryType, uint32_t stackCount, uint32_t stackRemaining); void SendRemoveItemFromInventory(Entity* entity, const SystemAddress& sysAddr, LWOOBJID iObjID, LOT templateID, int inventoryType, uint32_t stackCount, uint32_t stackRemaining);
void SendConsumeClientItem(Entity* entity, bool bSuccess, LWOOBJID item); void SendConsumeClientItem(Entity* entity, bool bSuccess, LWOOBJID item);

View File

@@ -82,7 +82,7 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth); if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
else if (serverType == ServerType::World) bitStream.Write(ServiceId::World); else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
else bitStream.Write(ServiceId::General); else bitStream.Write(ServiceId::General);
bitStream.Write<uint64_t>(215523405360); bitStream.Write<uint32_t>(774909490);
server->Send(&bitStream, sysAddr, false); server->Send(&bitStream, sysAddr, false);
} }