add migration

This commit is contained in:
David Markowitz 2025-04-10 18:35:11 -07:00
parent fd27ffa9ae
commit 4b188b87ef
22 changed files with 170 additions and 47 deletions

View File

@ -118,7 +118,7 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
} }
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader); std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
std::istringstream outputStringStream(outputString); std::stringstream outputStringStream(outputString);
try { try {
Database::Get()->UpdateUgcModelData(model.id, outputStringStream); Database::Get()->UpdateUgcModelData(model.id, outputStringStream);

View File

@ -53,8 +53,8 @@ void Logger::vLog(const char* format, va_list args) {
struct tm* time = localtime(&t); struct tm* time = localtime(&t);
char timeStr[70]; char timeStr[70];
strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time); strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time);
char message[2048]; char message[900'000];
vsnprintf(message, 2048, format, args); vsnprintf(message, 900'000, format, args);
for (const auto& writer : m_Writers) { for (const auto& writer : m_Writers) {
writer->Log(timeStr, message); writer->Log(timeStr, message);
} }

View File

@ -19,7 +19,10 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
std::map<std::string/* refID */, std::string> transformations; std::map<std::string/* refID */, std::string> transformations;
auto lxfml = reader["LXFML"]; auto lxfml = reader["LXFML"];
if (!lxfml) return toReturn; if (!lxfml) {
LOG("Failed to find LXFML element.");
return toReturn;
}
// First get all the positions of bricks // First get all the positions of bricks
for (const auto& brick : lxfml["Bricks"]) { for (const auto& brick : lxfml["Bricks"]) {
@ -106,6 +109,7 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
tinyxml2::XMLPrinter printer; tinyxml2::XMLPrinter printer;
doc.Print(&printer); doc.Print(&printer);
LOG("root pos %f %f %f", newRootPos.x, newRootPos.y, newRootPos.z);
toReturn.lxfml = printer.CStr(); toReturn.lxfml = printer.CStr();
toReturn.center = newRootPos; toReturn.center = newRootPos;
return toReturn; return toReturn;

View File

@ -34,16 +34,30 @@ int32_t GetDataOffset(bool firstBuffer) {
Sd0::Sd0(std::istream& buffer) { Sd0::Sd0(std::istream& buffer) {
char header[5]{}; char header[5]{};
// Check if this is an sd0 buffer. It's possible we may be handed a zlib buffer directly due to old code so check for that too.
if (!BinaryIO::BinaryRead(buffer, header) || memcmp(header, SD0_HEADER, sizeof(header)) != 0) { if (!BinaryIO::BinaryRead(buffer, header) || memcmp(header, SD0_HEADER, sizeof(header)) != 0) {
LOG("Failed to read SD0 header %i %i %i %i %i %i %i", buffer.good(), buffer.tellg(), header[0], header[1], header[2], header[3], header[4]); LOG("Failed to read SD0 header %i %i %i %i %i %i %i", buffer.good(), buffer.tellg(), header[0], header[1], header[2], header[3], header[4]);
LOG_DEBUG("This may be a zlib buffer directly? Trying again assuming its a zlib buffer.");
auto& firstChunk = m_Chunks.emplace_back();
WriteHeader(firstChunk);
buffer.seekg(0, std::ios::end);
uint32_t bufferSize = buffer.tellg();
buffer.seekg(0, std::ios::beg);
WriteSize(firstChunk, bufferSize);
firstChunk.resize(firstChunk.size() + bufferSize);
auto* dataStart = reinterpret_cast<char*>(firstChunk.data() + GetDataOffset(true));
if (!buffer.read(dataStart, bufferSize)) {
m_Chunks.pop_back();
LOG("Failed to read %u bytes from chunk %i", bufferSize, m_Chunks.size() - 1);
}
return; return;
} }
while (buffer) { while (buffer) {
uint32_t chunkSize{}; uint32_t chunkSize{};
if (!BinaryIO::BinaryRead(buffer, chunkSize)) { if (!BinaryIO::BinaryRead(buffer, chunkSize)) {
LOG("%i", m_Chunks.size()); LOG("Failed to read chunk size from stream %li %li", buffer.tellg(), m_Chunks.size());
LOG("Failed to read chunk size from stream %i %i", buffer.tellg(), static_cast<int>(m_Chunks.size()));
break; break;
} }
auto& chunk = m_Chunks.emplace_back(); auto& chunk = m_Chunks.emplace_back();

View File

@ -19,8 +19,6 @@ public:
*/ */
static constexpr inline size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 1024 * 256; static constexpr inline size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 1024 * 256;
Sd0() {}
// Read the input buffer into an internal chunk stream to be used later // Read the input buffer into an internal chunk stream to be used later
Sd0(std::istream& buffer); Sd0(std::istream& buffer);

View File

@ -1,7 +1,7 @@
add_subdirectory(CDClientDatabase) add_subdirectory(CDClientDatabase)
add_subdirectory(GameDatabase) add_subdirectory(GameDatabase)
add_library(dDatabase STATIC "MigrationRunner.cpp") add_library(dDatabase STATIC "MigrationRunner.cpp" "ModelNormalizeMigration.cpp")
add_custom_target(conncpp_dylib add_custom_target(conncpp_dylib
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR}) ${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})

View File

@ -34,9 +34,17 @@ public:
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0; virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
// Update the model position and rotation for the given property id. // Update the model position and rotation for the given property id.
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0; virtual void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
virtual void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<int32_t, 5> behaviorIDs) {
std::array<std::pair<int32_t, std::string>, 5> behaviors;
for (int32_t i = 0; i < behaviors.size(); i++) behaviors[i].first = behaviorIDs[i];
UpdateModel(modelID, position, rotation, behaviors);
}
// Remove the model for the given property id. // Remove the model for the given property id.
virtual void RemoveModel(const LWOOBJID& modelId) = 0; virtual void RemoveModel(const LWOOBJID& modelId) = 0;
// Gets a model by ID
virtual Model GetModel(const LWOOBJID modelID) = 0;
}; };
#endif //!__IPROPERTIESCONTENTS__H__ #endif //!__IPROPERTIESCONTENTS__H__

View File

@ -12,6 +12,7 @@ public:
struct Model { struct Model {
std::stringstream lxfmlData; std::stringstream lxfmlData;
LWOOBJID id{}; LWOOBJID id{};
LWOOBJID modelID{};
}; };
// Gets all UGC models for the given property id. // Gets all UGC models for the given property id.
@ -27,6 +28,6 @@ public:
virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0; virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0;
// Inserts a new UGC model into the database. // Inserts a new UGC model into the database.
virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) = 0; virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) = 0;
}; };
#endif //!__IUGC__H__ #endif //!__IUGC__H__

View File

@ -48,7 +48,7 @@ public:
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override; void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
void DeleteUgcModelData(const LWOOBJID& modelId) override; void DeleteUgcModelData(const LWOOBJID& modelId) override;
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override; void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
std::vector<IUgc::Model> GetAllUgcModels() override; std::vector<IUgc::Model> GetAllUgcModels() override;
void CreateMigrationHistoryTable() override; void CreateMigrationHistoryTable() override;
bool IsMigrationRun(const std::string_view str) override; bool IsMigrationRun(const std::string_view str) override;
@ -74,7 +74,7 @@ public:
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override; std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
void RemoveUnreferencedUgcModels() override; void RemoveUnreferencedUgcModels() override;
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override; void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override; void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
void RemoveModel(const LWOOBJID& modelId) override; void RemoveModel(const LWOOBJID& modelId) override;
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
void InsertNewBugReport(const IBugReports::Info& info) override; void InsertNewBugReport(const IBugReports::Info& info) override;
@ -126,6 +126,7 @@ public:
void DeleteUgcBuild(const LWOOBJID bigId) override; void DeleteUgcBuild(const LWOOBJID bigId) override;
uint32_t GetAccountCount() override; uint32_t GetAccountCount() override;
bool IsNameInUse(const std::string_view name) override; bool IsNameInUse(const std::string_view name) override;
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
sql::PreparedStatement* CreatePreppedStmt(const std::string& query); sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
private: private:

View File

@ -52,14 +52,39 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
} }
} }
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) { void MySQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
ExecuteUpdate( ExecuteUpdate(
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, " "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;", "behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId); behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, modelID);
} }
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) { void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId); ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
} }
IPropertyContents::Model MySQLDatabase::GetModel(const LWOOBJID modelID) {
auto result = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
IPropertyContents::Model model{};
while (result->next()) {
model.id = result->getUInt64("id");
model.lot = static_cast<LOT>(result->getUInt("lot"));
model.position.x = result->getFloat("x");
model.position.y = result->getFloat("y");
model.position.z = result->getFloat("z");
model.rotation.w = result->getFloat("rw");
model.rotation.x = result->getFloat("rx");
model.rotation.y = result->getFloat("ry");
model.rotation.z = result->getFloat("rz");
model.ugcId = result->getUInt64("ugc_id");
model.behaviors[0] = result->getInt("behavior_1");
model.behaviors[1] = result->getInt("behavior_2");
model.behaviors[2] = result->getInt("behavior_3");
model.behaviors[3] = result->getInt("behavior_4");
model.behaviors[4] = result->getInt("behavior_5");
}
return model;
}

View File

@ -2,7 +2,7 @@
std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) { std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
auto result = ExecuteSelect( auto result = ExecuteSelect(
"SELECT lxfml, u.id 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;", "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); propertyId);
std::vector<IUgc::Model> toReturn; std::vector<IUgc::Model> toReturn;
@ -13,7 +13,8 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
// blob is owned by the query, so we need to do a deep copy :/ // blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml")); std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
model.lxfmlData << blob->rdbuf(); model.lxfmlData << blob->rdbuf();
model.id = result->getUInt64("id"); model.id = result->getUInt64("ugcID");
model.modelID = result->getUInt64("modelID");
toReturn.push_back(std::move(model)); toReturn.push_back(std::move(model));
} }
@ -21,13 +22,14 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
} }
std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() { std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
auto result = ExecuteSelect("SELECT id, lxfml FROM ugc;"); auto result = ExecuteSelect("SELECT u.id AS ugcID, lxfml, pc.id AS modelID 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<IUgc::Model> models; std::vector<IUgc::Model> models;
models.reserve(result->rowsCount()); models.reserve(result->rowsCount());
while (result->next()) { while (result->next()) {
IUgc::Model model; IUgc::Model model;
model.id = result->getInt64("id"); model.id = result->getInt64("ugcID");
model.modelID = result->getUInt64("modelID");
// blob is owned by the query, so we need to do a deep copy :/ // blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml")); std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
@ -65,7 +67,7 @@ void MySQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId); ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
} }
void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) { void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
const std::istream stream(lxfml.rdbuf()); const std::istream stream(lxfml.rdbuf());
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId); ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
} }

View File

@ -46,7 +46,7 @@ public:
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override; void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
void DeleteUgcModelData(const LWOOBJID& modelId) override; void DeleteUgcModelData(const LWOOBJID& modelId) override;
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override; void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
std::vector<IUgc::Model> GetAllUgcModels() override; std::vector<IUgc::Model> GetAllUgcModels() override;
void CreateMigrationHistoryTable() override; void CreateMigrationHistoryTable() override;
bool IsMigrationRun(const std::string_view str) override; bool IsMigrationRun(const std::string_view str) override;
@ -72,7 +72,7 @@ public:
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override; std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
void RemoveUnreferencedUgcModels() override; void RemoveUnreferencedUgcModels() override;
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override; void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override; void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
void RemoveModel(const LWOOBJID& modelId) override; void RemoveModel(const LWOOBJID& modelId) override;
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
void InsertNewBugReport(const IBugReports::Info& info) override; void InsertNewBugReport(const IBugReports::Info& info) override;
@ -124,6 +124,7 @@ public:
void DeleteUgcBuild(const LWOOBJID bigId) override; void DeleteUgcBuild(const LWOOBJID bigId) override;
uint32_t GetAccountCount() override; uint32_t GetAccountCount() override;
bool IsNameInUse(const std::string_view name) override; bool IsNameInUse(const std::string_view name) override;
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
private: private:
CppSQLite3Statement CreatePreppedStmt(const std::string& query); CppSQLite3Statement CreatePreppedStmt(const std::string& query);

View File

@ -52,14 +52,41 @@ void SQLiteDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IP
} }
} }
void SQLiteDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) { void SQLiteDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
ExecuteUpdate( ExecuteUpdate(
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, " "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;", "behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId); behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, modelID);
} }
void SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) { void SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId); ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
} }
IPropertyContents::Model SQLiteDatabase::GetModel(const LWOOBJID modelID) {
auto [_, result] = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
IPropertyContents::Model model{};
if (!result.eof()) {
do {
model.id = result.getInt64Field("id");
model.lot = static_cast<LOT>(result.getIntField("lot"));
model.position.x = result.getFloatField("x");
model.position.y = result.getFloatField("y");
model.position.z = result.getFloatField("z");
model.rotation.w = result.getFloatField("rw");
model.rotation.x = result.getFloatField("rx");
model.rotation.y = result.getFloatField("ry");
model.rotation.z = result.getFloatField("rz");
model.ugcId = result.getInt64Field("ugc_id");
model.behaviors[0] = result.getIntField("behavior_1");
model.behaviors[1] = result.getIntField("behavior_2");
model.behaviors[2] = result.getIntField("behavior_3");
model.behaviors[3] = result.getIntField("behavior_4");
model.behaviors[4] = result.getIntField("behavior_5");
} while (result.nextRow());
}
return model;
}

View File

@ -2,7 +2,7 @@
std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) { std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) {
auto [_, result] = ExecuteSelect( auto [_, result] = ExecuteSelect(
"SELECT lxfml, u.id 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;", "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); propertyId);
std::vector<IUgc::Model> toReturn; std::vector<IUgc::Model> toReturn;
@ -13,7 +13,8 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
int blobSize{}; int blobSize{};
const auto* blob = result.getBlobField("lxfml", blobSize); const auto* blob = result.getBlobField("lxfml", blobSize);
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize); model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
model.id = result.getInt64Field("id"); model.id = result.getInt64Field("ugcID");
model.modelID = result.getInt64Field("modelID");
toReturn.push_back(std::move(model)); toReturn.push_back(std::move(model));
result.nextRow(); result.nextRow();
} }
@ -22,12 +23,13 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
} }
std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() { std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() {
auto [_, result] = ExecuteSelect("SELECT id, lxfml FROM ugc;"); 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; std::vector<IUgc::Model> models;
while (!result.eof()) { while (!result.eof()) {
IUgc::Model model; IUgc::Model model;
model.id = result.getInt64Field("id"); model.id = result.getInt64Field("ugcID");
model.modelID = result.getInt64Field("modelID");
int blobSize{}; int blobSize{};
const auto* blob = result.getBlobField("lxfml", blobSize); const auto* blob = result.getBlobField("lxfml", blobSize);
@ -66,7 +68,7 @@ void SQLiteDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId); ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId);
} }
void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) { void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
const std::istream stream(lxfml.rdbuf()); const std::istream stream(lxfml.rdbuf());
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId); ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
} }

View File

@ -60,7 +60,7 @@ void TestSQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
} }
void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) { void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) {
} }
@ -164,7 +164,7 @@ void TestSQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const I
} }
void TestSQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) { void TestSQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
} }

View File

@ -25,7 +25,7 @@ class TestSQLDatabase : public GameDatabase {
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override; void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
void DeleteUgcModelData(const LWOOBJID& modelId) override; void DeleteUgcModelData(const LWOOBJID& modelId) override;
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override; void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
std::vector<IUgc::Model> GetAllUgcModels() override; std::vector<IUgc::Model> GetAllUgcModels() override;
void CreateMigrationHistoryTable() override; void CreateMigrationHistoryTable() override;
bool IsMigrationRun(const std::string_view str) override; bool IsMigrationRun(const std::string_view str) override;
@ -51,7 +51,7 @@ class TestSQLDatabase : public GameDatabase {
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override; std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
void RemoveUnreferencedUgcModels() override; void RemoveUnreferencedUgcModels() override;
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override; void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override; void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
void RemoveModel(const LWOOBJID& modelId) override; void RemoveModel(const LWOOBJID& modelId) override;
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
void InsertNewBugReport(const IBugReports::Info& info) override; void InsertNewBugReport(const IBugReports::Info& info) override;
@ -104,6 +104,7 @@ class TestSQLDatabase : public GameDatabase {
uint32_t GetAccountCount() override { return 0; }; uint32_t GetAccountCount() override { return 0; };
bool IsNameInUse(const std::string_view name) override { return false; }; bool IsNameInUse(const std::string_view name) override { return false; };
IPropertyContents::Model GetModel(const LWOOBJID modelID) override { return {}; }
}; };
#endif //!TESTSQLDATABASE_H #endif //!TESTSQLDATABASE_H

View File

@ -7,6 +7,7 @@
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "Logger.h" #include "Logger.h"
#include "BinaryPathFinder.h" #include "BinaryPathFinder.h"
#include "ModelNormalizeMigration.h"
#include <fstream> #include <fstream>
@ -35,7 +36,7 @@ void MigrationRunner::RunMigrations() {
Database::Get()->CreateMigrationHistoryTable(); Database::Get()->CreateMigrationHistoryTable();
// has to be here because when moving the files to the new folder, the migration_history table is not updated so it will run them all again. // has to be here because when moving the files to the new folder, the migration_history table is not updated so it will run them all again.
const auto migrationFolder = Database::GetMigrationFolder(); const auto migrationFolder = Database::GetMigrationFolder();
if (!Database::Get()->IsMigrationRun("17_migration_for_migrations.sql") && migrationFolder == "mysql") { if (!Database::Get()->IsMigrationRun("17_migration_for_migrations.sql") && migrationFolder == "mysql") {
LOG("Running migration: 17_migration_for_migrations.sql"); LOG("Running migration: 17_migration_for_migrations.sql");
@ -45,6 +46,7 @@ void MigrationRunner::RunMigrations() {
std::string finalSQL = ""; std::string finalSQL = "";
bool runSd0Migrations = false; bool runSd0Migrations = false;
bool runNormalizeMigrations = false;
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) { for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry); auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
@ -57,6 +59,8 @@ void MigrationRunner::RunMigrations() {
LOG("Running migration: %s", migration.name.c_str()); LOG("Running migration: %s", migration.name.c_str());
if (migration.name == "5_brick_model_sd0.sql") { if (migration.name == "5_brick_model_sd0.sql") {
runSd0Migrations = true; runSd0Migrations = true;
} else if (migration.name.ends_with("_normalize_model_positions.sql")) {
runNormalizeMigrations = true;
} else { } else {
finalSQL.append(migration.data.c_str()); finalSQL.append(migration.data.c_str());
} }
@ -64,7 +68,7 @@ void MigrationRunner::RunMigrations() {
Database::Get()->InsertMigration(migration.name); Database::Get()->InsertMigration(migration.name);
} }
if (finalSQL.empty() && !runSd0Migrations) { if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations) {
LOG("Server database is up to date."); LOG("Server database is up to date.");
return; return;
} }
@ -88,6 +92,10 @@ void MigrationRunner::RunMigrations() {
uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml(); uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml();
LOG("%i models were truncated from the database.", numberOfTruncatedModels); LOG("%i models were truncated from the database.", numberOfTruncatedModels);
} }
if (runNormalizeMigrations) {
ModelNormalizeMigration::Run();
}
} }
void MigrationRunner::RunSQLiteMigrations() { void MigrationRunner::RunSQLiteMigrations() {

View File

@ -0,0 +1,30 @@
#include "ModelNormalizeMigration.h"
#include "Database.h"
#include "Lxfml.h"
#include "Sd0.h"
void ModelNormalizeMigration::Run() {
const auto oldCommit = Database::Get()->GetAutoCommit();
Database::Get()->SetAutoCommit(false);
for (auto& [lxfmlData, id, modelID] : Database::Get()->GetAllUgcModels()) {
const auto model = Database::Get()->GetModel(modelID);
// only BBB models (lot 14) and models with a position of NiPoint3::ZERO need to have their position fixed.
if (model.position != NiPoint3Constant::ZERO || model.lot != 14) continue;
Sd0 sd0(lxfmlData);
const auto asStr = sd0.GetAsStringUncompressed();
const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr);
if (newCenter == NiPoint3Constant::ZERO) {
LOG("Failed to update model %llu due to failure reading xml.");
continue;
}
LOG("Updated model %llu to have a center of %f %f %f", modelID, newCenter.x, newCenter.y, newCenter.z);
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
auto asStream = sd0.GetAsStream();
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
Database::Get()->UpdateUgcModelData(id, asStream);
}
Database::Get()->SetAutoCommit(oldCommit);
}

View File

@ -0,0 +1,11 @@
// Darkflame Universe
// Copyright 2025
#ifndef MODELNORMALIZEMIGRATION_H
#define MODELNORMALIZEMIGRATION_H
namespace ModelNormalizeMigration {
void Run();
};
#endif //!MODELNORMALIZEMIGRATION_H

View File

@ -2576,18 +2576,6 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
TODO Apparently the bricks are supposed to be taken via MoveInventoryBatch? TODO Apparently the bricks are supposed to be taken via MoveInventoryBatch?
*/ */
////Decompress the SD0 from the client so we can process the lxfml properly
//uint8_t* outData = new uint8_t[327680];
//int32_t error;
//int32_t size = ZCompression::Decompress(inData, lxfmlSize, outData, 327680, error);
//if (size == -1) {
// LOG("Failed to decompress LXFML: (%i)", error);
// return;
//}
//
//std::string lxfml(reinterpret_cast<char*>(outData), size); //std::string version of the decompressed data!
//Now, the cave of dragons: //Now, the cave of dragons:
//We runs this in async because the http library here is blocking, meaning it'll halt the thread. //We runs this in async because the http library here is blocking, meaning it'll halt the thread.

View File

@ -0,0 +1 @@
/* See ModelNormalizeMigration.cpp for details */

View File

@ -0,0 +1 @@
/* See ModelNormalizeMigration.cpp for details */