Files
DarkflameServer/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp
Aaron Kimbrell b579344482 feat: keep every brick by brick save before it is split into models
The save is stored as the client sent it in the new ugc_unsplit table, and each model split from it points back to it (ugc.unsplit_id), so the whole build can be looked at or restored later. A save is kept while any model split from it exists: deleting models removes saves no model points to any more, and deleting a character removes its saves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 20:47:43 -05:00

94 lines
3.2 KiB
C++

#include "SQLiteDatabase.h"
IUgc::Model ReadModel(CppSQLite3Query& result) {
IUgc::Model model;
int blobSize{};
const auto* blob = result.getBlobField("lxfml", blobSize);
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
model.id = result.getInt64Field("ugcID");
model.modelID = result.getInt64Field("modelID");
return model;
}
std::vector<IUgc::Model> 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<IUgc::Model> toReturn;
while (!result.eof()) {
toReturn.push_back(ReadModel(result));
result.nextRow();
}
return toReturn;
}
std::vector<IUgc::Model> 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.id = u.id;");
std::vector<IUgc::Model> 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);");
// An unsplit save is kept while any model split from it is
ExecuteDelete("DELETE FROM ugc_unsplit WHERE id NOT IN (SELECT unsplit_id FROM ugc WHERE unsplit_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::optional<LWOOBJID> unsplitId) {
const std::istream stream(sd0Data.rdbuf());
ExecuteInsert(
"INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`, `unsplit_id`) VALUES (?,?,?,?,?,?,?,?)",
blueprintId,
accountId,
characterId,
0,
&stream,
false,
"weedeater.lxfml",
unsplitId
);
}
void SQLiteDatabase::InsertUnsplitUgcModel(std::stringstream& sd0Data, const LWOOBJID id, const uint32_t accountId, const LWOOBJID characterId) {
const std::istream stream(sd0Data.rdbuf());
ExecuteInsert("INSERT INTO `ugc_unsplit`(`id`, `account_id`, `character_id`, `lxfml`) VALUES (?,?,?,?)", id, accountId, characterId, &stream);
}
void SQLiteDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM ugc WHERE id = ?;", modelId);
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
ExecuteDelete("DELETE FROM ugc_unsplit WHERE id NOT IN (SELECT unsplit_id FROM ugc WHERE unsplit_id IS NOT NULL);");
}
void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
const std::istream stream(lxfml.rdbuf());
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
}
std::optional<IUgc::Model> SQLiteDatabase::GetUgcModel(const LWOOBJID ugcId) {
auto [_, result] = ExecuteSelect("SELECT u.id AS ugcID, pc.id AS modelID, lxfml FROM ugc AS u JOIN properties_contents AS pc ON pc.id = u.id WHERE u.id = ?;", ugcId);
std::optional<IUgc::Model> toReturn = std::nullopt;
if (!result.eof()) {
toReturn = ReadModel(result);
}
return toReturn;
}