feat: add mission progression for behaviors (#1962)

* feat: add mission progression for behaviors

* Add const to ptr

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
David Markowitz
2026-03-31 13:02:23 -07:00
committed by GitHub
parent 8283d1fa95
commit 8dfdca7fbd
7 changed files with 43 additions and 14 deletions

View File

@@ -9,6 +9,28 @@
typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef; typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef;
typedef std::unique_ptr<sql::ResultSet> UniqueResultSet; typedef std::unique_ptr<sql::ResultSet> UniqueResultSet;
// Holds a PreparedStatement and its ResultSet together to ensure the statement
// outlives the result.
struct PreparedStmtResultSet {
std::unique_ptr<sql::PreparedStatement> m_stmt;
std::unique_ptr<sql::ResultSet> m_resultSet;
PreparedStmtResultSet(sql::PreparedStatement* stmt = nullptr, sql::ResultSet* resultSet = nullptr)
: m_stmt(stmt), m_resultSet(resultSet) {}
PreparedStmtResultSet(PreparedStmtResultSet&&) = default;
PreparedStmtResultSet& operator=(PreparedStmtResultSet&&) = default;
~PreparedStmtResultSet() {
m_resultSet.reset();
m_stmt.reset();
}
sql::ResultSet* operator->() const {
return m_resultSet.get();
}
};
// Purposefully no definition for this to provide linker errors in the case someone tries to // Purposefully no definition for this to provide linker errors in the case someone tries to
// bind a parameter to a type that isn't defined. // bind a parameter to a type that isn't defined.
template<typename ParamType> template<typename ParamType>
@@ -136,12 +158,15 @@ private:
// Generic query functions that can be used for any query. // Generic query functions that can be used for any query.
// Return type may be different depending on the query, so it is up to the caller to check the return type. // Return type may be different depending on the query, so it is up to the caller to check the return type.
// The first argument is the query string, and the rest are the parameters to bind to the query. // The first argument is the query string, and the rest are the parameters to bind to the query.
// The return type is a unique_ptr to the result set, which is deleted automatically when it goes out of scope // The return type is a PreparedStmtResultSet which keeps the PreparedStatement alive alongside the ResultSet.
template<typename... Args> template<typename... Args>
inline std::unique_ptr<sql::ResultSet> ExecuteSelect(const std::string& query, Args&&... args) { inline PreparedStmtResultSet ExecuteSelect(const std::string& query, Args&&... args) {
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query)); std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
SetParams(preppedStmt, std::forward<Args>(args)...); SetParams(preppedStmt, std::forward<Args>(args)...);
DLU_SQL_TRY_CATCH_RETHROW(return std::unique_ptr<sql::ResultSet>(preppedStmt->executeQuery())); std::unique_ptr<sql::ResultSet> resultSet;
DLU_SQL_TRY_CATCH_RETHROW(resultSet.reset(preppedStmt->executeQuery()));
// Release ownership of the pointers to the PreparedStatement and ResultSet to the PreparedStmtResultSet struct, which will ensure they are properly cleaned up.
return PreparedStmtResultSet(preppedStmt.release(), resultSet.release());
} }
template<typename... Args> template<typename... Args>

View File

@@ -12,7 +12,7 @@ std::vector<std::string> MySQLDatabase::GetApprovedCharacterNames() {
return toReturn; return toReturn;
} }
std::optional<ICharInfo::Info> CharInfoFromQueryResult(std::unique_ptr<sql::ResultSet> stmt) { std::optional<ICharInfo::Info> CharInfoFromQueryResult(PreparedStmtResultSet& stmt) {
if (!stmt->next()) { if (!stmt->next()) {
return std::nullopt; return std::nullopt;
} }
@@ -31,15 +31,13 @@ std::optional<ICharInfo::Info> CharInfoFromQueryResult(std::unique_ptr<sql::Resu
} }
std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const LWOOBJID charId) { std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const LWOOBJID charId) {
return CharInfoFromQueryResult( auto result = ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId);
ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId) return CharInfoFromQueryResult(result);
);
} }
std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const std::string_view name) { std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const std::string_view name) {
return CharInfoFromQueryResult( auto result = ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE name = ? LIMIT 1;", name);
ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE name = ? LIMIT 1;", name) return CharInfoFromQueryResult(result);
);
} }
std::vector<LWOOBJID> MySQLDatabase::GetAccountCharacterIds(const LWOOBJID accountId) { std::vector<LWOOBJID> MySQLDatabase::GetAccountCharacterIds(const LWOOBJID accountId) {

View File

@@ -14,7 +14,7 @@ std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityI
return donation_total->getUInt("donation_total"); return donation_total->getUInt("donation_total");
} }
std::vector<ILeaderboard::Entry> ProcessQuery(UniqueResultSet& rows) { std::vector<ILeaderboard::Entry> ProcessQuery(PreparedStmtResultSet& rows) {
std::vector<ILeaderboard::Entry> entries; std::vector<ILeaderboard::Entry> entries;
entries.reserve(rows->rowsCount()); entries.reserve(rows->rowsCount());

View File

@@ -1,7 +1,7 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
#include "ePropertySortType.h" #include "ePropertySortType.h"
IProperty::Info ReadPropertyInfo(UniqueResultSet& result) { IProperty::Info ReadPropertyInfo(PreparedStmtResultSet& result) {
IProperty::Info info; IProperty::Info info;
info.id = result->getUInt64("id"); info.id = result->getUInt64("id");
info.ownerId = result->getInt64("owner_id"); info.ownerId = result->getInt64("owner_id");
@@ -21,7 +21,7 @@ IProperty::Info ReadPropertyInfo(UniqueResultSet& result) {
std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) { std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
std::optional<IProperty::PropertyEntranceResult> result; std::optional<IProperty::PropertyEntranceResult> result;
std::string query; std::string query;
std::unique_ptr<sql::ResultSet> properties; PreparedStmtResultSet properties;
if (params.sortChoice == SORT_TYPE_FEATURED || params.sortChoice == SORT_TYPE_FRIENDS) { if (params.sortChoice == SORT_TYPE_FEATURED || params.sortChoice == SORT_TYPE_FRIENDS) {
query = R"QUERY( query = R"QUERY(

View File

@@ -1,6 +1,6 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
IUgc::Model ReadModel(UniqueResultSet& result) { IUgc::Model ReadModel(PreparedStmtResultSet& result) {
IUgc::Model model; IUgc::Model model;
// blob is owned by the query, so we need to do a deep copy :/ // blob is owned by the query, so we need to do a deep copy :/

View File

@@ -8,7 +8,9 @@
#include "ControlBehaviorMsgs.h" #include "ControlBehaviorMsgs.h"
#include "tinyxml2.h" #include "tinyxml2.h"
#include "InventoryComponent.h" #include "InventoryComponent.h"
#include "MissionComponent.h"
#include "SimplePhysicsComponent.h" #include "SimplePhysicsComponent.h"
#include "eMissionTaskType.h"
#include "eObjectBits.h" #include "eObjectBits.h"
#include "Database.h" #include "Database.h"
@@ -187,6 +189,8 @@ void ModelComponent::AddBehavior(AddMessage& msg) {
// Check if this behavior is able to be found via lot (if so, its a loot behavior). // Check if this behavior is able to be found via lot (if so, its a loot behavior).
insertedBehavior.SetIsLoot(inventoryComponent->FindItemByLot(msg.GetBehaviorId(), eInventoryType::BEHAVIORS)); insertedBehavior.SetIsLoot(inventoryComponent->FindItemByLot(msg.GetBehaviorId(), eInventoryType::BEHAVIORS));
} }
auto* const missionComponent = playerEntity->GetComponent<MissionComponent>();
if (missionComponent) missionComponent->Progress(eMissionTaskType::ADD_BEHAVIOR, 0);
} }
auto* const simplePhysComponent = m_Parent->GetComponent<SimplePhysicsComponent>(); auto* const simplePhysComponent = m_Parent->GetComponent<SimplePhysicsComponent>();

View File

@@ -446,6 +446,8 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
break; break;
} }
case eMissionTaskType::PLACE_MODEL: case eMissionTaskType::PLACE_MODEL:
[[fallthrough]];
case eMissionTaskType::ADD_BEHAVIOR:
{ {
AddProgress(count); AddProgress(count);
break; break;