diff --git a/dCommon/BrickByBrickFix.cpp b/dCommon/BrickByBrickFix.cpp index 27007a0b..b5a2df9b 100644 --- a/dCommon/BrickByBrickFix.cpp +++ b/dCommon/BrickByBrickFix.cpp @@ -118,7 +118,7 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { } std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader); - std::istringstream outputStringStream(outputString); + std::stringstream outputStringStream(outputString); try { Database::Get()->UpdateUgcModelData(model.id, outputStringStream); diff --git a/dCommon/Logger.cpp b/dCommon/Logger.cpp index 1888f1eb..f201e843 100644 --- a/dCommon/Logger.cpp +++ b/dCommon/Logger.cpp @@ -53,8 +53,8 @@ void Logger::vLog(const char* format, va_list args) { struct tm* time = localtime(&t); char timeStr[70]; strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time); - char message[2048]; - vsnprintf(message, 2048, format, args); + char message[900'000]; + vsnprintf(message, 900'000, format, args); for (const auto& writer : m_Writers) { writer->Log(timeStr, message); } diff --git a/dCommon/Lxfml.cpp b/dCommon/Lxfml.cpp index 4bc074bb..fb4dcfde 100644 --- a/dCommon/Lxfml.cpp +++ b/dCommon/Lxfml.cpp @@ -19,7 +19,10 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) { std::map transformations; 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 for (const auto& brick : lxfml["Bricks"]) { @@ -106,6 +109,7 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) { tinyxml2::XMLPrinter printer; doc.Print(&printer); + LOG("root pos %f %f %f", newRootPos.x, newRootPos.y, newRootPos.z); toReturn.lxfml = printer.CStr(); toReturn.center = newRootPos; return toReturn; diff --git a/dCommon/Sd0.cpp b/dCommon/Sd0.cpp index aa745380..39908d14 100644 --- a/dCommon/Sd0.cpp +++ b/dCommon/Sd0.cpp @@ -34,16 +34,30 @@ int32_t GetDataOffset(bool firstBuffer) { Sd0::Sd0(std::istream& buffer) { 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) { 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(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; } while (buffer) { uint32_t chunkSize{}; if (!BinaryIO::BinaryRead(buffer, chunkSize)) { - LOG("%i", m_Chunks.size()); - LOG("Failed to read chunk size from stream %i %i", buffer.tellg(), static_cast(m_Chunks.size())); + LOG("Failed to read chunk size from stream %li %li", buffer.tellg(), m_Chunks.size()); break; } auto& chunk = m_Chunks.emplace_back(); diff --git a/dCommon/Sd0.h b/dCommon/Sd0.h index 977c53da..ec0c4854 100644 --- a/dCommon/Sd0.h +++ b/dCommon/Sd0.h @@ -19,8 +19,6 @@ public: */ 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 Sd0(std::istream& buffer); diff --git a/dDatabase/CMakeLists.txt b/dDatabase/CMakeLists.txt index 42bdb983..56ed4df7 100644 --- a/dDatabase/CMakeLists.txt +++ b/dDatabase/CMakeLists.txt @@ -1,7 +1,7 @@ add_subdirectory(CDClientDatabase) add_subdirectory(GameDatabase) -add_library(dDatabase STATIC "MigrationRunner.cpp") +add_library(dDatabase STATIC "MigrationRunner.cpp" "ModelNormalizeMigration.cpp") add_custom_target(conncpp_dylib ${CMAKE_COMMAND} -E copy $ ${PROJECT_BINARY_DIR}) diff --git a/dDatabase/GameDatabase/ITables/IPropertyContents.h b/dDatabase/GameDatabase/ITables/IPropertyContents.h index 969a6583..e965e05c 100644 --- a/dDatabase/GameDatabase/ITables/IPropertyContents.h +++ b/dDatabase/GameDatabase/ITables/IPropertyContents.h @@ -34,9 +34,17 @@ public: 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. - virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) = 0; + virtual void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) = 0; + virtual void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array behaviorIDs) { + std::array, 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. virtual void RemoveModel(const LWOOBJID& modelId) = 0; + + // Gets a model by ID + virtual Model GetModel(const LWOOBJID modelID) = 0; }; #endif //!__IPROPERTIESCONTENTS__H__ diff --git a/dDatabase/GameDatabase/ITables/IUgc.h b/dDatabase/GameDatabase/ITables/IUgc.h index 024636ac..cbc770b8 100644 --- a/dDatabase/GameDatabase/ITables/IUgc.h +++ b/dDatabase/GameDatabase/ITables/IUgc.h @@ -12,6 +12,7 @@ public: struct Model { std::stringstream lxfmlData; LWOOBJID id{}; + LWOOBJID modelID{}; }; // Gets all UGC models for the given property id. @@ -27,6 +28,6 @@ public: virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0; // 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__ diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index 90af24a6..efe9b036 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -48,7 +48,7 @@ public: 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 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 GetAllUgcModels() override; void CreateMigrationHistoryTable() override; bool IsMigrationRun(const std::string_view str) override; @@ -74,7 +74,7 @@ public: std::vector GetPropertyModels(const LWOOBJID& propertyId) override; void RemoveUnreferencedUgcModels() 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, 5>& behaviors) override; + void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) override; void RemoveModel(const LWOOBJID& modelId) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void InsertNewBugReport(const IBugReports::Info& info) override; @@ -126,6 +126,7 @@ public: void DeleteUgcBuild(const LWOOBJID bigId) override; uint32_t GetAccountCount() override; bool IsNameInUse(const std::string_view name) override; + IPropertyContents::Model GetModel(const LWOOBJID modelID) override; sql::PreparedStatement* CreatePreppedStmt(const std::string& query); private: diff --git a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp index 05998785..fe63fc49 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp @@ -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, 5>& behaviors) { +void MySQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) { ExecuteUpdate( "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, " "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, - 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) { 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(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; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp index 13c2b44e..2d2655f4 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp @@ -2,7 +2,7 @@ std::vector MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) { 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); std::vector toReturn; @@ -13,7 +13,8 @@ std::vector MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) // blob is owned by the query, so we need to do a deep copy :/ std::unique_ptr blob(result->getBlob("lxfml")); 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)); } @@ -21,13 +22,14 @@ std::vector MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) } std::vector 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 models; models.reserve(result->rowsCount()); while (result->next()) { 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 :/ std::unique_ptr blob(result->getBlob("lxfml")); @@ -65,7 +67,7 @@ void MySQLDatabase::DeleteUgcModelData(const LWOOBJID& 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()); ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId); } diff --git a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h index 398e053f..968345a7 100644 --- a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h +++ b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h @@ -46,7 +46,7 @@ public: 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 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 GetAllUgcModels() override; void CreateMigrationHistoryTable() override; bool IsMigrationRun(const std::string_view str) override; @@ -72,7 +72,7 @@ public: std::vector GetPropertyModels(const LWOOBJID& propertyId) override; void RemoveUnreferencedUgcModels() 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, 5>& behaviors) override; + void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) override; void RemoveModel(const LWOOBJID& modelId) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void InsertNewBugReport(const IBugReports::Info& info) override; @@ -124,6 +124,7 @@ public: void DeleteUgcBuild(const LWOOBJID bigId) override; uint32_t GetAccountCount() override; bool IsNameInUse(const std::string_view name) override; + IPropertyContents::Model GetModel(const LWOOBJID modelID) override; private: CppSQLite3Statement CreatePreppedStmt(const std::string& query); diff --git a/dDatabase/GameDatabase/SQLite/Tables/PropertyContents.cpp b/dDatabase/GameDatabase/SQLite/Tables/PropertyContents.cpp index 6a8d7028..960e1113 100644 --- a/dDatabase/GameDatabase/SQLite/Tables/PropertyContents.cpp +++ b/dDatabase/GameDatabase/SQLite/Tables/PropertyContents.cpp @@ -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, 5>& behaviors) { +void SQLiteDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) { ExecuteUpdate( "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, " "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, - 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) { 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(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; +} diff --git a/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp b/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp index 66e3794b..c6410a42 100644 --- a/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp +++ b/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp @@ -2,7 +2,7 @@ std::vector SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) { 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); std::vector toReturn; @@ -13,7 +13,8 @@ std::vector SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId int blobSize{}; const auto* blob = result.getBlobField("lxfml", blobSize); model.lxfmlData << std::string(reinterpret_cast(blob), blobSize); - model.id = result.getInt64Field("id"); + model.id = result.getInt64Field("ugcID"); + model.modelID = result.getInt64Field("modelID"); toReturn.push_back(std::move(model)); result.nextRow(); } @@ -22,12 +23,13 @@ std::vector SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId } std::vector 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 models; while (!result.eof()) { IUgc::Model model; - model.id = result.getInt64Field("id"); + model.id = result.getInt64Field("ugcID"); + model.modelID = result.getInt64Field("modelID"); int 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); } -void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) { +void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) { const std::istream stream(lxfml.rdbuf()); ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId); } diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp index 006e2e32..12016609 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp @@ -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, 5>& behaviors) { +void TestSQLDatabase::UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) { } diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h index c864aadc..8b530490 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h @@ -25,7 +25,7 @@ class TestSQLDatabase : public GameDatabase { 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 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 GetAllUgcModels() override; void CreateMigrationHistoryTable() override; bool IsMigrationRun(const std::string_view str) override; @@ -51,7 +51,7 @@ class TestSQLDatabase : public GameDatabase { std::vector GetPropertyModels(const LWOOBJID& propertyId) override; void RemoveUnreferencedUgcModels() 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, 5>& behaviors) override; + void UpdateModel(const LWOOBJID& modelID, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) override; void RemoveModel(const LWOOBJID& modelId) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void InsertNewBugReport(const IBugReports::Info& info) override; @@ -104,6 +104,7 @@ class TestSQLDatabase : public GameDatabase { uint32_t GetAccountCount() override { return 0; }; bool IsNameInUse(const std::string_view name) override { return false; }; + IPropertyContents::Model GetModel(const LWOOBJID modelID) override { return {}; } }; #endif //!TESTSQLDATABASE_H diff --git a/dDatabase/MigrationRunner.cpp b/dDatabase/MigrationRunner.cpp index e6dfb042..8cdd17ae 100644 --- a/dDatabase/MigrationRunner.cpp +++ b/dDatabase/MigrationRunner.cpp @@ -7,6 +7,7 @@ #include "GeneralUtils.h" #include "Logger.h" #include "BinaryPathFinder.h" +#include "ModelNormalizeMigration.h" #include @@ -35,7 +36,7 @@ void MigrationRunner::RunMigrations() { 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. - + const auto migrationFolder = Database::GetMigrationFolder(); if (!Database::Get()->IsMigrationRun("17_migration_for_migrations.sql") && migrationFolder == "mysql") { LOG("Running migration: 17_migration_for_migrations.sql"); @@ -45,6 +46,7 @@ void MigrationRunner::RunMigrations() { std::string finalSQL = ""; bool runSd0Migrations = false; + bool runNormalizeMigrations = false; for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) { auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry); @@ -57,6 +59,8 @@ void MigrationRunner::RunMigrations() { LOG("Running migration: %s", migration.name.c_str()); if (migration.name == "5_brick_model_sd0.sql") { runSd0Migrations = true; + } else if (migration.name.ends_with("_normalize_model_positions.sql")) { + runNormalizeMigrations = true; } else { finalSQL.append(migration.data.c_str()); } @@ -64,7 +68,7 @@ void MigrationRunner::RunMigrations() { Database::Get()->InsertMigration(migration.name); } - if (finalSQL.empty() && !runSd0Migrations) { + if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations) { LOG("Server database is up to date."); return; } @@ -88,6 +92,10 @@ void MigrationRunner::RunMigrations() { uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml(); LOG("%i models were truncated from the database.", numberOfTruncatedModels); } + + if (runNormalizeMigrations) { + ModelNormalizeMigration::Run(); + } } void MigrationRunner::RunSQLiteMigrations() { diff --git a/dDatabase/ModelNormalizeMigration.cpp b/dDatabase/ModelNormalizeMigration.cpp new file mode 100644 index 00000000..b8215733 --- /dev/null +++ b/dDatabase/ModelNormalizeMigration.cpp @@ -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(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); +} diff --git a/dDatabase/ModelNormalizeMigration.h b/dDatabase/ModelNormalizeMigration.h new file mode 100644 index 00000000..000781cd --- /dev/null +++ b/dDatabase/ModelNormalizeMigration.h @@ -0,0 +1,11 @@ +// Darkflame Universe +// Copyright 2025 + +#ifndef MODELNORMALIZEMIGRATION_H +#define MODELNORMALIZEMIGRATION_H + +namespace ModelNormalizeMigration { + void Run(); +}; + +#endif //!MODELNORMALIZEMIGRATION_H diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 666085fe..5b1a381b 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -2576,18 +2576,6 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent 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(outData), size); //std::string version of the decompressed data! - //Now, the cave of dragons: //We runs this in async because the http library here is blocking, meaning it'll halt the thread. diff --git a/migrations/dlu/mysql/19_normalize_model_positions.sql b/migrations/dlu/mysql/19_normalize_model_positions.sql new file mode 100644 index 00000000..833fbe9b --- /dev/null +++ b/migrations/dlu/mysql/19_normalize_model_positions.sql @@ -0,0 +1 @@ +/* See ModelNormalizeMigration.cpp for details */ diff --git a/migrations/dlu/sqlite/2_normalize_model_positions.sql b/migrations/dlu/sqlite/2_normalize_model_positions.sql new file mode 100644 index 00000000..833fbe9b --- /dev/null +++ b/migrations/dlu/sqlite/2_normalize_model_positions.sql @@ -0,0 +1 @@ +/* See ModelNormalizeMigration.cpp for details */