From fd27ffa9ae9d525511b6c3b8d28f73bf3fd531f3 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Wed, 9 Apr 2025 23:36:57 -0700 Subject: [PATCH] Normalize model positions when placing in the world Have tested that placing a small and very large model both place and are located at the correct position. --- dCommon/BrickByBrickFix.cpp | 5 ++-- dCommon/Logger.h | 4 +-- dCommon/ZCompression.h | 6 ----- dCommon/dClient/Pack.cpp | 3 ++- .../GameDatabase/ITables/IPropertyContents.h | 2 +- dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 2 +- dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp | 2 +- .../GameDatabase/SQLite/SQLiteDatabase.h | 2 +- dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp | 2 +- .../GameDatabase/TestSQL/TestSQLDatabase.cpp | 2 +- .../GameDatabase/TestSQL/TestSQLDatabase.h | 2 +- dGame/EntityManager.cpp | 2 +- dGame/dGameMessages/GameMessages.cpp | 27 ++++++++++++++----- dWorldServer/WorldServer.cpp | 1 - 14 files changed, 36 insertions(+), 26 deletions(-) diff --git a/dCommon/BrickByBrickFix.cpp b/dCommon/BrickByBrickFix.cpp index 5aef091a..27007a0b 100644 --- a/dCommon/BrickByBrickFix.cpp +++ b/dCommon/BrickByBrickFix.cpp @@ -8,6 +8,7 @@ #include "Database.h" #include "Game.h" +#include "Sd0.h" #include "ZCompression.h" #include "Logger.h" @@ -44,10 +45,10 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { } // Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size. - std::unique_ptr uncompressedChunk(new uint8_t[ZCompression::MAX_SD0_CHUNK_SIZE]); + std::unique_ptr uncompressedChunk(new uint8_t[Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE]); int32_t err{}; int32_t actualUncompressedSize = ZCompression::Decompress( - compressedChunk.get(), chunkSize, uncompressedChunk.get(), ZCompression::MAX_SD0_CHUNK_SIZE, err); + compressedChunk.get(), chunkSize, uncompressedChunk.get(), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err); if (actualUncompressedSize != -1) { uint32_t previousSize = completeUncompressedModel.size(); diff --git a/dCommon/Logger.h b/dCommon/Logger.h index 5754d9ac..3a1771e6 100644 --- a/dCommon/Logger.h +++ b/dCommon/Logger.h @@ -29,8 +29,8 @@ constexpr const char* GetFileNameFromAbsolutePath(const char* path) { // they will not be valid constexpr and will be evaluated at runtime instead of compile time! // The full string is still stored in the binary, however the offset of the filename in the absolute paths // is used in the instruction instead of the start of the absolute path. -#define LOG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->Log(str, message, ##__VA_ARGS__); } while(0) -#define LOG_DEBUG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); } while(0) +#define LOG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->Log(str_, message, ##__VA_ARGS__); } while(0) +#define LOG_DEBUG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->LogDebug(str_, message, ##__VA_ARGS__); } while(0) // Writer class for writing data to files. class Writer { diff --git a/dCommon/ZCompression.h b/dCommon/ZCompression.h index 84e8a9b4..22a5ff86 100644 --- a/dCommon/ZCompression.h +++ b/dCommon/ZCompression.h @@ -8,11 +8,5 @@ namespace ZCompression { int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst); int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr); - - /** - * @brief Max size of an inflated sd0 zlib chunk - * - */ - constexpr uint32_t MAX_SD0_CHUNK_SIZE = 1024 * 256; } diff --git a/dCommon/dClient/Pack.cpp b/dCommon/dClient/Pack.cpp index 04d07f12..800cfa19 100644 --- a/dCommon/dClient/Pack.cpp +++ b/dCommon/dClient/Pack.cpp @@ -1,6 +1,7 @@ #include "Pack.h" #include "BinaryIO.h" +#include "Sd0.h" #include "ZCompression.h" Pack::Pack(const std::filesystem::path& filePath) { @@ -106,7 +107,7 @@ bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) cons pos += size; // Move pointer position the amount of bytes read to the right int32_t err; - currentReadPos += ZCompression::Decompress(reinterpret_cast(chunk), size, reinterpret_cast(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err); + currentReadPos += ZCompression::Decompress(reinterpret_cast(chunk), size, reinterpret_cast(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err); free(chunk); } diff --git a/dDatabase/GameDatabase/ITables/IPropertyContents.h b/dDatabase/GameDatabase/ITables/IPropertyContents.h index 0d8d8b5c..969a6583 100644 --- a/dDatabase/GameDatabase/ITables/IPropertyContents.h +++ b/dDatabase/GameDatabase/ITables/IPropertyContents.h @@ -22,7 +22,7 @@ public: // Inserts a new UGC model into the database. virtual void InsertNewUgcModel( - std::istringstream& sd0Data, + std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) = 0; diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index 0f50f174..90af24a6 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -81,7 +81,7 @@ public: void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; void InsertNewMail(const MailInfo& mail) override; void InsertNewUgcModel( - std::istringstream& sd0Data, + std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) override; diff --git a/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp index 3b62a51b..13c2b44e 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp @@ -43,7 +43,7 @@ void MySQLDatabase::RemoveUnreferencedUgcModels() { } void MySQLDatabase::InsertNewUgcModel( - std::istringstream& sd0Data, // cant be const sad + std:: stringstream& sd0Data, // cant be const sad const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) { diff --git a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h index f456c459..398e053f 100644 --- a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h +++ b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h @@ -79,7 +79,7 @@ public: void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; void InsertNewMail(const MailInfo& mail) override; void InsertNewUgcModel( - std::istringstream& sd0Data, + std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) override; diff --git a/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp b/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp index 048b53ab..66e3794b 100644 --- a/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp +++ b/dDatabase/GameDatabase/SQLite/Tables/Ugc.cpp @@ -44,7 +44,7 @@ void SQLiteDatabase::RemoveUnreferencedUgcModels() { } void SQLiteDatabase::InsertNewUgcModel( - std::istringstream& sd0Data, // cant be const sad + std::stringstream& sd0Data, // cant be const sad const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) { diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp index 0cae0fc2..006e2e32 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp @@ -188,7 +188,7 @@ void TestSQLDatabase::InsertNewMail(const MailInfo& mail) { } -void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) { +void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) { } diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h index 1cbb3c7b..c864aadc 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h @@ -58,7 +58,7 @@ class TestSQLDatabase : public GameDatabase { void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; void InsertNewMail(const MailInfo& mail) override; void InsertNewUgcModel( - std::istringstream& sd0Data, + std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) override; diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index c95af3d7..ad0c0b1c 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -99,7 +99,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE } // Exclude the zone control object from any flags - if (!controller && info.lot != 14) { + if (!controller) { // The client flags means the client should render the entity GeneralUtils::SetBit(id, eObjectBits::CLIENT); diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 6fd7576f..666085fe 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -102,6 +102,8 @@ #include "CDComponentsRegistryTable.h" #include "CDObjectsTable.h" #include "eItemType.h" +#include "Lxfml.h" +#include "Sd0.h" void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) { CBITSTREAM; @@ -2613,16 +2615,26 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent LWOOBJID propertyId = LWOOBJID_EMPTY; if (propertyInfo) propertyId = propertyInfo->id; - //Insert into ugc: + // Save the binary data to the Sd0 buffer std::string str(sd0Data.get(), sd0Size); std::istringstream sd0DataStream(str); - Database::Get()->InsertNewUgcModel(sd0DataStream, blueprintIDSmall, entity->GetCharacter()->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID()); + Sd0 sd0(sd0DataStream); + + // Uncompress the data and normalize the position + const auto asStr = sd0.GetAsStringUncompressed(); + const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr); + auto [x, y, z] = newCenter; + + // Recompress the data and save to the database + sd0.FromData(reinterpret_cast(newLxfml.data()), newLxfml.size()); + auto sd0AsStream = sd0.GetAsStream(); + Database::Get()->InsertNewUgcModel(sd0AsStream, blueprintIDSmall, entity->GetCharacter()->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID()); //Insert into the db as a BBB model: IPropertyContents::Model model; model.id = newIDL; model.ugcId = blueprintIDSmall; - model.position = NiPoint3Constant::ZERO; + model.position = newCenter; model.rotation = NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f); model.lot = 14; Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_14_name"); @@ -2648,6 +2660,9 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent //} //Tell the client their model is saved: (this causes us to actually pop out of our current state): + const auto& newSd0 = sd0.GetAsVector(); + uint32_t sd0Size{}; + for (const auto& chunk : newSd0) sd0Size += chunk.size(); CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::BLUEPRINT_SAVE_RESPONSE); bitStream.Write(localId); @@ -2655,9 +2670,9 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent bitStream.Write(1); bitStream.Write(blueprintID); - bitStream.Write(sd0Size); + bitStream.Write(sd0Size); - bitStream.WriteAlignedBytes(reinterpret_cast(sd0Data.get()), sd0Size); + for (const auto& chunk : newSd0) bitStream.WriteAlignedBytes(reinterpret_cast(chunk.data()), chunk.size()); SEND_PACKET; @@ -2665,7 +2680,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent EntityInfo info; info.lot = 14; - info.pos = {}; + info.pos = newCenter; info.rot = {}; info.spawner = nullptr; info.spawnerID = entity->GetObjectID(); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 1506cac4..643c85d2 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -1417,7 +1417,6 @@ void WorldShutdownProcess(uint32_t zoneId) { if (PropertyManagementComponent::Instance() != nullptr) { LOG("Saving ALL property data for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId()); PropertyManagementComponent::Instance()->Save(); - Database::Get()->RemoveUnreferencedUgcModels(); LOG("ALL property data saved for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId()); }