feat(db): bbb_autosave table for the client's BBB autosave

The client sends SetBBBAutosave (996) with the model being built every five
minutes, before an AFK kick and before quitting, and expects the server to
rebuild an unfinished model later (RebuildBBBAutosaveMsg). This keeps the
last one per character with the model items that were in the BBB inventory
at the time. MySQL 82 / SQLite 65; parity test included.

Refs #1632

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 04:08:19 -05:00
parent dd16c67177
commit e25a6df45b
13 changed files with 187 additions and 1 deletions

View File

@@ -0,0 +1,38 @@
#ifndef __BBBAUTOSAVEITEMS__H__
#define __BBBAUTOSAVEITEMS__H__
#include <cstdlib>
#include <string>
#include <string_view>
#include <vector>
#include "dCommonVars.h"
// bbb_autosave.source_items: the item ids as a comma separated list
namespace BbbAutosaveItems {
inline std::string Join(const std::vector<LWOOBJID>& items) {
std::string out;
for (const auto id : items) {
if (!out.empty()) out += ',';
out += std::to_string(id);
}
return out;
}
inline std::vector<LWOOBJID> Parse(const std::string_view text) {
std::vector<LWOOBJID> items;
size_t start = 0;
while (start < text.size()) {
auto end = text.find(',', start);
if (end == std::string_view::npos) end = text.size();
const std::string part(text.substr(start, end - start));
char* parsedEnd = nullptr;
const auto id = std::strtoll(part.c_str(), &parsedEnd, 10);
if (parsedEnd != part.c_str() && id != 0) items.push_back(id);
start = end + 1;
}
return items;
}
}
#endif //!__BBBAUTOSAVEITEMS__H__

View File

@@ -0,0 +1,30 @@
#ifndef __IBBBAUTOSAVE__H__
#define __IBBBAUTOSAVE__H__
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "dCommonVars.h"
// The brick by brick model a character was building when the client last sent SetBBBAutosave (the client's quick save:
// every five minutes, before an AFK kick and before quitting). The server rebuilds it into a model when the build ends
// without a save (see docs/BuildWorkflow.md).
class IBbbAutosave {
public:
struct Info {
std::string lxfml; // sd0 compressed LXFML as the client sent it
std::vector<LWOOBJID> sourceItems; // the models that were in the BBB inventory when it was sent
int64_t updatedAt{}; // Unix seconds
};
virtual std::optional<IBbbAutosave::Info> GetBbbAutosave(const LWOOBJID characterId) = 0;
// Replaces the character's autosave
virtual void SetBbbAutosave(const LWOOBJID characterId, const IBbbAutosave::Info& info) = 0;
virtual void DeleteBbbAutosave(const LWOOBJID characterId) = 0;
};
#endif //!__IBBBAUTOSAVE__H__