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.
This commit is contained in:
David Markowitz 2025-04-09 23:36:57 -07:00
parent 77c88575f9
commit fd27ffa9ae
14 changed files with 36 additions and 26 deletions

View File

@ -8,6 +8,7 @@
#include "Database.h" #include "Database.h"
#include "Game.h" #include "Game.h"
#include "Sd0.h"
#include "ZCompression.h" #include "ZCompression.h"
#include "Logger.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. // Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
std::unique_ptr<uint8_t[]> uncompressedChunk(new uint8_t[ZCompression::MAX_SD0_CHUNK_SIZE]); std::unique_ptr<uint8_t[]> uncompressedChunk(new uint8_t[Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE]);
int32_t err{}; int32_t err{};
int32_t actualUncompressedSize = ZCompression::Decompress( 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) { if (actualUncompressedSize != -1) {
uint32_t previousSize = completeUncompressedModel.size(); uint32_t previousSize = completeUncompressedModel.size();

View File

@ -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! // 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 // 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. // 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(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_DEBUG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->LogDebug(str_, message, ##__VA_ARGS__); } while(0)
// Writer class for writing data to files. // Writer class for writing data to files.
class Writer { class Writer {

View File

@ -8,11 +8,5 @@ namespace ZCompression {
int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst); 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); 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;
} }

View File

@ -1,6 +1,7 @@
#include "Pack.h" #include "Pack.h"
#include "BinaryIO.h" #include "BinaryIO.h"
#include "Sd0.h"
#include "ZCompression.h" #include "ZCompression.h"
Pack::Pack(const std::filesystem::path& filePath) { 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 pos += size; // Move pointer position the amount of bytes read to the right
int32_t err; int32_t err;
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err); currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
free(chunk); free(chunk);
} }

View File

@ -22,7 +22,7 @@ public:
// Inserts a new UGC model into the database. // Inserts a new UGC model into the database.
virtual void InsertNewUgcModel( virtual void InsertNewUgcModel(
std::istringstream& sd0Data, std::stringstream& sd0Data,
const uint32_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const uint32_t characterId) = 0; const uint32_t characterId) = 0;

View File

@ -81,7 +81,7 @@ public:
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
void InsertNewMail(const MailInfo& mail) override; void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel( void InsertNewUgcModel(
std::istringstream& sd0Data, std::stringstream& sd0Data,
const uint32_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const uint32_t characterId) override; const uint32_t characterId) override;

View File

@ -43,7 +43,7 @@ void MySQLDatabase::RemoveUnreferencedUgcModels() {
} }
void MySQLDatabase::InsertNewUgcModel( void MySQLDatabase::InsertNewUgcModel(
std::istringstream& sd0Data, // cant be const sad std:: stringstream& sd0Data, // cant be const sad
const uint32_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const uint32_t characterId) { const uint32_t characterId) {

View File

@ -79,7 +79,7 @@ public:
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
void InsertNewMail(const MailInfo& mail) override; void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel( void InsertNewUgcModel(
std::istringstream& sd0Data, std::stringstream& sd0Data,
const uint32_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const uint32_t characterId) override; const uint32_t characterId) override;

View File

@ -44,7 +44,7 @@ void SQLiteDatabase::RemoveUnreferencedUgcModels() {
} }
void SQLiteDatabase::InsertNewUgcModel( void SQLiteDatabase::InsertNewUgcModel(
std::istringstream& sd0Data, // cant be const sad std::stringstream& sd0Data, // cant be const sad
const uint32_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const uint32_t characterId) { const uint32_t characterId) {

View File

@ -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) {
} }

View File

@ -58,7 +58,7 @@ class TestSQLDatabase : public GameDatabase {
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
void InsertNewMail(const MailInfo& mail) override; void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel( void InsertNewUgcModel(
std::istringstream& sd0Data, std::stringstream& sd0Data,
const uint32_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const uint32_t characterId) override; const uint32_t characterId) override;

View File

@ -99,7 +99,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
} }
// Exclude the zone control object from any flags // 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 // The client flags means the client should render the entity
GeneralUtils::SetBit(id, eObjectBits::CLIENT); GeneralUtils::SetBit(id, eObjectBits::CLIENT);

View File

@ -102,6 +102,8 @@
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
#include "CDObjectsTable.h" #include "CDObjectsTable.h"
#include "eItemType.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) { void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) {
CBITSTREAM; CBITSTREAM;
@ -2613,16 +2615,26 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
LWOOBJID propertyId = LWOOBJID_EMPTY; LWOOBJID propertyId = LWOOBJID_EMPTY;
if (propertyInfo) propertyId = propertyInfo->id; if (propertyInfo) propertyId = propertyInfo->id;
//Insert into ugc: // Save the binary data to the Sd0 buffer
std::string str(sd0Data.get(), sd0Size); std::string str(sd0Data.get(), sd0Size);
std::istringstream sd0DataStream(str); 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<const uint8_t*>(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: //Insert into the db as a BBB model:
IPropertyContents::Model model; IPropertyContents::Model model;
model.id = newIDL; model.id = newIDL;
model.ugcId = blueprintIDSmall; model.ugcId = blueprintIDSmall;
model.position = NiPoint3Constant::ZERO; model.position = newCenter;
model.rotation = NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f); model.rotation = NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
model.lot = 14; model.lot = 14;
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_14_name"); 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): //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; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::BLUEPRINT_SAVE_RESPONSE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::BLUEPRINT_SAVE_RESPONSE);
bitStream.Write(localId); bitStream.Write(localId);
@ -2655,9 +2670,9 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
bitStream.Write<uint32_t>(1); bitStream.Write<uint32_t>(1);
bitStream.Write(blueprintID); bitStream.Write(blueprintID);
bitStream.Write<uint32_t>(sd0Size); bitStream.Write(sd0Size);
bitStream.WriteAlignedBytes(reinterpret_cast<unsigned char*>(sd0Data.get()), sd0Size); for (const auto& chunk : newSd0) bitStream.WriteAlignedBytes(reinterpret_cast<const unsigned char*>(chunk.data()), chunk.size());
SEND_PACKET; SEND_PACKET;
@ -2665,7 +2680,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
EntityInfo info; EntityInfo info;
info.lot = 14; info.lot = 14;
info.pos = {}; info.pos = newCenter;
info.rot = {}; info.rot = {};
info.spawner = nullptr; info.spawner = nullptr;
info.spawnerID = entity->GetObjectID(); info.spawnerID = entity->GetObjectID();

View File

@ -1417,7 +1417,6 @@ void WorldShutdownProcess(uint32_t zoneId) {
if (PropertyManagementComponent::Instance() != nullptr) { if (PropertyManagementComponent::Instance() != nullptr) {
LOG("Saving ALL property data for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId()); LOG("Saving ALL property data for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId());
PropertyManagementComponent::Instance()->Save(); PropertyManagementComponent::Instance()->Save();
Database::Get()->RemoveUnreferencedUgcModels();
LOG("ALL property data saved for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId()); LOG("ALL property data saved for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId());
} }