#include "SQLiteDatabase.h" #include namespace { IUgc::ProcessInfo ReadUgcProcessInfo(CppSQLite3Query& result, bool modular) { IUgc::ProcessInfo info; info.id = result.getInt64Field("id"); info.characterId = result.getInt64Field("character_id"); info.characterName = result.getStringField("character_name", ""); info.state = static_cast(result.getIntField("is_optimized")); info.attempts = static_cast(result.getIntField("process_attempts")); info.processedAt = result.getInt64Field("processed_at"); info.error = result.getStringField("process_error", ""); if (modular) info.details = result.getStringField("ldf_config", ""); else info.bakeAo = result.getIntField("bake_ao") != 0; return info; } int64_t UnixNow() { return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } } IUgc::Model ReadModel(CppSQLite3Query& result) { IUgc::Model model; int blobSize{}; const auto* blob = result.getBlobField("lxfml", blobSize); model.lxfmlData << std::string(reinterpret_cast(blob), blobSize); model.id = result.getInt64Field("ugcID"); model.modelID = result.getInt64Field("modelID"); return model; } std::vector SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) { auto [_, result] = ExecuteSelect( "SELECT lxfml, u.id AS ugcID, pc.id AS modelID FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;", propertyId); std::vector toReturn; while (!result.eof()) { toReturn.push_back(ReadModel(result)); result.nextRow(); } return toReturn; } std::vector SQLiteDatabase::GetAllUgcModels() { auto [_, result] = ExecuteSelect("SELECT u.id AS ugcID, pc.id AS modelID, lxfml FROM ugc AS u JOIN properties_contents AS pc ON pc.ugc_id = u.id WHERE pc.lot = 14 AND pc.ugc_id IS NOT NULL;"); std::vector models; while (!result.eof()) { models.push_back(ReadModel(result)); result.nextRow(); } return models; } void SQLiteDatabase::RemoveUnreferencedUgcModels() { ExecuteDelete("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents WHERE ugc_id IS NOT NULL);"); } void SQLiteDatabase::InsertNewUgcModel( std::stringstream& sd0Data, // cant be const sad const uint64_t blueprintId, const uint32_t accountId, const LWOOBJID characterId) { const std::istream stream(sd0Data.rdbuf()); ExecuteInsert( "INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`) VALUES (?,?,?,?,?,?,?)", blueprintId, accountId, characterId, 0, &stream, false, "weedeater.lxfml" ); } void SQLiteDatabase::DeleteUgcModelData(const LWOOBJID& modelId) { ExecuteDelete("DELETE FROM ugc WHERE id = ?;", modelId); ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId); } void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) { const std::istream stream(lxfml.rdbuf()); // The UGC server makes the model's files again ExecuteUpdate("UPDATE ugc SET lxfml = ?, is_optimized = 0, process_attempts = 0, process_error = '' WHERE id = ?;", &stream, modelId); } std::optional SQLiteDatabase::GetUgcModel(const LWOOBJID ugcId) { // LEFT JOIN: a model that is in someone's inventory rather than placed on a property still exists auto [_, result] = ExecuteSelect("SELECT u.id AS ugcID, pc.id AS modelID, lxfml FROM ugc AS u LEFT JOIN properties_contents AS pc ON pc.ugc_id = u.id WHERE u.id = ? LIMIT 1;", ugcId); std::optional toReturn = std::nullopt; if (!result.eof()) { toReturn = ReadModel(result); } return toReturn; } std::vector SQLiteDatabase::GetUgcModelsToProcess(const uint32_t limit) { auto [_, result] = ExecuteSelect("SELECT id, lxfml, process_attempts FROM ugc WHERE is_optimized = 0 ORDER BY process_attempts ASC, id DESC LIMIT ?;", limit); std::vector models; while (!result.eof()) { auto& model = models.emplace_back(); model.id = result.getInt64Field("id"); int blobSize{}; const auto* blob = result.getBlobField("lxfml", blobSize); model.lxfml.assign(reinterpret_cast(blob), blobSize); model.attempts = static_cast(result.getIntField("process_attempts")); result.nextRow(); } return models; } void SQLiteDatabase::SetUgcModelProcessed(const LWOOBJID id, const eProcessState state, const uint32_t attempts, const std::string_view error, const bool bakeAo) { ExecuteUpdate("UPDATE ugc SET is_optimized = ?, process_attempts = ?, process_error = ?, bake_ao = ?, processed_at = ? WHERE id = ?;", static_cast(state), attempts, error, bakeAo, UnixNow(), id); } std::optional SQLiteDatabase::GetUgcProcessInfo(const LWOOBJID id) { auto [_, result] = ExecuteSelect( "SELECT u.id, u.character_id, c.name AS character_name, u.is_optimized, u.process_attempts, u.processed_at, u.process_error, u.bake_ao " "FROM ugc AS u LEFT JOIN charinfo AS c ON c.id = u.character_id WHERE u.id = ? LIMIT 1;", id); if (result.eof()) return std::nullopt; return ReadUgcProcessInfo(result, false); } uint64_t SQLiteDatabase::ResetUgcModelProcessing(const std::optional id, const bool failedOnly) { if (id) return ExecuteUpdate("UPDATE ugc SET is_optimized = 0, process_attempts = 0, process_error = '' WHERE id = ?;", *id); if (failedOnly) return ExecuteUpdate("UPDATE ugc SET is_optimized = 0, process_attempts = 0, process_error = '' WHERE is_optimized = 2;"); return ExecuteUpdate("UPDATE ugc SET is_optimized = 0, process_attempts = 0, process_error = '';"); } std::vector SQLiteDatabase::GetUgcProcessList(const std::optional state, const uint32_t offset, const uint32_t limit) { const std::string select = "SELECT u.id, u.character_id, c.name AS character_name, u.is_optimized, u.process_attempts, u.processed_at, u.process_error, u.bake_ao " "FROM ugc AS u LEFT JOIN charinfo AS c ON c.id = u.character_id "; std::vector list; auto read = [&list](CppSQLite3Query& result) { while (!result.eof()) { list.push_back(ReadUgcProcessInfo(result, false)); result.nextRow(); } }; if (state) { auto [_, result] = ExecuteSelect(select + "WHERE u.is_optimized = ? ORDER BY u.id DESC LIMIT ? OFFSET ?;", static_cast(*state), limit, offset); read(result); } else { auto [_, result] = ExecuteSelect(select + "ORDER BY u.id DESC LIMIT ? OFFSET ?;", limit, offset); read(result); } return list; } std::vector> SQLiteDatabase::GetUgcProcessCounts() { auto [_, result] = ExecuteSelect("SELECT is_optimized, COUNT(*) AS count FROM ugc GROUP BY is_optimized;"); std::vector> counts; while (!result.eof()) { counts.emplace_back(static_cast(result.getIntField("is_optimized")), static_cast(result.getInt64Field("count"))); result.nextRow(); } return counts; }