mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-23 21:22:49 +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:
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
|
||||
Reference in New Issue
Block a user