DarkflameServer/dGame/dComponents/PropertyEntranceComponent.cpp

293 lines
12 KiB
C++
Raw Permalink Normal View History

#include "PropertyEntranceComponent.h"
2022-03-31 03:33:50 +00:00
2023-07-11 07:02:59 +00:00
#include "CDPropertyEntranceComponentTable.h"
2022-03-31 03:33:50 +00:00
#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"
2022-03-31 03:33:50 +00:00
#include "dLogger.h"
#include "Amf3.h"
#include "eObjectBits.h"
#include "eGameMasterLevel.h"
2023-07-11 05:18:42 +00:00
#include "DluAssert.h"
2023-07-11 07:02:59 +00:00
PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, int32_t componentID) : Component(parent) {
2023-07-11 05:18:42 +00:00
this->propertyQueries.clear();
m_ComponentId = componentID;
}
2023-07-11 05:18:42 +00:00
void PropertyEntranceComponent::LoadTemplateData() {
auto table = CDClientManager::Instance().GetTable<CDPropertyEntranceComponentTable>();
2023-07-11 05:18:42 +00:00
const auto& entry = table->GetByID(m_ComponentId);
2023-07-11 05:18:42 +00:00
m_MapID = entry.mapID;
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_ParentEntity->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) {
2023-07-11 05:18:42 +00:00
if (!entity->GetCharacter()) {
Game::logger->Log("PropertyEntranceComponent", "Entity %llu attempted to enter a property with no character attached.", entity->GetObjectID());
}
2022-07-28 13:39:57 +00:00
LWOCLONEID cloneId = 0;
2022-07-28 13:39:57 +00:00
if (index == -1 && !returnToZone) {
cloneId = entity->GetCharacter()->GetPropertyCloneID();
} else if (index >= 0) {
// Increment index once here because the first index of other player properties is 2 in the propertyQueries cache.
index++;
2023-07-11 07:02:59 +00:00
const auto& cachedQuery = propertyQueries.find(entity->GetObjectID());
2023-07-11 07:02:59 +00:00
if (cachedQuery == propertyQueries.end()) {
2023-07-11 05:18:42 +00:00
Game::logger->Log("PropertyEntranceComponent", "Player %llu:%s did not have a property query open", entity->GetObjectID(), entity->GetCharacter()->GetName().c_str());
return;
}
2023-07-11 07:02:59 +00:00
const auto& query = cachedQuery->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;
}
auto* launcher = m_ParentEntity->GetComponent<RocketLaunchpadControlComponent>();
2023-07-11 05:18:42 +00:00
if (!launcher) 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);
}
2023-07-11 07:02:59 +00:00
std::string PropertyEntranceComponent::BuildQuery(const ePropertySortType sortMethod, Character* character, const std::string& customQuery, const bool wantLimits) {
const std::string baseQueryForProperties =
R"QUERY(
SELECT p.*
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 >= ?
)QUERY";
2023-07-11 05:18:42 +00:00
DluAssert(character != nullptr);
2023-07-11 07:02:59 +00:00
const std::string base = customQuery.empty() ? baseQueryForProperties : customQuery;
std::string orderBy = "ORDER BY p.last_updated DESC";
if (sortMethod == ePropertySortType::SORT_TYPE_FEATURED || sortMethod == ePropertySortType::SORT_TYPE_FRIENDS) {
2023-07-11 05:18:42 +00:00
std::stringstream friendsStream;
friendsStream << " AND p.owner_id IN (";
2022-07-28 13:39:57 +00:00
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::PreparedStatement> 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;"
));
2022-07-28 13:39:57 +00:00
friendsListQuery->setUInt(1, character->GetID());
friendsListQuery->setUInt(2, character->GetID());
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::ResultSet> friendsListQueryResult(friendsListQuery->executeQuery());
2022-07-28 13:39:57 +00:00
2023-07-11 05:18:42 +00:00
if (friendsListQueryResult->next()) {
2023-07-11 07:02:59 +00:00
friendsStream << friendsListQueryResult->getInt("requested_player");
2023-07-11 05:18:42 +00:00
while (friendsListQueryResult->next()) {
2023-07-11 07:02:59 +00:00
friendsStream << ',' << friendsListQueryResult->getInt("requested_player");
2023-07-11 05:18:42 +00:00
}
2022-07-28 13:39:57 +00:00
}
// If we have no friends then use a -1 for the query.
2023-07-11 05:18:42 +00:00
if (friendsListQueryResult->rowsCount() == 0) friendsStream << -1;
friendsStream << ')';
2022-07-28 13:39:57 +00:00
2023-07-11 05:18:42 +00:00
orderBy += friendsStream.str() + " ORDER BY ci.name ASC";
2023-07-11 07:02:59 +00:00
} else if (sortMethod == ePropertySortType::SORT_TYPE_RECENT) {
2023-07-11 05:18:42 +00:00
orderBy = "ORDER BY p.last_updated DESC";
2023-07-11 07:02:59 +00:00
} else if (sortMethod == ePropertySortType::SORT_TYPE_REPUTATION) {
2023-07-11 05:18:42 +00:00
orderBy = "ORDER BY p.reputation DESC, p.last_updated DESC";
2022-07-28 13:39:57 +00:00
}
2023-07-11 05:18:42 +00:00
return base + orderBy + (wantLimits ? " LIMIT ? OFFSET ?;" : " ;");
}
2023-07-11 07:02:59 +00:00
void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, ePropertySortType sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr) {
std::vector<PropertySelectQueryProperty> propertyPageEntries;
PropertySelectQueryProperty requestorEntry;
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
2023-07-11 05:18:42 +00:00
// The first index of every player requested query must be the players' own property since it is visible on every page.
std::unique_ptr<sql::PreparedStatement> 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());
2023-07-11 05:18:42 +00:00
playerPropertyLookup->setInt(2, m_MapID);
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::ResultSet> playerPropertyLookupResults(playerPropertyLookup->executeQuery());
2022-07-28 13:39:57 +00:00
// If the player has a property this query will have a single result.
2023-07-11 07:02:59 +00:00
requestorEntry.CloneId = character->GetPropertyCloneID();
requestorEntry.OwnerName = character->GetName();
2022-07-28 13:39:57 +00:00
if (playerPropertyLookupResults->next()) {
2023-07-11 07:02:59 +00:00
requestorEntry.Name = std::string(playerPropertyLookupResults->getString("name").c_str());
requestorEntry.Description = std::string(playerPropertyLookupResults->getString("description").c_str());
requestorEntry.Reputation = playerPropertyLookupResults->getUInt("reputation");
requestorEntry.IsBestFriend = true;
requestorEntry.IsFriend = true;
requestorEntry.IsModeratorApproved = playerPropertyLookupResults->getBoolean("mod_approved");
requestorEntry.IsAlt = true;
requestorEntry.IsOwned = true;
requestorEntry.AccessType = playerPropertyLookupResults->getInt("privacy_option");
requestorEntry.DateLastPublished = playerPropertyLookupResults->getInt64("last_updated");
requestorEntry.PerformanceCost = playerPropertyLookupResults->getDouble("performance_cost");
2022-07-28 13:39:57 +00:00
}
2023-07-11 07:02:59 +00:00
propertyPageEntries.push_back(requestorEntry);
2023-07-11 07:02:59 +00:00
const auto query = BuildQuery(sortMethod, character);
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::PreparedStatement> propertyLookup(Database::CreatePreppedStmt(query));
2022-07-28 13:39:57 +00:00
const auto searchString = "%" + filterText + "%";
2023-07-11 07:02:59 +00:00
PropertyPrivacyOption friendsLookup =
sortMethod == ePropertySortType::SORT_TYPE_FEATURED || sortMethod == ePropertySortType::SORT_TYPE_FRIENDS ?
PropertyPrivacyOption::Friends :
PropertyPrivacyOption::Public;
2022-07-28 13:39:57 +00:00
propertyLookup->setUInt(1, this->m_MapID);
propertyLookup->setString(2, searchString.c_str());
propertyLookup->setString(3, searchString.c_str());
propertyLookup->setString(4, searchString.c_str());
2023-07-11 07:02:59 +00:00
propertyLookup->setInt(5, static_cast<uint32_t>(friendsLookup));
2022-07-28 13:39:57 +00:00
propertyLookup->setInt(6, numResults);
propertyLookup->setInt(7, startIndex);
2022-03-28 03:04:45 +00:00
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::ResultSet> propertyEntry(propertyLookup->executeQuery());
2022-03-31 03:33:50 +00:00
2022-07-28 13:39:57 +00:00
while (propertyEntry->next()) {
2023-07-11 05:18:42 +00:00
PropertySelectQueryProperty playerPropertyEntry;
2023-07-11 07:02:59 +00:00
const auto owner = propertyEntry->getInt("owner_id");
playerPropertyEntry.CloneId = propertyEntry->getUInt64("clone_id");
playerPropertyEntry.Name = std::string(propertyEntry->getString("name").c_str());
playerPropertyEntry.Description = std::string(propertyEntry->getString("description").c_str());
playerPropertyEntry.AccessType = propertyEntry->getInt("privacy_option");
playerPropertyEntry.IsModeratorApproved = propertyEntry->getBoolean("mod_approved");
playerPropertyEntry.DateLastPublished = propertyEntry->getInt("last_updated");
playerPropertyEntry.Reputation = static_cast<float>(propertyEntry->getInt("reputation"));
playerPropertyEntry.PerformanceCost = static_cast<float>(propertyEntry->getDouble("performance_cost"));
2023-07-11 05:18:42 +00:00
playerPropertyEntry.OwnerName = "";
playerPropertyEntry.IsBestFriend = false;
playerPropertyEntry.IsFriend = false;
playerPropertyEntry.IsAlt = false;
playerPropertyEntry.IsOwned = true;
std::unique_ptr<sql::PreparedStatement> nameLookup(
Database::CreatePreppedStmt(
"SELECT name FROM charinfo WHERE prop_clone_id = ?;"));
nameLookup->setUInt64(1, playerPropertyEntry.CloneId);
std::unique_ptr<sql::ResultSet> nameResult(nameLookup->executeQuery());
2022-07-28 13:39:57 +00:00
if (!nameResult->next()) {
2023-07-11 05:18:42 +00:00
Game::logger->Log("PropertyEntranceComponent", "Failed to find property owner name for %llu!", playerPropertyEntry.CloneId);
2022-07-28 13:39:57 +00:00
continue;
}
2023-07-11 05:18:42 +00:00
playerPropertyEntry.IsOwned = playerPropertyEntry.CloneId == character->GetPropertyCloneID();
2023-07-11 07:02:59 +00:00
playerPropertyEntry.OwnerName = std::string(nameResult->getString("name").c_str());
2022-03-28 03:04:45 +00:00
2022-07-28 13:39:57 +00:00
// Query to get friend and best friend fields
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::PreparedStatement> 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());
2023-07-11 05:18:42 +00:00
friendCheck->setUInt(2, owner);
friendCheck->setUInt(3, owner);
2022-07-28 13:39:57 +00:00
friendCheck->setUInt(4, character->GetID());
2022-03-28 03:04:45 +00:00
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::ResultSet> 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.
2023-07-11 05:18:42 +00:00
playerPropertyEntry.IsFriend = friendResult->next();
2023-07-11 07:02:59 +00:00
playerPropertyEntry.IsBestFriend = playerPropertyEntry.IsFriend && friendResult->getInt("best_friend") == 3;
2022-03-29 03:51:15 +00:00
2023-07-11 07:02:59 +00:00
playerPropertyEntry.IsModeratorApproved = propertyEntry->getBoolean("mod_approved");
2022-03-29 03:51:15 +00:00
2023-07-11 05:18:42 +00:00
// So lead moderators can visit the property but also see that it is still awaiting approval.
if (!playerPropertyEntry.IsModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) {
playerPropertyEntry.Name = "[AWAITING APPROVAL]";
playerPropertyEntry.Description = "[AWAITING APPROVAL]";
playerPropertyEntry.IsModeratorApproved = true;
2022-07-28 13:39:57 +00:00
}
2022-03-28 06:46:43 +00:00
2022-07-28 13:39:57 +00:00
// Query to determine whether this property is an alt character of the entity.
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::PreparedStatement> 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
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::ResultSet> isAltQueryResults(isAltQuery->executeQuery());
2022-03-28 06:46:43 +00:00
2023-07-11 05:18:42 +00:00
if (isAltQueryResults->next()) playerPropertyEntry.IsAlt = true;
2022-03-28 03:04:45 +00:00
2023-07-11 07:02:59 +00:00
propertyPageEntries.push_back(playerPropertyEntry);
2022-07-28 13:39:57 +00:00
}
2023-07-11 07:02:59 +00:00
propertyQueries[entity->GetObjectID()] = propertyPageEntries;
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.
2023-07-11 07:02:59 +00:00
const std::string baseButtonQuery =
R"QUERY(
SELECT COUNT(*) as numProperties
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 >= ?
)QUERY";
auto buttonQuery = BuildQuery(sortMethod, character, baseButtonQuery, false);
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::PreparedStatement> 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());
2023-07-11 07:02:59 +00:00
propertiesLeft->setInt(5, static_cast<uint32_t>(friendsLookup));
2022-03-30 06:59:50 +00:00
2023-07-11 05:18:42 +00:00
std::unique_ptr<sql::ResultSet> result(propertiesLeft->executeQuery());
2022-07-28 13:39:57 +00:00
result->next();
2023-07-11 05:18:42 +00:00
int32_t numberOfProperties = result->getInt("numProperties");
2022-03-30 23:12:42 +00:00
2023-07-11 07:02:59 +00:00
GameMessages::SendPropertySelectQuery(m_ParentEntity->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, propertyPageEntries, 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
}