DarkflameServer/dGame/dComponents/PropertyEntranceComponent.cpp

335 lines
12 KiB
C++
Raw Normal View History

#include "PropertyEntranceComponent.h"
2022-03-31 03:33:50 +00:00
#include <CDPropertyEntranceComponentTable.h>
#include "Character.h"
#include "Database.h"
2022-03-31 03:33:50 +00:00
#include "GameMessages.h"
#include "PropertyManagementComponent.h"
2022-03-31 03:33:50 +00:00
#include "PropertySelectQueryProperty.h"
#include "RocketLaunchpadControlComponent.h"
#include "CharacterComponent.h"
2022-03-28 03:04:45 +00:00
#include "UserManager.h"
#include "Logger.h"
#include "Amf3.h"
#include "eObjectBits.h"
#include "eGameMasterLevel.h"
2022-07-28 13:39:57 +00:00
PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entity* parent) : Component(parent) {
this->propertyQueries = {};
auto table = CDClientManager::Instance().GetTable<CDPropertyEntranceComponentTable>();
2022-07-28 13:39:57 +00:00
const auto& entry = table->GetByID(componentID);
2022-07-28 13:39:57 +00:00
this->m_MapID = entry.mapID;
this->m_PropertyName = entry.propertyName;
}
void PropertyEntranceComponent::OnUse(Entity* entity) {
auto* characterComponent = entity->GetComponent<CharacterComponent>();
if (!characterComponent) return;
auto* rocket = entity->GetComponent<CharacterComponent>()->RocketEquip(entity);
if (!rocket) return;
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress());
AMFArrayValue args;
args.Insert("state", "property_menu");
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args);
}
2022-07-28 13:39:57 +00:00
void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress& sysAddr) {
LWOCLONEID cloneId = 0;
2022-07-28 13:39:57 +00:00
if (index == -1 && !returnToZone) {
cloneId = entity->GetCharacter()->GetPropertyCloneID();
} else if (index == -1 && returnToZone) {
cloneId = 0;
} else if (index >= 0) {
// Increment index once here because the first index of other player properties is 2 in the propertyQueries cache.
index++;
2022-07-28 13:39:57 +00:00
const auto& pair = propertyQueries.find(entity->GetObjectID());
2022-07-28 13:39:57 +00:00
if (pair == propertyQueries.end()) return;
2022-07-28 13:39:57 +00:00
const auto& query = pair->second;
2022-07-28 13:39:57 +00:00
if (index >= query.size()) return;
2022-07-28 13:39:57 +00:00
cloneId = query[index].CloneId;
}
2022-07-28 13:39:57 +00:00
auto* launcher = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
2022-07-28 13:39:57 +00:00
if (launcher == nullptr) {
return;
}
2022-07-28 13:39:57 +00:00
launcher->SetSelectedCloneId(entity->GetObjectID(), cloneId);
2022-07-28 13:39:57 +00:00
launcher->Launch(entity, launcher->GetTargetZone(), cloneId);
}
2022-03-31 04:18:29 +00:00
PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId, std::string ownerName, std::string propertyName, std::string propertyDescription, float reputation, bool isBFF, bool isFriend, bool isModeratorApproved, bool isAlt, bool isOwned, uint32_t privacyOption, uint32_t timeLastUpdated, float performanceCost) {
2022-07-28 13:39:57 +00:00
property.CloneId = cloneId;
property.OwnerName = ownerName;
property.Name = propertyName;
property.Description = propertyDescription;
property.Reputation = reputation;
property.IsBestFriend = isBFF;
property.IsFriend = isFriend;
property.IsModeratorApproved = isModeratorApproved;
property.IsAlt = isAlt;
property.IsOwned = isOwned;
property.AccessType = privacyOption;
property.DateLastPublished = timeLastUpdated;
property.PerformanceCost = performanceCost;
return property;
}
Friends List Overhaul (#630) v103 * Add friends list migration * Change friends to use charID Update friends table to use charID and not LWOOBJID variant. * Fix remove friend Fix remove friend and make the query more readable at a glance. * Add and remove friends in the container Properly add and remove friends in the player container * add enums * Add best friends and basic GM support V1 * Add more features * not online / doesnt exist implementation Implements the not online and invalid character response codes * Address players not being removed Fix an issue where players would not be marked as offline in the friends list due to the message not being sent in all circumstances. Tested changes on 3 clients, switching characters, logging out from character select, switching characters, world transfer and my friends list looked as it was supposed to. * Implement proper friends system Remove debug logs Track count of best friends Add best friends list cap of 5 Add config option and best friend update Add a config option and implement the last missing best friend serialization Added comments and fixed remove best friend bug Added some comments and addressed an issue where removing best friends would not remove them from your internal count of friends. properties and logs fixes whoops, had an issue send reply if already BFFs Send the correct objectID I really need to rename these Fix white space goon * Replace queries with unique ptrs * remove user from player container on deletion Remove the user from the player container when they delete their character.
2022-07-13 03:36:06 +00:00
std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery, bool wantLimits) {
2022-07-28 13:39:57 +00:00
std::string base;
if (customQuery == "") {
base = baseQueryForProperties;
} else {
base = customQuery;
}
std::string orderBy = "";
if (sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS) {
std::string friendsList = " AND p.owner_id IN (";
auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;");
friendsListQuery->setUInt(1, character->GetID());
friendsListQuery->setUInt(2, character->GetID());
auto friendsListQueryResult = friendsListQuery->executeQuery();
while (friendsListQueryResult->next()) {
auto playerIDToConvert = friendsListQueryResult->getInt(1);
friendsList = friendsList + std::to_string(playerIDToConvert) + ",";
}
// Replace trailing comma with the closing parenthesis.
if (friendsList.at(friendsList.size() - 1) == ',') friendsList.erase(friendsList.size() - 1, 1);
friendsList += ") ";
// If we have no friends then use a -1 for the query.
if (friendsList.find("()") != std::string::npos) friendsList = " AND p.owner_id IN (-1) ";
orderBy += friendsList + "ORDER BY ci.name ASC ";
delete friendsListQueryResult;
friendsListQueryResult = nullptr;
delete friendsListQuery;
friendsListQuery = nullptr;
} else if (sortMethod == SORT_TYPE_RECENT) {
orderBy = "ORDER BY p.last_updated DESC ";
} else if (sortMethod == SORT_TYPE_REPUTATION) {
orderBy = "ORDER BY p.reputation DESC, p.last_updated DESC ";
} else {
orderBy = "ORDER BY p.last_updated DESC ";
}
return base + orderBy + (wantLimits ? "LIMIT ? OFFSET ?;" : ";");
}
2022-07-28 13:39:57 +00:00
void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr) {
2022-07-28 13:39:57 +00:00
std::vector<PropertySelectQueryProperty> entries{};
PropertySelectQueryProperty playerEntry{};
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
auto character = entity->GetCharacter();
if (!character) return;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
// Player property goes in index 1 of the vector. This is how the client expects it.
auto playerPropertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?");
2022-07-28 13:39:57 +00:00
playerPropertyLookup->setInt(1, character->GetID());
playerPropertyLookup->setInt(2, this->m_MapID);
2022-07-28 13:39:57 +00:00
auto playerPropertyLookupResults = playerPropertyLookup->executeQuery();
2022-07-28 13:39:57 +00:00
// If the player has a property this query will have a single result.
if (playerPropertyLookupResults->next()) {
const auto cloneId = playerPropertyLookupResults->getUInt64(4);
const auto propertyName = std::string(playerPropertyLookupResults->getString(5).c_str());
const auto propertyDescription = std::string(playerPropertyLookupResults->getString(6).c_str());
const auto privacyOption = playerPropertyLookupResults->getInt(9);
const auto modApproved = playerPropertyLookupResults->getBoolean(10);
const auto dateLastUpdated = playerPropertyLookupResults->getInt64(11);
const auto reputation = playerPropertyLookupResults->getUInt(14);
const auto performanceCost = (float)playerPropertyLookupResults->getDouble(16);
2022-07-28 13:39:57 +00:00
playerEntry = SetPropertyValues(playerEntry, cloneId, character->GetName(), propertyName, propertyDescription, reputation, true, true, modApproved, true, true, privacyOption, dateLastUpdated, performanceCost);
} else {
playerEntry = SetPropertyValues(playerEntry, character->GetPropertyCloneID(), character->GetName(), "", "", 0, true, true);
}
2022-07-28 13:39:57 +00:00
delete playerPropertyLookupResults;
playerPropertyLookupResults = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
delete playerPropertyLookup;
playerPropertyLookup = nullptr;
2022-07-28 13:39:57 +00:00
entries.push_back(playerEntry);
2022-07-28 13:39:57 +00:00
const auto query = BuildQuery(entity, sortMethod, character);
2022-07-28 13:39:57 +00:00
auto propertyLookup = Database::CreatePreppedStmt(query);
2022-07-28 13:39:57 +00:00
const auto searchString = "%" + filterText + "%";
propertyLookup->setUInt(1, this->m_MapID);
propertyLookup->setString(2, searchString.c_str());
propertyLookup->setString(3, searchString.c_str());
propertyLookup->setString(4, searchString.c_str());
propertyLookup->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? (uint32_t)PropertyPrivacyOption::Friends : (uint32_t)PropertyPrivacyOption::Public);
propertyLookup->setInt(6, numResults);
propertyLookup->setInt(7, startIndex);
2022-03-28 03:04:45 +00:00
2022-07-28 13:39:57 +00:00
auto propertyEntry = propertyLookup->executeQuery();
2022-03-31 03:33:50 +00:00
2022-07-28 13:39:57 +00:00
while (propertyEntry->next()) {
const auto propertyId = propertyEntry->getUInt64(1);
const auto owner = propertyEntry->getInt(2);
const auto cloneId = propertyEntry->getUInt64(4);
const auto propertyNameFromDb = std::string(propertyEntry->getString(5).c_str());
const auto propertyDescriptionFromDb = std::string(propertyEntry->getString(6).c_str());
const auto privacyOption = propertyEntry->getInt(9);
const auto modApproved = propertyEntry->getBoolean(10);
const auto dateLastUpdated = propertyEntry->getInt(11);
const float reputation = propertyEntry->getInt(14);
const auto performanceCost = (float)propertyEntry->getDouble(16);
2022-03-28 10:24:49 +00:00
2022-07-28 13:39:57 +00:00
PropertySelectQueryProperty entry{};
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
std::string ownerName = "";
bool isOwned = true;
auto nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;");
2022-07-28 13:39:57 +00:00
nameLookup->setUInt64(1, cloneId);
2022-07-28 13:39:57 +00:00
auto nameResult = nameLookup->executeQuery();
2022-07-28 13:39:57 +00:00
if (!nameResult->next()) {
delete nameLookup;
nameLookup = nullptr;
LOG("Failed to find property owner name for %llu!", cloneId);
2022-07-28 13:39:57 +00:00
continue;
} else {
isOwned = cloneId == character->GetPropertyCloneID();
ownerName = std::string(nameResult->getString(1).c_str());
}
2022-03-28 06:46:43 +00:00
2022-07-28 13:39:57 +00:00
delete nameResult;
nameResult = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
delete nameLookup;
nameLookup = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
std::string propertyName = propertyNameFromDb;
std::string propertyDescription = propertyDescriptionFromDb;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
bool isBestFriend = false;
bool isFriend = false;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
// Convert owner char id to LWOOBJID
LWOOBJID ownerObjId = owner;
GeneralUtils::SetBit(ownerObjId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(ownerObjId, eObjectBits::PERSISTENT);
2022-03-28 03:04:45 +00:00
2022-07-28 13:39:57 +00:00
// Query to get friend and best friend fields
auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)");
2022-03-28 03:04:45 +00:00
2022-07-28 13:39:57 +00:00
friendCheck->setUInt(1, character->GetID());
friendCheck->setUInt(2, ownerObjId);
friendCheck->setUInt(3, ownerObjId);
friendCheck->setUInt(4, character->GetID());
2022-03-28 03:04:45 +00:00
2022-07-28 13:39:57 +00:00
auto friendResult = friendCheck->executeQuery();
2022-03-31 03:33:50 +00:00
2022-07-28 13:39:57 +00:00
// If we got a result than the two players are friends.
if (friendResult->next()) {
isFriend = true;
if (friendResult->getInt(1) == 3) {
isBestFriend = true;
}
}
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
delete friendCheck;
friendCheck = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
delete friendResult;
friendResult = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
bool isModeratorApproved = propertyEntry->getBoolean(10);
2022-03-29 03:51:15 +00:00
if (!isModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) {
2022-07-28 13:39:57 +00:00
propertyName = "[AWAITING APPROVAL]";
propertyDescription = "[AWAITING APPROVAL]";
isModeratorApproved = true;
}
2022-03-28 06:46:43 +00:00
2022-07-28 13:39:57 +00:00
bool isAlt = false;
// Query to determine whether this property is an alt character of the entity.
auto isAltQuery = Database::CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;");
2022-03-28 06:46:43 +00:00
2022-07-28 13:39:57 +00:00
isAltQuery->setInt(1, character->GetID());
isAltQuery->setInt(2, owner);
2022-03-28 06:46:43 +00:00
2022-07-28 13:39:57 +00:00
auto isAltQueryResults = isAltQuery->executeQuery();
2022-03-28 06:46:43 +00:00
2022-07-28 13:39:57 +00:00
if (isAltQueryResults->next()) {
isAlt = true;
}
2022-03-28 03:04:45 +00:00
2022-07-28 13:39:57 +00:00
delete isAltQueryResults;
isAltQueryResults = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
delete isAltQuery;
isAltQuery = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
entry = SetPropertyValues(entry, cloneId, ownerName, propertyName, propertyDescription, reputation, isBestFriend, isFriend, isModeratorApproved, isAlt, isOwned, privacyOption, dateLastUpdated, performanceCost);
2022-07-28 13:39:57 +00:00
entries.push_back(entry);
}
2022-07-28 13:39:57 +00:00
delete propertyEntry;
propertyEntry = nullptr;
2022-03-29 03:51:15 +00:00
2022-07-28 13:39:57 +00:00
delete propertyLookup;
propertyLookup = nullptr;
2022-03-28 10:24:49 +00:00
2022-07-28 13:39:57 +00:00
propertyQueries[entity->GetObjectID()] = entries;
2022-07-28 13:39:57 +00:00
// Query here is to figure out whether or not to display the button to go to the next page or not.
int32_t numberOfProperties = 0;
2022-03-29 10:50:41 +00:00
2022-07-28 13:39:57 +00:00
auto buttonQuery = BuildQuery(entity, sortMethod, character, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false);
auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery);
2022-03-29 10:50:41 +00:00
2022-07-28 13:39:57 +00:00
propertiesLeft->setUInt(1, this->m_MapID);
propertiesLeft->setString(2, searchString.c_str());
propertiesLeft->setString(3, searchString.c_str());
propertiesLeft->setString(4, searchString.c_str());
propertiesLeft->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? 1 : 2);
2022-03-30 06:59:50 +00:00
2022-07-28 13:39:57 +00:00
auto result = propertiesLeft->executeQuery();
result->next();
numberOfProperties = result->getInt(1);
2022-03-29 10:50:41 +00:00
2022-07-28 13:39:57 +00:00
delete result;
result = nullptr;
2022-07-28 13:39:57 +00:00
delete propertiesLeft;
propertiesLeft = nullptr;
2022-03-30 23:12:42 +00:00
2022-07-28 13:39:57 +00:00
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
Add Aarch64 support (#231) * added mariadb-connector-cpp submodule * raknet aarch64 support * fix compile errors * mariadb connector swap (in progress) * update CMakeLists, add preprocessor definition to switch between mysql and mariadb connectors * update types with missing aarch64 check * corrected adding extra flag to properly compile mariadbconn in CMakeLists * updated readme with arm builds section * fix build failure if test folder does not exist * Remove mysql connector from all builds, add mariadbconnector to windows build * readd Linux check for backtrace lib to CMakeLists.txt * Separate system specific mariadbconncpp extra compile flags * Copy dlls to exes directory once built * fetch prebuilt binaries on windows so that ClangCL can be used * Delay load dll so that plugin directory is set correctly * Fixed typo in glibcxx compile flag * whitespacing, spaces -> tabs * Updated README.md, included instructions to update * Updated README.md added libssl-dev requirement and removed mysql connector references from macOS builds section * apple compile fixes for zlib and shared library name * add windows arm64 checks to raknet * remove extra . in shared library location * Setup plugins directory for the connector to search in, pass openssl_root_dir on for apple * Fix copy paths for single config generators and non windows * change plugin folder location, another single config generator fix * GENERATOR_IS_MULTI_CONFIG is a property not a variable * Fixed a few errors after merge * Fix plugin directory path, force windows to look at the right folder * fixed directory name for make_directory command * Update README.md Updated MacOS, Windows build instructions. * set INSTALL_PLUGINDIR so that the right directory is used * Support for relative rpath for docker build * added mariadb-connector-cpp submodule * raknet aarch64 support * fix compile errors * mariadb connector swap (in progress) * update CMakeLists, add preprocessor definition to switch between mysql and mariadb connectors * update types with missing aarch64 check * corrected adding extra flag to properly compile mariadbconn in CMakeLists * updated readme with arm builds section * fix build failure if test folder does not exist * Remove mysql connector from all builds, add mariadbconnector to windows build * readd Linux check for backtrace lib to CMakeLists.txt * Separate system specific mariadbconncpp extra compile flags * Copy dlls to exes directory once built * fetch prebuilt binaries on windows so that ClangCL can be used * Delay load dll so that plugin directory is set correctly * Fixed typo in glibcxx compile flag * whitespacing, spaces -> tabs * Updated README.md, included instructions to update * Updated README.md added libssl-dev requirement and removed mysql connector references from macOS builds section * apple compile fixes for zlib and shared library name * add windows arm64 checks to raknet * Setup plugins directory for the connector to search in, pass openssl_root_dir on for apple * Fix copy paths for single config generators and non windows * change plugin folder location, another single config generator fix * GENERATOR_IS_MULTI_CONFIG is a property not a variable * Fixed a few errors after merge * Fix plugin directory path, force windows to look at the right folder * fixed directory name for make_directory command * Update README.md Updated MacOS, Windows build instructions. * set INSTALL_PLUGINDIR so that the right directory is used * Support for relative rpath for docker build * Rebase on main * Remove extra git submodule * Update CMakeLists.txt * Remove CMakeLists.txt file from mariadb Remove the CMakeLists.txt file from the mariaDBConnector so we dont build the tests. Also add a config option to the CMakeVariables.txt so you can build the connector with multiple jobs * Compile on windows Specify the mariadbcpp.dll file location with a defined absolute path so windows knows it actually exists. * default to 1 job Default mariadb jobs running in parallel to 1 instead of 4 * Move mariadbcpp.dll file to the expected directory on windows * Changed plugin Updated the plugin location from the project binary directory to the expected location, the mariadb binary directory. * Addressed windows dll issues by moving files to the expected directory instead of a directory that wouldnt get created * Update README Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com> Co-authored-by: EmosewaMC <39972741+EmosewaMC@users.noreply.github.com>
2022-07-04 04:33:05 +00:00
}