mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat(database): look up players' creations and where they are
IUgcLookup: search ugc and ugc_modular_build by id, creator, property, model name or LOT, and find where a creation is placed or mailed. The SQL is shared by MySQL and SQLite (UgcLookupSql.h). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,7 @@
|
||||
#include "IBbbAutosave.h"
|
||||
#include "IServerTraffic.h"
|
||||
#include "IApiKeys.h"
|
||||
#include "IUgcLookup.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (std::exception& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0)
|
||||
@@ -66,7 +67,7 @@ class GameDatabase :
|
||||
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
||||
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
|
||||
public IBehaviors, public IUgcModularBuild, public IAuditLog, public IDashboardStats, public IAccountEmails, public IDashboardMaintenance, public IEconomyLedger, public IDashboardAdmin, public IServerConfig, public IScheduledTasks, public ICharacterSnapshots, public IAccountNotes, public IServerHealth, public IRelatedData, public IChatLog, public IAccountStrikes, public ISlashCommands, public IModeration, public IServerOperations, public IPlayerPositions, public IAiSuggestions, public ILiveOps, public IFeaturedProperties, public IMessageCaptures, public IContraband, public IPropertyRent, public IPropertyReputation, public IBbbAutosave, public IServerTraffic, public IApiKeys {
|
||||
public IBehaviors, public IUgcModularBuild, public IAuditLog, public IDashboardStats, public IAccountEmails, public IDashboardMaintenance, public IEconomyLedger, public IDashboardAdmin, public IServerConfig, public IScheduledTasks, public ICharacterSnapshots, public IAccountNotes, public IServerHealth, public IRelatedData, public IChatLog, public IAccountStrikes, public ISlashCommands, public IModeration, public IServerOperations, public IPlayerPositions, public IAiSuggestions, public ILiveOps, public IFeaturedProperties, public IMessageCaptures, public IContraband, public IPropertyRent, public IPropertyReputation, public IBbbAutosave, public IServerTraffic, public IApiKeys, public IUgcLookup {
|
||||
public:
|
||||
virtual ~GameDatabase() = default;
|
||||
// TODO: These should be made private.
|
||||
|
||||
86
dDatabase/GameDatabase/ITables/IUgcLookup.h
Normal file
86
dDatabase/GameDatabase/ITables/IUgcLookup.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef IUGCLOOKUP_H
|
||||
#define IUGCLOOKUP_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "IUgc.h"
|
||||
|
||||
/**
|
||||
* Finding players' creations for the dashboard: brick built models (ugc) and cars and rockets (ugc_modular_build),
|
||||
* who made them, what the UGC server made of them and where they are (placed on a property, attached to a mail).
|
||||
*/
|
||||
class IUgcLookup {
|
||||
public:
|
||||
enum class eUgcKind : uint8_t {
|
||||
MODEL = 0, // ugc: a brick built model, its id is the blueprint id
|
||||
MODULAR = 1, // ugc_modular_build: a car or rocket, its id is the item's subkey
|
||||
};
|
||||
|
||||
struct UgcEntry {
|
||||
eUgcKind kind{};
|
||||
LWOOBJID id{};
|
||||
LWOOBJID characterId{}; // the creator
|
||||
std::string characterName; // empty when the character is gone
|
||||
uint32_t accountId{};
|
||||
std::string accountName;
|
||||
IUgc::eProcessState state{};
|
||||
std::string error; // why the UGC server failed, if it did
|
||||
std::string detail; // models: the file name it was uploaded as; modular builds: the modules
|
||||
};
|
||||
|
||||
// What SearchUgc matches. A number (when set) is matched against ids; text against names
|
||||
struct UgcSearch {
|
||||
enum class eField : uint8_t {
|
||||
ANY, // any of the below
|
||||
ID, // the ugc / blueprint id, the placed model's object id, the property, creator character or account id
|
||||
OWNER, // the creator's character or account name (or id)
|
||||
PROPERTY, // the name (or id) of a property it is placed on
|
||||
MODEL, // the name or description given to it on a property, or its upload file name
|
||||
LOT, // the LOT it is placed as, or one of a modular build's modules
|
||||
};
|
||||
eField field = eField::ANY;
|
||||
std::string text; // matched as a part of names; empty for none
|
||||
std::optional<int64_t> number; // matched exactly against ids
|
||||
};
|
||||
|
||||
// Up to `limit` of each kind, the newest first
|
||||
virtual std::vector<UgcEntry> SearchUgc(const UgcSearch& search, const uint32_t limit) = 0;
|
||||
|
||||
// The entries (of either kind) with these ids
|
||||
virtual std::vector<UgcEntry> GetUgcEntries(const std::vector<LWOOBJID>& ids) = 0;
|
||||
|
||||
// A creation placed on a property (properties_contents)
|
||||
struct UgcPlacement {
|
||||
LWOOBJID ugcId{};
|
||||
LWOOBJID modelId{}; // the placed object's id
|
||||
LOT lot{};
|
||||
LWOOBJID propertyId{};
|
||||
std::string propertyName;
|
||||
LWOOBJID ownerId{}; // the property owner's character
|
||||
std::string ownerName;
|
||||
uint32_t zoneId{};
|
||||
std::string modelName;
|
||||
std::string modelDescription;
|
||||
};
|
||||
|
||||
virtual std::vector<UgcPlacement> GetUgcPlacements(const std::vector<LWOOBJID>& ugcIds) = 0;
|
||||
|
||||
// A mail with an attachment that may be a creation: its subkey is one of `subkeys` (modular builds), or it is a
|
||||
// brick built model item (`modelItemLot`, whose blueprint is in the attachment's config)
|
||||
struct UgcMail {
|
||||
uint64_t id{};
|
||||
LWOOBJID receiverId{};
|
||||
std::string receiverName;
|
||||
LOT lot{};
|
||||
LWOOBJID subkey{};
|
||||
std::string config;
|
||||
};
|
||||
|
||||
virtual std::vector<UgcMail> GetUgcMail(const std::vector<LWOOBJID>& subkeys, const LOT modelItemLot) = 0;
|
||||
};
|
||||
|
||||
#endif //!IUGCLOOKUP_H
|
||||
80
dDatabase/GameDatabase/ITables/UgcLookupSql.h
Normal file
80
dDatabase/GameDatabase/ITables/UgcLookupSql.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef UGCLOOKUPSQL_H
|
||||
#define UGCLOOKUPSQL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "IUgcLookup.h"
|
||||
|
||||
/**
|
||||
* The SQL IUgcLookup runs, the same on MySQL and SQLite. Ids are numbers, so they are written into the query; the
|
||||
* search text is bound (TEXT_BINDS times, always, so the statement's parameters don't change with the search).
|
||||
*/
|
||||
namespace UgcLookupSql {
|
||||
constexpr int TEXT_BINDS = 6;
|
||||
|
||||
inline std::string IdList(const std::vector<LWOOBJID>& ids) {
|
||||
std::string list;
|
||||
for (const auto id : ids) list += (list.empty() ? "" : ",") + std::to_string(id);
|
||||
return list;
|
||||
}
|
||||
|
||||
// The columns every entry query selects, for models (ugc AS u) or modular builds (ugc_modular_build AS b)
|
||||
inline std::string Select(bool modular) {
|
||||
return modular
|
||||
? "SELECT b.ugc_id AS id, b.character_id, c.name AS character_name, COALESCE(c.account_id, 0) AS account_id, a.name AS account_name, "
|
||||
"b.is_optimized, b.process_error, b.ldf_config AS detail FROM ugc_modular_build AS b "
|
||||
"LEFT JOIN charinfo AS c ON c.id = b.character_id LEFT JOIN accounts AS a ON a.id = c.account_id "
|
||||
: "SELECT u.id, u.character_id, c.name AS character_name, u.account_id, a.name AS account_name, "
|
||||
"u.is_optimized, u.process_error, u.filename AS detail FROM ugc AS u "
|
||||
"LEFT JOIN charinfo AS c ON c.id = u.character_id LEFT JOIN accounts AS a ON a.id = u.account_id ";
|
||||
}
|
||||
|
||||
// WHERE for a search; binds the search's %text% TEXT_BINDS times
|
||||
inline std::string Where(const IUgcLookup::UgcSearch& search, bool modular) {
|
||||
using eField = IUgcLookup::UgcSearch::eField;
|
||||
const auto field = search.field;
|
||||
const bool any = field == eField::ANY;
|
||||
const bool num = search.number.has_value();
|
||||
const bool txt = !search.text.empty();
|
||||
const std::string n = num ? std::to_string(*search.number) : "NULL";
|
||||
const std::string id = modular ? "b.ugc_id" : "u.id";
|
||||
const std::string character = modular ? "b.character_id" : "u.character_id";
|
||||
const std::string account = modular ? "c.account_id" : "u.account_id";
|
||||
const std::string detail = modular ? "b.ldf_config" : "u.filename";
|
||||
const auto on = [](bool value) { return std::string(value ? "1=1" : "0=1"); };
|
||||
const std::string placed = "SELECT 1 FROM properties_contents AS pc WHERE pc.ugc_id = " + id;
|
||||
|
||||
std::string where = "WHERE (0=1";
|
||||
if (num && (any || field == eField::ID)) {
|
||||
where += " OR " + id + " = " + n + " OR " + character + " = " + n + " OR " + account + " = " + n +
|
||||
" OR EXISTS (" + placed + " AND (pc.id = " + n + " OR pc.property_id = " + n + "))";
|
||||
}
|
||||
if (num && field == eField::OWNER) where += " OR " + character + " = " + n + " OR " + account + " = " + n;
|
||||
if (num && (any || field == eField::LOT)) {
|
||||
if (modular) where += " OR b.ldf_config LIKE '%:" + n + "+%' OR b.ldf_config LIKE '%:" + n + "'";
|
||||
else where += " OR EXISTS (" + placed + " AND pc.lot = " + n + ")";
|
||||
}
|
||||
// The bound text: owner (2), property (1), model (3)
|
||||
where += " OR (" + on(txt && (any || field == eField::OWNER)) + " AND (c.name LIKE ? OR a.name LIKE ?))";
|
||||
where += " OR (" + on((txt && any) || field == eField::PROPERTY) + " AND EXISTS (SELECT 1 FROM properties_contents AS pc "
|
||||
"JOIN properties AS p ON p.id = pc.property_id WHERE pc.ugc_id = " + id + " AND ((" + on(txt) + " AND p.name LIKE ?) OR p.id = " + n + ")))";
|
||||
where += " OR (" + on(txt && (any || field == eField::MODEL)) + " AND (" + detail + " LIKE ? OR EXISTS (" + placed +
|
||||
" AND (pc.model_name LIKE ? OR pc.model_description LIKE ?))))";
|
||||
return where + ") ";
|
||||
}
|
||||
|
||||
inline std::string Placements(const std::vector<LWOOBJID>& ugcIds) {
|
||||
return "SELECT pc.ugc_id, pc.id, pc.lot, pc.property_id, p.name AS property_name, p.owner_id, c.name AS owner_name, p.zone_id, "
|
||||
"pc.model_name, pc.model_description FROM properties_contents AS pc JOIN properties AS p ON p.id = pc.property_id "
|
||||
"LEFT JOIN charinfo AS c ON c.id = p.owner_id WHERE pc.ugc_id IN (" + IdList(ugcIds) + ") ORDER BY pc.id;";
|
||||
}
|
||||
|
||||
inline std::string Mail(const std::vector<LWOOBJID>& subkeys, LOT modelItemLot) {
|
||||
return "SELECT id, receiver_id, receiver_name, attachment_lot, attachment_subkey, attachment_config FROM mail WHERE attachment_count > 0 AND "
|
||||
"(attachment_lot = " + std::to_string(modelItemLot) + (subkeys.empty() ? "" : " OR attachment_subkey IN (" + IdList(subkeys) + ")") +
|
||||
") ORDER BY id DESC LIMIT 500;";
|
||||
}
|
||||
}
|
||||
|
||||
#endif //!UGCLOOKUPSQL_H
|
||||
@@ -220,6 +220,12 @@ public:
|
||||
std::vector<TrafficMinute> GetTrafficMinutes(int64_t from, int64_t to, int64_t bucketSeconds) override;
|
||||
uint32_t PruneTrafficMinutes(int64_t beforeTime) override;
|
||||
|
||||
// IUgcLookup
|
||||
std::vector<UgcEntry> SearchUgc(const UgcSearch& search, const uint32_t limit) override;
|
||||
std::vector<UgcEntry> GetUgcEntries(const std::vector<LWOOBJID>& ids) override;
|
||||
std::vector<UgcPlacement> GetUgcPlacements(const std::vector<LWOOBJID>& ugcIds) override;
|
||||
std::vector<UgcMail> GetUgcMail(const std::vector<LWOOBJID>& subkeys, const LOT modelItemLot) override;
|
||||
|
||||
// IApiKeys
|
||||
uint64_t InsertApiKey(const ApiKey& key) override;
|
||||
std::optional<ApiKey> GetApiKey(uint64_t id) override;
|
||||
|
||||
@@ -47,6 +47,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Servers.cpp"
|
||||
"Ugc.cpp"
|
||||
"UgcModularBuild.cpp"
|
||||
"UgcLookup.cpp"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
77
dDatabase/GameDatabase/MySQL/Tables/UgcLookup.cpp
Normal file
77
dDatabase/GameDatabase/MySQL/Tables/UgcLookup.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
#include "UgcLookupSql.h"
|
||||
|
||||
namespace {
|
||||
IUgcLookup::UgcEntry ReadEntry(PreparedStmtResultSet& result, IUgcLookup::eUgcKind kind) {
|
||||
IUgcLookup::UgcEntry entry;
|
||||
entry.kind = kind;
|
||||
entry.id = result->getInt64("id");
|
||||
entry.characterId = result->getInt64("character_id");
|
||||
entry.characterName = std::string(result->getString("character_name").c_str());
|
||||
entry.accountId = static_cast<uint32_t>(result->getInt64("account_id"));
|
||||
entry.accountName = std::string(result->getString("account_name").c_str());
|
||||
entry.state = static_cast<IUgc::eProcessState>(result->getInt("is_optimized"));
|
||||
entry.error = std::string(result->getString("process_error").c_str());
|
||||
entry.detail = std::string(result->getString("detail").c_str());
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcEntry> MySQLDatabase::SearchUgc(const UgcSearch& search, const uint32_t limit) {
|
||||
std::vector<UgcEntry> entries;
|
||||
const std::string pattern = "%" + search.text + "%";
|
||||
for (const bool modular : { false, true }) {
|
||||
const auto order = modular ? "ORDER BY b.ugc_id DESC LIMIT ?;" : "ORDER BY u.id DESC LIMIT ?;";
|
||||
auto result = ExecuteSelect(UgcLookupSql::Select(modular) + UgcLookupSql::Where(search, modular) + order,
|
||||
pattern, pattern, pattern, pattern, pattern, pattern, limit);
|
||||
while (result->next()) entries.push_back(ReadEntry(result, modular ? eUgcKind::MODULAR : eUgcKind::MODEL));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcEntry> MySQLDatabase::GetUgcEntries(const std::vector<LWOOBJID>& ids) {
|
||||
std::vector<UgcEntry> entries;
|
||||
if (ids.empty()) return entries;
|
||||
const auto list = UgcLookupSql::IdList(ids);
|
||||
for (const bool modular : { false, true }) {
|
||||
auto result = ExecuteSelect(UgcLookupSql::Select(modular) + (modular ? "WHERE b.ugc_id IN (" : "WHERE u.id IN (") + list + ");");
|
||||
while (result->next()) entries.push_back(ReadEntry(result, modular ? eUgcKind::MODULAR : eUgcKind::MODEL));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcPlacement> MySQLDatabase::GetUgcPlacements(const std::vector<LWOOBJID>& ugcIds) {
|
||||
std::vector<UgcPlacement> placements;
|
||||
if (ugcIds.empty()) return placements;
|
||||
auto result = ExecuteSelect(UgcLookupSql::Placements(ugcIds));
|
||||
while (result->next()) {
|
||||
auto& placement = placements.emplace_back();
|
||||
placement.ugcId = result->getInt64("ugc_id");
|
||||
placement.modelId = result->getInt64("id");
|
||||
placement.lot = result->getInt("lot");
|
||||
placement.propertyId = result->getInt64("property_id");
|
||||
placement.propertyName = std::string(result->getString("property_name").c_str());
|
||||
placement.ownerId = result->getInt64("owner_id");
|
||||
placement.ownerName = std::string(result->getString("owner_name").c_str());
|
||||
placement.zoneId = static_cast<uint32_t>(result->getInt("zone_id"));
|
||||
placement.modelName = std::string(result->getString("model_name").c_str());
|
||||
placement.modelDescription = std::string(result->getString("model_description").c_str());
|
||||
}
|
||||
return placements;
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcMail> MySQLDatabase::GetUgcMail(const std::vector<LWOOBJID>& subkeys, const LOT modelItemLot) {
|
||||
std::vector<UgcMail> mail;
|
||||
auto result = ExecuteSelect(UgcLookupSql::Mail(subkeys, modelItemLot));
|
||||
while (result->next()) {
|
||||
auto& entry = mail.emplace_back();
|
||||
entry.id = static_cast<uint64_t>(result->getInt64("id"));
|
||||
entry.receiverId = result->getInt64("receiver_id");
|
||||
entry.receiverName = std::string(result->getString("receiver_name").c_str());
|
||||
entry.lot = result->getInt("attachment_lot");
|
||||
entry.subkey = result->getInt64("attachment_subkey");
|
||||
entry.config = std::string(result->getString("attachment_config").c_str());
|
||||
}
|
||||
return mail;
|
||||
}
|
||||
@@ -204,6 +204,12 @@ public:
|
||||
std::vector<TrafficMinute> GetTrafficMinutes(int64_t from, int64_t to, int64_t bucketSeconds) override;
|
||||
uint32_t PruneTrafficMinutes(int64_t beforeTime) override;
|
||||
|
||||
// IUgcLookup
|
||||
std::vector<UgcEntry> SearchUgc(const UgcSearch& search, const uint32_t limit) override;
|
||||
std::vector<UgcEntry> GetUgcEntries(const std::vector<LWOOBJID>& ids) override;
|
||||
std::vector<UgcPlacement> GetUgcPlacements(const std::vector<LWOOBJID>& ugcIds) override;
|
||||
std::vector<UgcMail> GetUgcMail(const std::vector<LWOOBJID>& subkeys, const LOT modelItemLot) override;
|
||||
|
||||
// IApiKeys
|
||||
uint64_t InsertApiKey(const ApiKey& key) override;
|
||||
std::optional<ApiKey> GetApiKey(uint64_t id) override;
|
||||
|
||||
@@ -47,6 +47,7 @@ set(DDATABASES_DATABASES_SQLITE_TABLES_SOURCES
|
||||
"Servers.cpp"
|
||||
"Ugc.cpp"
|
||||
"UgcModularBuild.cpp"
|
||||
"UgcLookup.cpp"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
77
dDatabase/GameDatabase/SQLite/Tables/UgcLookup.cpp
Normal file
77
dDatabase/GameDatabase/SQLite/Tables/UgcLookup.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
#include "UgcLookupSql.h"
|
||||
|
||||
namespace {
|
||||
IUgcLookup::UgcEntry ReadEntry(CppSQLite3Query& result, IUgcLookup::eUgcKind kind) {
|
||||
IUgcLookup::UgcEntry entry;
|
||||
entry.kind = kind;
|
||||
entry.id = result.getInt64Field("id");
|
||||
entry.characterId = result.getInt64Field("character_id");
|
||||
entry.characterName = result.getStringField("character_name", "");
|
||||
entry.accountId = static_cast<uint32_t>(result.getInt64Field("account_id"));
|
||||
entry.accountName = result.getStringField("account_name", "");
|
||||
entry.state = static_cast<IUgc::eProcessState>(result.getIntField("is_optimized"));
|
||||
entry.error = result.getStringField("process_error", "");
|
||||
entry.detail = result.getStringField("detail", "");
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcEntry> SQLiteDatabase::SearchUgc(const UgcSearch& search, const uint32_t limit) {
|
||||
std::vector<UgcEntry> entries;
|
||||
const std::string pattern = "%" + search.text + "%";
|
||||
for (const bool modular : { false, true }) {
|
||||
const auto order = modular ? "ORDER BY b.ugc_id DESC LIMIT ?;" : "ORDER BY u.id DESC LIMIT ?;";
|
||||
auto [_, result] = ExecuteSelect(UgcLookupSql::Select(modular) + UgcLookupSql::Where(search, modular) + order,
|
||||
pattern, pattern, pattern, pattern, pattern, pattern, limit);
|
||||
for (; !result.eof(); result.nextRow()) entries.push_back(ReadEntry(result, modular ? eUgcKind::MODULAR : eUgcKind::MODEL));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcEntry> SQLiteDatabase::GetUgcEntries(const std::vector<LWOOBJID>& ids) {
|
||||
std::vector<UgcEntry> entries;
|
||||
if (ids.empty()) return entries;
|
||||
const auto list = UgcLookupSql::IdList(ids);
|
||||
for (const bool modular : { false, true }) {
|
||||
auto [_, result] = ExecuteSelect(UgcLookupSql::Select(modular) + (modular ? "WHERE b.ugc_id IN (" : "WHERE u.id IN (") + list + ");");
|
||||
for (; !result.eof(); result.nextRow()) entries.push_back(ReadEntry(result, modular ? eUgcKind::MODULAR : eUgcKind::MODEL));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcPlacement> SQLiteDatabase::GetUgcPlacements(const std::vector<LWOOBJID>& ugcIds) {
|
||||
std::vector<UgcPlacement> placements;
|
||||
if (ugcIds.empty()) return placements;
|
||||
auto [_, result] = ExecuteSelect(UgcLookupSql::Placements(ugcIds));
|
||||
for (; !result.eof(); result.nextRow()) {
|
||||
auto& placement = placements.emplace_back();
|
||||
placement.ugcId = result.getInt64Field("ugc_id");
|
||||
placement.modelId = result.getInt64Field("id");
|
||||
placement.lot = result.getIntField("lot");
|
||||
placement.propertyId = result.getInt64Field("property_id");
|
||||
placement.propertyName = result.getStringField("property_name", "");
|
||||
placement.ownerId = result.getInt64Field("owner_id");
|
||||
placement.ownerName = result.getStringField("owner_name", "");
|
||||
placement.zoneId = static_cast<uint32_t>(result.getIntField("zone_id"));
|
||||
placement.modelName = result.getStringField("model_name", "");
|
||||
placement.modelDescription = result.getStringField("model_description", "");
|
||||
}
|
||||
return placements;
|
||||
}
|
||||
|
||||
std::vector<IUgcLookup::UgcMail> SQLiteDatabase::GetUgcMail(const std::vector<LWOOBJID>& subkeys, const LOT modelItemLot) {
|
||||
std::vector<UgcMail> mail;
|
||||
auto [_, result] = ExecuteSelect(UgcLookupSql::Mail(subkeys, modelItemLot));
|
||||
for (; !result.eof(); result.nextRow()) {
|
||||
auto& entry = mail.emplace_back();
|
||||
entry.id = static_cast<uint64_t>(result.getInt64Field("id"));
|
||||
entry.receiverId = result.getInt64Field("receiver_id");
|
||||
entry.receiverName = result.getStringField("receiver_name", "");
|
||||
entry.lot = result.getIntField("attachment_lot");
|
||||
entry.subkey = result.getInt64Field("attachment_subkey");
|
||||
entry.config = result.getStringField("attachment_config", "");
|
||||
}
|
||||
return mail;
|
||||
}
|
||||
@@ -267,6 +267,12 @@ class TestSQLDatabase : public GameDatabase {
|
||||
void DeleteBbbAutosave(const LWOOBJID characterId) override { m_BbbAutosaves.erase(characterId); }
|
||||
std::map<LWOOBJID, IBbbAutosave::Info> m_BbbAutosaves;
|
||||
|
||||
// IUgcLookup
|
||||
std::vector<UgcEntry> SearchUgc(const UgcSearch& search, const uint32_t limit) override { return {}; }
|
||||
std::vector<UgcEntry> GetUgcEntries(const std::vector<LWOOBJID>& ids) override { return {}; }
|
||||
std::vector<UgcPlacement> GetUgcPlacements(const std::vector<LWOOBJID>& ugcIds) override { return {}; }
|
||||
std::vector<UgcMail> GetUgcMail(const std::vector<LWOOBJID>& subkeys, const LOT modelItemLot) override { return {}; }
|
||||
|
||||
// IPropertyReputation
|
||||
VisitorHistory GetPropertyVisitorHistory(LWOOBJID propertyId, uint32_t accountId, uint32_t day, uint32_t days) override { return {}; }
|
||||
int64_t GetPropertyReputationOnDay(LWOOBJID propertyId, uint32_t day) override { return 0; }
|
||||
|
||||
Reference in New Issue
Block a user