mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-10-31 12:41:55 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/main' into ub-fixes
This commit is contained in:
		| @@ -87,7 +87,7 @@ if(UNIX) | ||||
| 	endif() | ||||
|  | ||||
| 	if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") | ||||
| 		add_compile_options("-rdynamic") | ||||
| 		add_link_options("-export-dynamic") | ||||
| 	endif() | ||||
|  | ||||
| 	if(${GGDB}) | ||||
|   | ||||
							
								
								
									
										11
									
								
								dCommon/dEnums/ePropertySortType.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								dCommon/dEnums/ePropertySortType.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #ifndef EPROPERTYSORTTYPE_H | ||||
| #define EPROPERTYSORTTYPE_H | ||||
|  | ||||
| enum ePropertySortType : int32_t { | ||||
| 	SORT_TYPE_FRIENDS = 0, | ||||
| 	SORT_TYPE_REPUTATION = 1, | ||||
| 	SORT_TYPE_RECENT = 3, | ||||
| 	SORT_TYPE_FEATURED = 5 | ||||
| }; | ||||
|  | ||||
| #endif  //!EPROPERTYSORTTYPE_H | ||||
| @@ -4,6 +4,8 @@ | ||||
| #include <cstdint> | ||||
| #include <optional> | ||||
|  | ||||
| enum ePropertySortType : int32_t; | ||||
|  | ||||
| class IProperty { | ||||
| public: | ||||
| 	struct Info { | ||||
| @@ -18,11 +20,33 @@ public: | ||||
| 		uint32_t lastUpdatedTime{}; | ||||
| 		uint32_t claimedTime{}; | ||||
| 		uint32_t reputation{}; | ||||
| 		float performanceCost{}; | ||||
| 	}; | ||||
|  | ||||
| 	struct PropertyLookup { | ||||
| 		uint32_t mapId{}; | ||||
| 		std::string searchString; | ||||
| 		ePropertySortType sortChoice{}; | ||||
| 		uint32_t playerId{}; | ||||
| 		uint32_t numResults{}; | ||||
| 		uint32_t startIndex{}; | ||||
| 		uint32_t playerSort{}; | ||||
| 	}; | ||||
|  | ||||
| 	struct PropertyEntranceResult { | ||||
| 		int32_t totalEntriesMatchingQuery{}; | ||||
| 		// The entries that match the query. This should only contain up to 12 entries. | ||||
| 		std::vector<IProperty::Info> entries; | ||||
| 	}; | ||||
|  | ||||
| 	// Get the property info for the given property id. | ||||
| 	virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0; | ||||
|  | ||||
| 	// Get the properties for the given property lookup params. | ||||
| 	// This is expected to return a result set of up to 12 properties | ||||
| 	// so as not to transfer too much data at once. | ||||
| 	virtual std::optional<IProperty::PropertyEntranceResult> GetProperties(const PropertyLookup& params) = 0; | ||||
|  | ||||
| 	// Update the property moderation info for the given property id. | ||||
| 	virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0; | ||||
| 	 | ||||
|   | ||||
| @@ -112,6 +112,7 @@ public: | ||||
| 	std::string GetBehavior(const int32_t behaviorId) override; | ||||
| 	void RemoveBehavior(const int32_t characterId) override; | ||||
| 	void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override; | ||||
| 	std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override; | ||||
| private: | ||||
|  | ||||
| 	// Generic query functions that can be used for any query. | ||||
|   | ||||
| @@ -1,8 +1,140 @@ | ||||
| #include "MySQLDatabase.h" | ||||
| #include "ePropertySortType.h" | ||||
|  | ||||
| std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) { | ||||
| 	std::optional<IProperty::PropertyEntranceResult> result; | ||||
| 	std::string query; | ||||
| 	std::unique_ptr<sql::ResultSet> properties; | ||||
|  | ||||
| 	if (params.sortChoice == SORT_TYPE_FEATURED || params.sortChoice == SORT_TYPE_FRIENDS) { | ||||
| 		query = R"QUERY( | ||||
| 		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 >= ? | ||||
| 		AND p.owner_id IN ( | ||||
| 			SELECT fr.requested_player AS player FROM ( | ||||
| 				SELECT CASE  | ||||
| 				WHEN player_id = ? THEN friend_id  | ||||
| 				WHEN friend_id = ? THEN player_id  | ||||
| 				END AS requested_player FROM friends | ||||
| 			) AS fr  | ||||
| 			JOIN charinfo AS ci ON ci.id = fr.requested_player  | ||||
| 			WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ? | ||||
| 		) ORDER BY ci.name ASC | ||||
| 		)QUERY"; | ||||
| 		const auto completeQuery = "SELECT p.* " + query + " LIMIT ? OFFSET ?;"; | ||||
| 		properties = ExecuteSelect( | ||||
| 			completeQuery, | ||||
| 			params.mapId, | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			params.playerSort, | ||||
| 			params.playerId, | ||||
| 			params.playerId, | ||||
| 			params.playerId, | ||||
| 			params.numResults, | ||||
| 			params.startIndex | ||||
| 		); | ||||
| 		const auto countQuery = "SELECT COUNT(*) as count" + query + ";"; | ||||
| 		auto count = ExecuteSelect( | ||||
| 			countQuery, | ||||
| 			params.mapId, | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			params.playerSort, | ||||
| 			params.playerId, | ||||
| 			params.playerId, | ||||
| 			params.playerId | ||||
| 		); | ||||
| 		if (count->next()) { | ||||
| 			result->totalEntriesMatchingQuery = count->getUInt("count"); | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (params.sortChoice == SORT_TYPE_REPUTATION) { | ||||
| 			query = R"QUERY( | ||||
| 			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 >= ? | ||||
| 			ORDER BY p.reputation DESC, p.last_updated DESC  | ||||
| 			)QUERY"; | ||||
| 		} else { | ||||
| 			query = R"QUERY( | ||||
| 			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 >= ? | ||||
| 			ORDER BY p.last_updated DESC | ||||
| 			)QUERY"; | ||||
| 		} | ||||
| 		const auto completeQuery = "SELECT p.* " + query + " LIMIT ? OFFSET ?;"; | ||||
| 		properties = ExecuteSelect( | ||||
| 			completeQuery, | ||||
| 			params.mapId, | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			params.playerSort, | ||||
| 			params.numResults, | ||||
| 			params.startIndex | ||||
| 		); | ||||
| 		const auto countQuery = "SELECT COUNT(*) as count" + query + ";"; | ||||
| 		auto count = ExecuteSelect( | ||||
| 			countQuery, | ||||
| 			params.mapId, | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			"%" + params.searchString + "%", | ||||
| 			params.playerSort | ||||
| 		); | ||||
| 		if (count->next()) { | ||||
| 			result->totalEntriesMatchingQuery = count->getUInt("count"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	while (properties->next()) { | ||||
| 		auto& entry = result->entries.emplace_back(); | ||||
| 		entry.id = properties->getUInt64("id"); | ||||
| 		entry.ownerId = properties->getUInt64("owner_id"); | ||||
| 		entry.cloneId = properties->getUInt64("clone_id"); | ||||
| 		entry.name = properties->getString("name").c_str(); | ||||
| 		entry.description = properties->getString("description").c_str(); | ||||
| 		entry.privacyOption = properties->getInt("privacy_option"); | ||||
| 		entry.rejectionReason = properties->getString("rejection_reason").c_str(); | ||||
| 		entry.lastUpdatedTime = properties->getUInt("last_updated"); | ||||
| 		entry.claimedTime = properties->getUInt("time_claimed"); | ||||
| 		entry.reputation = properties->getUInt("reputation"); | ||||
| 		entry.modApproved = properties->getUInt("mod_approved"); | ||||
| 		entry.performanceCost = properties->getFloat("performance_cost"); | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) { | ||||
| 	auto propertyEntry = ExecuteSelect( | ||||
| 		"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved " | ||||
| 		"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved, performance_cost " | ||||
| 		"FROM properties WHERE zone_id = ? AND clone_id = ?;", mapId, cloneId); | ||||
|  | ||||
| 	if (!propertyEntry->next()) { | ||||
| @@ -21,6 +153,7 @@ std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID map | ||||
| 	toReturn.claimedTime = propertyEntry->getUInt("time_claimed"); | ||||
| 	toReturn.reputation = propertyEntry->getUInt("reputation"); | ||||
| 	toReturn.modApproved = propertyEntry->getUInt("mod_approved"); | ||||
| 	toReturn.performanceCost = propertyEntry->getFloat("performance_cost"); | ||||
|  | ||||
| 	return toReturn; | ||||
| } | ||||
|   | ||||
| @@ -90,6 +90,7 @@ class TestSQLDatabase : public GameDatabase { | ||||
| 	std::string GetBehavior(const int32_t behaviorId) override; | ||||
| 	void RemoveBehavior(const int32_t behaviorId) override; | ||||
| 	void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override; | ||||
| 	std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; }; | ||||
| }; | ||||
|  | ||||
| #endif  //!TESTSQLDATABASE_H | ||||
|   | ||||
| @@ -1351,11 +1351,6 @@ void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) { | ||||
| 		callback(other); | ||||
| 	} | ||||
|  | ||||
| 	SwitchComponent* switchComp = GetComponent<SwitchComponent>(); | ||||
| 	if (switchComp) { | ||||
| 		switchComp->EntityEnter(other); | ||||
| 	} | ||||
|  | ||||
| 	TriggerEvent(eTriggerEventType::ENTER, other); | ||||
|  | ||||
| 	// POI system | ||||
|   | ||||
| @@ -25,7 +25,7 @@ public: | ||||
| 	User& operator=(const User& other); | ||||
| 	bool operator==(const User& other) const; | ||||
|  | ||||
| 	uint32_t GetAccountID() { return m_AccountID; } | ||||
| 	uint32_t GetAccountID() const noexcept { return m_AccountID; } | ||||
| 	std::string& GetUsername() { return m_Username; } | ||||
| 	std::string& GetSessionKey() { return m_SessionKey; } | ||||
| 	SystemAddress& GetSystemAddress() { return m_SystemAddress; } | ||||
|   | ||||
| @@ -381,7 +381,7 @@ void PetComponent::Update(float deltaTime) { | ||||
| 			float distance = Vector3::DistanceSquared(position, switchPosition); | ||||
| 			if (distance < 3 * 3) { | ||||
| 				m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); | ||||
| 				closestSwitch->EntityEnter(m_Parent); | ||||
| 				closestSwitch->OnUse(m_Parent); | ||||
| 			} else if (distance < 20 * 20) { | ||||
| 				haltDistance = 1; | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,8 @@ | ||||
| #include "Amf3.h" | ||||
| #include "eObjectBits.h" | ||||
| #include "eGameMasterLevel.h" | ||||
| #include "ePropertySortType.h" | ||||
| #include "User.h" | ||||
|  | ||||
| PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, uint32_t componentID) : Component(parent) { | ||||
| 	this->propertyQueries = {}; | ||||
| @@ -74,261 +76,103 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, | ||||
| 	launcher->Launch(entity, launcher->GetTargetZone(), cloneId); | ||||
| } | ||||
|  | ||||
| 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) { | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery, bool wantLimits) { | ||||
| 	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::Get()->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 ?;" : ";"); | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|  | ||||
| 	std::vector<PropertySelectQueryProperty> entries{}; | ||||
| 	PropertySelectQueryProperty playerEntry{}; | ||||
|  | ||||
| 	auto character = entity->GetCharacter(); | ||||
| 	const auto* const character = entity->GetCharacter(); | ||||
| 	if (!character) return; | ||||
| 	const auto* const user = character->GetParentUser(); | ||||
| 	if (!user) return; | ||||
|  | ||||
| 	auto& entries = propertyQueries[entity->GetObjectID()]; | ||||
| 	entries.clear(); | ||||
| 	// Player property goes in index 1 of the vector.  This is how the client expects it. | ||||
| 	auto playerPropertyLookup = Database::Get()->CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?"); | ||||
|  | ||||
| 	playerPropertyLookup->setInt(1, character->GetID()); | ||||
| 	playerPropertyLookup->setInt(2, this->m_MapID); | ||||
|  | ||||
| 	auto playerPropertyLookupResults = playerPropertyLookup->executeQuery(); | ||||
| 	const auto playerProperty = Database::Get()->GetPropertyInfo(m_MapID, character->GetPropertyCloneID()); | ||||
|  | ||||
| 	// 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 = playerPropertyLookupResults->getFloat(16); | ||||
|  | ||||
| 		playerEntry = SetPropertyValues(playerEntry, cloneId, character->GetName(), propertyName, propertyDescription, reputation, true, true, modApproved, true, true, privacyOption, dateLastUpdated, performanceCost); | ||||
| 	auto& playerEntry = entries.emplace_back(); | ||||
| 	if (playerProperty.has_value()) { | ||||
| 		playerEntry.OwnerName = character->GetName(); | ||||
| 		playerEntry.IsBestFriend = true; | ||||
| 		playerEntry.IsFriend = true; | ||||
| 		playerEntry.IsAlt = true; | ||||
| 		playerEntry.IsOwned = true; | ||||
| 		playerEntry.CloneId = playerProperty->cloneId; | ||||
| 		playerEntry.Name = playerProperty->name; | ||||
| 		playerEntry.Description = playerProperty->description; | ||||
| 		playerEntry.AccessType = playerProperty->privacyOption; | ||||
| 		playerEntry.IsModeratorApproved = playerProperty->modApproved; | ||||
| 		playerEntry.DateLastPublished = playerProperty->lastUpdatedTime; | ||||
| 		playerEntry.Reputation = playerProperty->reputation; | ||||
| 		playerEntry.PerformanceCost = playerProperty->performanceCost; | ||||
| 		auto& entry = playerEntry; | ||||
| 	} else { | ||||
| 		playerEntry = SetPropertyValues(playerEntry, character->GetPropertyCloneID(), character->GetName(), "", "", 0, true, true); | ||||
| 		playerEntry.OwnerName = character->GetName(); | ||||
| 		playerEntry.IsBestFriend = true; | ||||
| 		playerEntry.IsFriend = true; | ||||
| 		playerEntry.IsAlt = false; | ||||
| 		playerEntry.IsOwned = false; | ||||
| 		playerEntry.CloneId = character->GetPropertyCloneID(); | ||||
| 		playerEntry.Name = ""; | ||||
| 		playerEntry.Description = ""; | ||||
| 		playerEntry.AccessType = 0; | ||||
| 		playerEntry.IsModeratorApproved = false; | ||||
| 		playerEntry.DateLastPublished = 0; | ||||
| 		playerEntry.Reputation = 0; | ||||
| 		playerEntry.PerformanceCost = 0.0f; | ||||
| 	} | ||||
|  | ||||
| 	delete playerPropertyLookupResults; | ||||
| 	playerPropertyLookupResults = nullptr; | ||||
| 	IProperty::PropertyLookup propertyLookup; | ||||
| 	propertyLookup.mapId = m_MapID; | ||||
| 	propertyLookup.searchString = filterText; | ||||
| 	propertyLookup.sortChoice = static_cast<ePropertySortType>(sortMethod); | ||||
| 	propertyLookup.playerSort = static_cast<uint32_t>(sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? PropertyPrivacyOption::Friends : PropertyPrivacyOption::Public); | ||||
| 	propertyLookup.playerId = character->GetID(); | ||||
| 	propertyLookup.numResults = numResults; | ||||
| 	propertyLookup.startIndex = startIndex; | ||||
|  | ||||
| 	delete playerPropertyLookup; | ||||
| 	playerPropertyLookup = nullptr; | ||||
|  | ||||
| 	entries.push_back(playerEntry); | ||||
|  | ||||
| 	const auto query = BuildQuery(entity, sortMethod, character); | ||||
|  | ||||
| 	auto propertyLookup = Database::Get()->CreatePreppedStmt(query); | ||||
|  | ||||
| 	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 ? static_cast<uint32_t>(PropertyPrivacyOption::Friends) : static_cast<uint32_t>(PropertyPrivacyOption::Public)); | ||||
| 	propertyLookup->setInt(6, numResults); | ||||
| 	propertyLookup->setInt(7, startIndex); | ||||
|  | ||||
| 	auto propertyEntry = propertyLookup->executeQuery(); | ||||
|  | ||||
| 	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 = propertyEntry->getFloat(16); | ||||
|  | ||||
| 		PropertySelectQueryProperty entry{}; | ||||
|  | ||||
| 		std::string ownerName = ""; | ||||
| 		bool isOwned = true; | ||||
| 		auto nameLookup = Database::Get()->CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); | ||||
|  | ||||
| 		nameLookup->setUInt64(1, cloneId); | ||||
|  | ||||
| 		auto nameResult = nameLookup->executeQuery(); | ||||
|  | ||||
| 		if (!nameResult->next()) { | ||||
| 			delete nameLookup; | ||||
| 			nameLookup = nullptr; | ||||
|  | ||||
| 			LOG("Failed to find property owner name for %llu!", cloneId); | ||||
| 	const auto lookupResult = Database::Get()->GetProperties(propertyLookup); | ||||
|  | ||||
| 	for (const auto& propertyEntry : lookupResult->entries) { | ||||
| 		const auto owner = propertyEntry.ownerId; | ||||
| 		const auto otherCharacter = Database::Get()->GetCharacterInfo(owner); | ||||
| 		if (!otherCharacter.has_value()) { | ||||
| 			LOG("Failed to find property owner name for %u!", owner); | ||||
| 			continue; | ||||
| 		} else { | ||||
| 			isOwned = cloneId == character->GetPropertyCloneID(); | ||||
| 			ownerName = std::string(nameResult->getString(1).c_str()); | ||||
| 		} | ||||
| 		auto& entry = entries.emplace_back(); | ||||
|  | ||||
| 		delete nameResult; | ||||
| 		nameResult = nullptr; | ||||
|  | ||||
| 		delete nameLookup; | ||||
| 		nameLookup = nullptr; | ||||
|  | ||||
| 		std::string propertyName = propertyNameFromDb; | ||||
| 		std::string propertyDescription = propertyDescriptionFromDb; | ||||
|  | ||||
| 		bool isBestFriend = false; | ||||
| 		bool isFriend = false; | ||||
|  | ||||
| 		// Convert owner char id to LWOOBJID | ||||
| 		LWOOBJID ownerObjId = owner; | ||||
| 		GeneralUtils::SetBit(ownerObjId, eObjectBits::CHARACTER); | ||||
| 		GeneralUtils::SetBit(ownerObjId, eObjectBits::PERSISTENT); | ||||
|  | ||||
| 		entry.IsOwned = entry.CloneId == otherCharacter->cloneId; | ||||
| 		entry.OwnerName = otherCharacter->name; | ||||
| 		entry.CloneId = propertyEntry.cloneId; | ||||
| 		entry.Name = propertyEntry.name; | ||||
| 		entry.Description = propertyEntry.description; | ||||
| 		entry.AccessType = propertyEntry.privacyOption; | ||||
| 		entry.IsModeratorApproved = propertyEntry.modApproved; | ||||
| 		entry.DateLastPublished = propertyEntry.lastUpdatedTime; | ||||
| 		entry.Reputation = propertyEntry.reputation; | ||||
| 		entry.PerformanceCost = propertyEntry.performanceCost; | ||||
| 		entry.IsBestFriend = false; | ||||
| 		entry.IsFriend = false; | ||||
| 		// Query to get friend and best friend fields | ||||
| 		auto friendCheck = Database::Get()->CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)"); | ||||
|  | ||||
| 		friendCheck->setUInt(1, character->GetID()); | ||||
| 		friendCheck->setUInt(2, ownerObjId); | ||||
| 		friendCheck->setUInt(3, ownerObjId); | ||||
| 		friendCheck->setUInt(4, character->GetID()); | ||||
|  | ||||
| 		auto friendResult = friendCheck->executeQuery(); | ||||
|  | ||||
| 		const auto friendCheck = Database::Get()->GetBestFriendStatus(character->GetID(), owner); | ||||
| 		// If we got a result than the two players are friends. | ||||
| 		if (friendResult->next()) { | ||||
| 			isFriend = true; | ||||
| 			if (friendResult->getInt(1) == 3) { | ||||
| 				isBestFriend = true; | ||||
| 			} | ||||
| 		if (friendCheck.has_value()) { | ||||
| 			entry.IsFriend = true; | ||||
| 			entry.IsBestFriend = friendCheck->bestFriendStatus == 3; | ||||
| 		} | ||||
|  | ||||
| 		delete friendCheck; | ||||
| 		friendCheck = nullptr; | ||||
|  | ||||
| 		delete friendResult; | ||||
| 		friendResult = nullptr; | ||||
|  | ||||
| 		bool isModeratorApproved = propertyEntry->getBoolean(10); | ||||
|  | ||||
| 		if (!isModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) { | ||||
| 			propertyName = "[AWAITING APPROVAL]"; | ||||
| 			propertyDescription = "[AWAITING APPROVAL]"; | ||||
| 			isModeratorApproved = true; | ||||
| 		if (!entry.IsModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) { | ||||
| 			entry.Name = "[AWAITING APPROVAL]"; | ||||
| 			entry.Description = "[AWAITING APPROVAL]"; | ||||
| 			entry.IsModeratorApproved = true; | ||||
| 		} | ||||
|  | ||||
| 		bool isAlt = false; | ||||
| 		// Query to determine whether this property is an alt character of the entity. | ||||
| 		auto isAltQuery = Database::Get()->CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;"); | ||||
|  | ||||
| 		isAltQuery->setInt(1, character->GetID()); | ||||
| 		isAltQuery->setInt(2, owner); | ||||
|  | ||||
| 		auto isAltQueryResults = isAltQuery->executeQuery(); | ||||
|  | ||||
| 		if (isAltQueryResults->next()) { | ||||
| 			isAlt = true; | ||||
| 		for (const auto charid : Database::Get()->GetAccountCharacterIds(user->GetAccountID())) { | ||||
| 			entry.IsAlt = charid == owner; | ||||
| 			if (entry.IsAlt) break; | ||||
| 		} | ||||
|  | ||||
| 		delete isAltQueryResults; | ||||
| 		isAltQueryResults = nullptr; | ||||
|  | ||||
| 		delete isAltQuery; | ||||
| 		isAltQuery = nullptr; | ||||
|  | ||||
| 		entry = SetPropertyValues(entry, cloneId, ownerName, propertyName, propertyDescription, reputation, isBestFriend, isFriend, isModeratorApproved, isAlt, isOwned, privacyOption, dateLastUpdated, performanceCost); | ||||
|  | ||||
| 		entries.push_back(entry); | ||||
| 	} | ||||
|  | ||||
| 	delete propertyEntry; | ||||
| 	propertyEntry = nullptr; | ||||
|  | ||||
| 	delete propertyLookup; | ||||
| 	propertyLookup = nullptr; | ||||
|  | ||||
| 	propertyQueries[entity->GetObjectID()] = entries; | ||||
|  | ||||
| 	// 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; | ||||
|  | ||||
| 	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::Get()->CreatePreppedStmt(buttonQuery); | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| 	auto result = propertiesLeft->executeQuery(); | ||||
| 	result->next(); | ||||
| 	numberOfProperties = result->getInt(1); | ||||
|  | ||||
| 	delete result; | ||||
| 	result = nullptr; | ||||
|  | ||||
| 	delete propertiesLeft; | ||||
| 	propertiesLeft = nullptr; | ||||
|  | ||||
| 	GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr); | ||||
| 	GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, lookupResult->totalEntriesMatchingQuery - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr); | ||||
| } | ||||
|   | ||||
| @@ -57,11 +57,7 @@ public: | ||||
| 	 * Returns the map ID for this property | ||||
| 	 * @return the map ID for this property | ||||
| 	 */ | ||||
| 	[[nodiscard]] LWOMAPID GetMapID() const { return m_MapID; }; | ||||
|  | ||||
| 	PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f); | ||||
|  | ||||
| 	std::string BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery = "", bool wantLimits = true); | ||||
| 	[[nodiscard]] LWOMAPID GetMapID() const noexcept { return m_MapID; }; | ||||
|  | ||||
| private: | ||||
| 	/** | ||||
| @@ -78,13 +74,4 @@ private: | ||||
| 	 * The base map ID for this property (Avant Grove, etc). | ||||
| 	 */ | ||||
| 	LWOMAPID m_MapID; | ||||
|  | ||||
| 	enum ePropertySortType : int32_t { | ||||
| 		SORT_TYPE_FRIENDS = 0, | ||||
| 		SORT_TYPE_REPUTATION = 1, | ||||
| 		SORT_TYPE_RECENT = 3, | ||||
| 		SORT_TYPE_FEATURED = 5 | ||||
| 	}; | ||||
|  | ||||
| 	std::string baseQueryForProperties = "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 >= ? "; | ||||
| }; | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| #include "EntityManager.h" | ||||
| #include "eTriggerEventType.h" | ||||
| #include "RenderComponent.h" | ||||
| #include "DestroyableComponent.h" | ||||
|  | ||||
| std::vector<SwitchComponent*> SwitchComponent::petSwitches; | ||||
|  | ||||
| @@ -11,6 +12,13 @@ SwitchComponent::SwitchComponent(Entity* parent) : Component(parent) { | ||||
| 	m_ResetTime = m_Parent->GetVarAs<int32_t>(u"switch_reset_time"); | ||||
|  | ||||
| 	m_QuickBuild = m_Parent->GetComponent<QuickBuildComponent>(); | ||||
|  | ||||
| 	const auto factions = GeneralUtils::SplitString(m_Parent->GetVar<std::u16string>(u"respond_to_faction"), u':'); | ||||
| 	for (const auto& faction : factions) { | ||||
| 		auto factionID = GeneralUtils::TryParse<int32_t>(GeneralUtils::UTF16ToWTF8(faction)); | ||||
| 		if (!factionID) continue; | ||||
| 		m_FactionsToRespondTo.push_back(factionID.value()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| SwitchComponent::~SwitchComponent() { | ||||
| @@ -25,6 +33,17 @@ void SwitchComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitial | ||||
| 	outBitStream.Write(m_Active); | ||||
| } | ||||
|  | ||||
| void SwitchComponent::OnUse(Entity* originator) { | ||||
| 	const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>(); | ||||
| 	if (!destroyableComponent) return; | ||||
| 	for (const auto faction : m_FactionsToRespondTo) { | ||||
| 		if (destroyableComponent->HasFaction(faction)) { | ||||
| 			EntityEnter(originator); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void SwitchComponent::SetActive(bool active) { | ||||
| 	m_Active = active; | ||||
|  | ||||
| @@ -63,6 +82,7 @@ void SwitchComponent::EntityEnter(Entity* entity) { | ||||
| 			RenderComponent::PlayAnimation(m_Parent, u"engaged"); | ||||
| 			m_PetBouncer->SetPetBouncerEnabled(true); | ||||
| 		} else { | ||||
| 			GameMessages::SendKnockback(entity->GetObjectID(), m_Parent->GetObjectID(), m_Parent->GetObjectID(), 0.0f, NiPoint3(0.0f, 17.0f, 0.0f)); | ||||
| 			Game::entityManager->SerializeEntity(m_Parent); | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -22,6 +22,7 @@ public: | ||||
| 	~SwitchComponent() override; | ||||
|  | ||||
| 	void Update(float deltaTime) override; | ||||
| 	void OnUse(Entity* originator) override; | ||||
|  | ||||
| 	Entity* GetParentEntity() const; | ||||
|  | ||||
| @@ -101,6 +102,8 @@ private: | ||||
| 	 * Attached pet bouncer | ||||
| 	 */ | ||||
| 	BouncerComponent* m_PetBouncer = nullptr; | ||||
|  | ||||
| 	std::vector<int32_t> m_FactionsToRespondTo{}; | ||||
| }; | ||||
|  | ||||
| #endif // SWITCHCOMPONENT_H | ||||
|   | ||||
| @@ -29,10 +29,10 @@ void PropertySelectQueryProperty::Serialize(RakNet::BitStream& stream) const { | ||||
| 	stream.Write(IsOwned); | ||||
| 	stream.Write(AccessType); | ||||
| 	stream.Write(DateLastPublished); | ||||
| 	stream.Write(PerformanceIndex); | ||||
| 	stream.Write(PerformanceCost); | ||||
| } | ||||
|  | ||||
| void PropertySelectQueryProperty::Deserialize(RakNet::BitStream& stream) const { | ||||
| 	// Do we need this? | ||||
| 	// no | ||||
| } | ||||
|   | ||||
| @@ -5,8 +5,7 @@ | ||||
|  | ||||
| #include "Entity.h" | ||||
|  | ||||
| class PropertySelectQueryProperty final | ||||
| { | ||||
| class PropertySelectQueryProperty final { | ||||
| public: | ||||
| 	void Serialize(RakNet::BitStream& stream) const; | ||||
|  | ||||
| @@ -23,9 +22,8 @@ public: | ||||
| 	bool IsAlt = false;                             // Whether or not the property is owned by an alt of the account owner | ||||
| 	bool IsOwned = false;                           // Whether or not the property is owned | ||||
| 	uint32_t AccessType = 0;                        // The privacy option of the property | ||||
| 	uint32_t DateLastPublished = 0;                 // The last day the property was published | ||||
| 	uint64_t DateLastPublished = 0;                 // The last day the property was published | ||||
| 	float PerformanceCost = 0;                      // The performance cost of the property | ||||
| 	uint32_t PerformanceIndex = 0;                  // The performance index of the property?  Always 0? | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -12,7 +12,10 @@ void BaseFootRaceManager::OnFireEventServerSide(Entity* self, Entity* sender, st | ||||
| 	if (splitArguments.size() > 1) { | ||||
|  | ||||
| 		const auto eventName = splitArguments[0]; | ||||
| 		const auto player = Game::entityManager->GetEntity(std::stoull(splitArguments[1])); | ||||
| 		auto playerId = GeneralUtils::TryParse<LWOOBJID>(splitArguments[1]); | ||||
| 		if (!playerId) return; | ||||
|  | ||||
| 		const auto player = Game::entityManager->GetEntity(playerId.value()); | ||||
|  | ||||
| 		if (player != nullptr) { | ||||
| 			if (eventName == "updatePlayer") { | ||||
|   | ||||
| @@ -131,12 +131,16 @@ void Level::ReadChunks(std::istream& file) { | ||||
| 			if (initPos == std::streamoff(0)) { //Really old chunk version | ||||
| 				file.seekg(0); | ||||
| 				Header header; | ||||
| 				header.id = ChunkTypeID::FileInfo; //I guess? | ||||
| 				header.id = ChunkTypeID::FileInfo; | ||||
| 				BinaryIO::BinaryRead(file, header.chunkVersion); | ||||
| 				BinaryIO::BinaryRead(file, header.chunkType); | ||||
| 				file.ignore(1); | ||||
| 				BinaryIO::BinaryRead(file, header.fileInfo.revision); | ||||
|  | ||||
| 				uint8_t important = 0; | ||||
| 				BinaryIO::BinaryRead(file, important); | ||||
| 				// file.ignore(1); //probably used | ||||
| 				if (header.chunkVersion > 36) { | ||||
| 					BinaryIO::BinaryRead(file, header.fileInfo.revision); | ||||
| 				} | ||||
| 				// HARDCODED 3 | ||||
| 				if (header.chunkVersion >= 45) file.ignore(4); | ||||
| 				file.ignore(4 * (4 * 3)); | ||||
|  | ||||
| @@ -170,25 +174,32 @@ void Level::ReadChunks(std::istream& file) { | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				for (uint32_t i = 0; i < 6; ++i) { | ||||
| 					uint32_t count = 0; | ||||
| 					BinaryIO::BinaryRead(file, count); | ||||
| 					file.ignore(count); | ||||
| 				} | ||||
|  | ||||
| 				file.ignore(4); | ||||
|  | ||||
| 				// skydome info | ||||
| 				uint32_t count = 0; | ||||
| 				BinaryIO::BinaryRead(file, count); | ||||
| 				file.ignore(count * 12); | ||||
| 				file.ignore(count); | ||||
|  | ||||
| 				if (header.chunkVersion >= 33) { | ||||
| 					for (uint32_t i = 0; i < 5; ++i) { | ||||
| 						uint32_t count = 0; | ||||
| 						BinaryIO::BinaryRead(file, count); | ||||
| 						file.ignore(count); | ||||
| 					} | ||||
| 				} | ||||
| 				// editor settings | ||||
| 				if (!important && header.chunkVersion >= 37){ | ||||
| 					file.ignore(4); | ||||
|  | ||||
| 					uint32_t count = 0; | ||||
| 					BinaryIO::BinaryRead(file, count); | ||||
| 					file.ignore(count * 12); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				header.id = ChunkTypeID::SceneObjectData; | ||||
| 				header.fileInfo.version = header.chunkVersion; | ||||
| 				ReadSceneObjectDataChunk(file, header); | ||||
| 				m_ChunkHeaders.insert(std::make_pair(header.id, header)); | ||||
|  | ||||
| 				//Now pretend to be a normal file and read Objects chunk: | ||||
| 				Header hdr; | ||||
| 				hdr.id = ChunkTypeID::SceneObjectData; | ||||
| 				ReadSceneObjectDataChunk(file, hdr); | ||||
| 				m_ChunkHeaders.insert(std::make_pair(hdr.id, hdr)); | ||||
| 			} break; | ||||
| 		} | ||||
| 	} | ||||
| @@ -224,8 +235,14 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) { | ||||
| 		BinaryIO::BinaryRead(file, obj.id); | ||||
| 		BinaryIO::BinaryRead(file, obj.lot); | ||||
|  | ||||
| 		/*if (header.fileInfo->version >= 0x26)*/ BinaryIO::BinaryRead(file, obj.nodeType); | ||||
| 		/*if (header.fileInfo->version >= 0x20)*/ BinaryIO::BinaryRead(file, obj.glomId); | ||||
| 		if (header.fileInfo.version >= 38) { | ||||
| 			uint32_t tmp = 1; | ||||
| 			BinaryIO::BinaryRead(file, tmp); | ||||
| 			if (tmp > -1 && tmp < 11) obj.nodeType = tmp; | ||||
| 		} | ||||
| 		if (header.fileInfo.version >= 32) { | ||||
| 			BinaryIO::BinaryRead(file, obj.glomId); | ||||
| 		} | ||||
|  | ||||
| 		BinaryIO::BinaryRead(file, obj.position); | ||||
| 		BinaryIO::BinaryRead(file, obj.rotation); | ||||
|   | ||||
| @@ -160,6 +160,7 @@ std::string Zone::GetFilePathForZoneID() { | ||||
| 	if (zone != nullptr) { | ||||
| 		std::string toReturn = "maps/" + zone->zoneName; | ||||
| 		std::transform(toReturn.begin(), toReturn.end(), toReturn.begin(), ::tolower); | ||||
| 		std::ranges::replace(toReturn, '\\', '/'); | ||||
| 		return toReturn; | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 jadebenn
					jadebenn