Housing: Add immediate DB persistence for PlaceDecor and RemoveDecor

PlaceDecor and RemoveDecor previously only modified in-memory state,
relying on SaveToDB at logout for persistence. A server crash would
lose all placed/removed decor changes since last save.

Add crash-safe immediate persistence:
- PlaceDecor: INSERT decor row + UPDATE catalog count on placement
- RemoveDecor: DELETE decor row + UPDATE catalog count on removal
- New prepared statements: CHAR_UPD_CHARACTER_HOUSING_CATALOG_COUNT,
  CHAR_DEL_CHARACTER_HOUSING_DECOR_SINGLE
This commit is contained in:
luis
2026-02-20 21:57:10 -03:00
parent 6742e1d1c9
commit 578cc76cc8
3 changed files with 53 additions and 0 deletions
@@ -832,6 +832,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_CHARACTER_HOUSING_ROOM, "UPDATE character_housing_rooms SET slotIndex = ?, orientation = ?, mirrored = ?, themeId = ?, wallpaperId = ?, materialId = ?, doorTypeId = ?, doorSlot = ?, ceilingTypeId = ?, ceilingSlot = ? WHERE ownerGuid = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHARACTER_HOUSING_FIXTURE, "UPDATE character_housing_fixtures SET fixtureOptionId = ? WHERE ownerGuid = ? AND fixturePointId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHARACTER_HOUSING_FIXTURE_SINGLE, "DELETE FROM character_housing_fixtures WHERE ownerGuid = ? AND fixturePointId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHARACTER_HOUSING_CATALOG_COUNT, "UPDATE character_housing_catalog SET quantity = ? WHERE ownerGuid = ? AND houseDecorId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHARACTER_HOUSING_DECOR_SINGLE, "DELETE FROM character_housing_decor WHERE ownerGuid = ? AND id = ?", CONNECTION_ASYNC);
// Neighborhoods
PrepareStatement(CHAR_SEL_NEIGHBORHOOD, "SELECT guid, name, neighborhoodMapId, ownerGuid, factionRestriction, isPublic, createTime FROM neighborhoods WHERE guid = ?", CONNECTION_SYNCH);
@@ -686,6 +686,8 @@ enum CharacterDatabaseStatements : uint32
CHAR_UPD_CHARACTER_HOUSING_ROOM,
CHAR_UPD_CHARACTER_HOUSING_FIXTURE,
CHAR_DEL_CHARACTER_HOUSING_FIXTURE_SINGLE,
CHAR_UPD_CHARACTER_HOUSING_CATALOG_COUNT,
CHAR_DEL_CHARACTER_HOUSING_DECOR_SINGLE,
// Neighborhoods
CHAR_SEL_NEIGHBORHOOD,
+49
View File
@@ -483,6 +483,38 @@ HousingResult Housing::PlaceDecor(uint32 decorEntryId, float x, float y, float z
else
_interiorDecorWeightUsed += weightCost;
// Immediate persist for crash safety
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_HOUSING_DECOR);
uint8 index = 0;
stmt->setUInt64(index++, _owner->GetGUID().GetCounter());
stmt->setUInt64(index++, decorGuid.GetCounter());
stmt->setUInt32(index++, decorEntryId);
stmt->setFloat(index++, x);
stmt->setFloat(index++, y);
stmt->setFloat(index++, z);
stmt->setFloat(index++, rotX);
stmt->setFloat(index++, rotY);
stmt->setFloat(index++, rotZ);
stmt->setFloat(index++, rotW);
stmt->setUInt32(index++, 0); // dyeSlot0
stmt->setUInt32(index++, 0); // dyeSlot1
stmt->setUInt32(index++, 0); // dyeSlot2
stmt->setUInt64(index++, roomGuid.IsEmpty() ? 0 : roomGuid.GetCounter());
stmt->setUInt8(index++, 0); // locked
CharacterDatabase.Execute(stmt);
}
// Also persist updated catalog count
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_HOUSING_CATALOG_COUNT);
auto catItr = _catalog.find(decorEntryId);
stmt->setUInt32(0, catItr != _catalog.end() ? catItr->second.Count : 0);
stmt->setUInt64(1, _owner->GetGUID().GetCounter());
stmt->setUInt32(2, decorEntryId);
CharacterDatabase.Execute(stmt);
}
// Update account decor storage UpdateField
if (_owner->GetSession())
_owner->GetSession()->GetBattlenetAccount().SetHousingDecorStorageEntry(decorGuid, _houseGuid, 0);
@@ -579,6 +611,23 @@ HousingResult Housing::RemoveDecor(ObjectGuid decorGuid)
_placedDecor.erase(itr);
// Immediate persist for crash safety ? delete the placed decor row
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_HOUSING_DECOR_SINGLE);
stmt->setUInt64(0, _owner->GetGUID().GetCounter());
stmt->setUInt64(1, decorGuid.GetCounter());
CharacterDatabase.Execute(stmt);
}
// Persist updated catalog count
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_HOUSING_CATALOG_COUNT);
stmt->setUInt32(0, _catalog[decorEntryId].Count);
stmt->setUInt64(1, _owner->GetGUID().GetCounter());
stmt->setUInt32(2, decorEntryId);
CharacterDatabase.Execute(stmt);
}
// Remove from account decor storage UpdateField
if (_owner->GetSession())
_owner->GetSession()->GetBattlenetAccount().RemoveHousingDecorStorageEntry(decorGuid);