mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-06 03:47:13 +00:00
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>
This commit is contained in:
@@ -20,12 +20,16 @@ public:
|
||||
std::array<LWOOBJID, 5> behaviors{};
|
||||
};
|
||||
|
||||
// Inserts a new UGC model into the database.
|
||||
// Inserts a new UGC model into the database. unsplitId is the save it was split from (InsertUnsplitUgcModel), if any.
|
||||
virtual void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
const uint64_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const LWOOBJID characterId) = 0;
|
||||
const LWOOBJID characterId,
|
||||
const std::optional<LWOOBJID> unsplitId) = 0;
|
||||
|
||||
// Keeps a brick by brick save as the client sent it, before it is split into models.
|
||||
virtual void InsertUnsplitUgcModel(std::stringstream& sd0Data, const LWOOBJID id, const uint32_t accountId, const LWOOBJID characterId) = 0;
|
||||
|
||||
// Get the property models for the given property id.
|
||||
virtual std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) = 0;
|
||||
|
||||
@@ -108,6 +108,7 @@ void MySQLDatabase::DeleteCharacter(const LWOOBJID characterId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);", characterId);
|
||||
ExecuteDelete("DELETE FROM properties WHERE owner_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM ugc WHERE character_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM ugc_unsplit WHERE character_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM activity_log WHERE character_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM mail WHERE receiver_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM charinfo WHERE id=? LIMIT 1;", characterId);
|
||||
|
||||
@@ -99,7 +99,9 @@ public:
|
||||
std::stringstream& sd0Data,
|
||||
const uint64_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const LWOOBJID characterId) override;
|
||||
const LWOOBJID characterId,
|
||||
const std::optional<LWOOBJID> unsplitId) override;
|
||||
void InsertUnsplitUgcModel(std::stringstream& sd0Data, const LWOOBJID id, const uint32_t accountId, const LWOOBJID characterId) override;
|
||||
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||
uint32_t GetUnreadMailCount(const LWOOBJID characterId) override;
|
||||
|
||||
@@ -40,29 +40,39 @@ std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
|
||||
|
||||
void MySQLDatabase::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 MySQLDatabase::InsertNewUgcModel(
|
||||
std::stringstream& sd0Data, // cant be const sad
|
||||
const uint64_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const LWOOBJID characterId) {
|
||||
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`) VALUES (?,?,?,?,?,?,?)",
|
||||
"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"
|
||||
"weedeater.lxfml",
|
||||
unsplitId
|
||||
);
|
||||
}
|
||||
|
||||
void MySQLDatabase::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 MySQLDatabase::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 MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
|
||||
|
||||
@@ -75,6 +75,7 @@ void SQLiteDatabase::DeleteCharacter(const LWOOBJID characterId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);", characterId);
|
||||
ExecuteDelete("DELETE FROM properties WHERE owner_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM ugc WHERE character_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM ugc_unsplit WHERE character_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM activity_log WHERE character_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM mail WHERE receiver_id=?;", characterId);
|
||||
ExecuteDelete("DELETE FROM charinfo WHERE id=?;", characterId);
|
||||
|
||||
@@ -83,7 +83,9 @@ public:
|
||||
std::stringstream& sd0Data,
|
||||
const uint64_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const LWOOBJID characterId) override;
|
||||
const LWOOBJID characterId,
|
||||
const std::optional<LWOOBJID> unsplitId) override;
|
||||
void InsertUnsplitUgcModel(std::stringstream& sd0Data, const LWOOBJID id, const uint32_t accountId, const LWOOBJID characterId) override;
|
||||
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||
uint32_t GetUnreadMailCount(const LWOOBJID characterId) override;
|
||||
|
||||
@@ -41,29 +41,39 @@ std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() {
|
||||
|
||||
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 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`) VALUES (?,?,?,?,?,?,?)",
|
||||
"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"
|
||||
"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) {
|
||||
|
||||
@@ -192,7 +192,11 @@ void TestSQLDatabase::InsertNewMail(const MailInfo& mail) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint64_t blueprintId, const uint32_t accountId, const LWOOBJID characterId) {
|
||||
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint64_t blueprintId, const uint32_t accountId, const LWOOBJID characterId, const std::optional<LWOOBJID> unsplitId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertUnsplitUgcModel(std::stringstream& sd0Data, const LWOOBJID id, const uint32_t accountId, const LWOOBJID characterId) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,9 @@ class TestSQLDatabase : public GameDatabase {
|
||||
std::stringstream& sd0Data,
|
||||
const uint64_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const LWOOBJID characterId) override;
|
||||
const LWOOBJID characterId,
|
||||
const std::optional<LWOOBJID> unsplitId) override;
|
||||
void InsertUnsplitUgcModel(std::stringstream& sd0Data, const LWOOBJID id, const uint32_t accountId, const LWOOBJID characterId) override;
|
||||
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<MailInfo> GetMail(const uint64_t mailId) override;
|
||||
uint32_t GetUnreadMailCount(const LWOOBJID characterId) override;
|
||||
|
||||
@@ -2530,6 +2530,12 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
|
||||
outFile.close();
|
||||
}
|
||||
|
||||
// Keep the save as the client sent it before splitting it; every model made from it points back to it
|
||||
const auto accountId = entity->GetCharacter()->GetParentUser()->GetAccountID();
|
||||
const LWOOBJID unsplitId = ObjectIDManager::GetPersistentID();
|
||||
std::stringstream unsplitData(str);
|
||||
Database::Get()->InsertUnsplitUgcModel(unsplitData, unsplitId, accountId, entity->GetCharacter()->GetID());
|
||||
|
||||
auto splitLxfmls = Lxfml::Split(asStr);
|
||||
LOG_DEBUG("Split into %zu models", splitLxfmls.size());
|
||||
|
||||
@@ -2567,7 +2573,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
|
||||
// Save each model to the database
|
||||
sd0.FromData(reinterpret_cast<const uint8_t*>(splitLxfmls[i].lxfml.data()), splitLxfmls[i].lxfml.size());
|
||||
auto sd0AsStream = sd0.GetAsStream();
|
||||
Database::Get()->InsertNewUgcModel(sd0AsStream, blueprintID, entity->GetCharacter()->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID());
|
||||
Database::Get()->InsertNewUgcModel(sd0AsStream, blueprintID, accountId, entity->GetCharacter()->GetID(), unsplitId);
|
||||
|
||||
// Insert the new property model
|
||||
IPropertyContents::Model model;
|
||||
|
||||
11
migrations/dlu/mysql/27_ugc_unsplit.sql
Normal file
11
migrations/dlu/mysql/27_ugc_unsplit.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Every brick by brick save as the client sent it (sd0), before the server splits it into one model per group of
|
||||
-- connected bricks; each model made from it points back to it
|
||||
CREATE TABLE IF NOT EXISTS ugc_unsplit (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
account_id INT NOT NULL REFERENCES accounts(id),
|
||||
character_id BIGINT NOT NULL REFERENCES charinfo(id),
|
||||
lxfml MEDIUMBLOB NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE ugc ADD COLUMN unsplit_id BIGINT NULL DEFAULT NULL;
|
||||
11
migrations/dlu/sqlite/10_ugc_unsplit.sql
Normal file
11
migrations/dlu/sqlite/10_ugc_unsplit.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Every brick by brick save as the client sent it (sd0), before the server splits it into one model per group of
|
||||
-- connected bricks; each model made from it points back to it
|
||||
CREATE TABLE IF NOT EXISTS ugc_unsplit (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
account_id INTEGER NOT NULL REFERENCES accounts(id),
|
||||
character_id BIGINT NOT NULL REFERENCES charinfo(id),
|
||||
lxfml BLOB NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE ugc ADD COLUMN unsplit_id BIGINT NULL DEFAULT NULL;
|
||||
Reference in New Issue
Block a user