Core/Housing: stage 39 create deferred decor GUIDs in retail layout

This commit is contained in:
Gexo91
2026-09-12 23:54:54 +03:00
parent 34b118eaf6
commit 26aae0d429
@@ -0,0 +1,181 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*/
#include "HousingDecorRedemptionSession.h"
#include "DatabaseEnv.h"
#include "HousingDefines.h"
#include "HousingPackets.h"
#include "Log.h"
#include "RealmList.h"
#include "WorldSession.h"
namespace Housing
{
namespace
{
uint32 GetDecorGuidNamespace()
{
uint32 const realmId = sRealmList->GetCurrentRealmId().Realm & 0xFFFFu;
return realmId ? realmId : 1u;
}
uint64 MakeDecorInstanceId(uint32 battlenetAccountId, uint32 transactionId)
{
return (uint64(battlenetAccountId) << 32) | uint64(transactionId);
}
bool NormalizePersistedRedemptionGuid(uint32 battlenetAccountId, uint32 transactionId,
uint32 decorId, ObjectGuid& decorGuid)
{
if (IsDecorGuid(decorGuid) && GetDecorId(decorGuid) == decorId)
return true;
ObjectGuid const replacement = CreateDecorGuid(GetDecorGuidNamespace(), decorId,
MakeDecorInstanceId(battlenetAccountId, transactionId));
if (!IsDecorGuid(replacement) || GetDecorId(replacement) != decorId)
return false;
std::string const oldGuid = decorGuid.ToString();
std::string const newGuid = replacement.ToString();
QueryResult collision = CharacterDatabase.PQuery(
"SELECT 1 FROM housing_decor_instance WHERE DecorGuid = '{}' AND DecorGuid <> '{}' LIMIT 1",
newGuid, oldGuid);
if (collision)
return false;
CharacterDatabaseTransaction transaction = CharacterDatabase.BeginTransaction();
transaction->PAppend(
"UPDATE housing_placed_decor SET DecorGuid = '{}' WHERE DecorGuid = '{}'",
newGuid, oldGuid);
transaction->PAppend(
"UPDATE housing_decor_instance SET DecorGuid = '{}' "
"WHERE BattlenetAccountId = {} AND DecorGuid = '{}' AND DecorID = {}",
newGuid, battlenetAccountId, oldGuid, decorId);
transaction->PAppend(
"UPDATE housing_deferred_decor_redemption SET DecorGuid = '{}' "
"WHERE BattlenetAccountId = {} AND TransactionID = {} AND DecorID = {}",
newGuid, battlenetAccountId, transactionId, decorId);
CharacterDatabase.DirectCommitTransaction(transaction);
QueryResult verified = CharacterDatabase.PQuery(
"SELECT 1 FROM housing_deferred_decor_redemption r "
"LEFT JOIN housing_decor_instance i ON i.BattlenetAccountId = r.BattlenetAccountId "
"AND i.DecorGuid = r.DecorGuid AND i.DecorID = r.DecorID "
"WHERE r.BattlenetAccountId = {} AND r.TransactionID = {} AND r.DecorID = {} "
"AND r.DecorGuid = '{}' LIMIT 1",
battlenetAccountId, transactionId, decorId, newGuid);
if (!verified)
return false;
decorGuid = replacement;
return true;
}
}
void HandleDecorRedeemDeferred(WorldSession* session,
WorldPackets::Housing::HousingDecorRedeemDeferredDecor& packet)
{
if (!session)
return;
WorldPackets::Housing::HousingRedeemDeferredDecorResponse response;
response.TransactionID = packet.TransactionID;
response.Result = static_cast<uint8>(Result::InvalidDecorItem);
if (!packet.DecorID || !packet.TransactionID)
{
session->SendPacket(response.Write());
return;
}
uint32 const battlenetAccountId = session->GetBattlenetAccountId();
QueryResult existing = CharacterDatabase.PQuery(
"SELECT DecorID, DecorGuid FROM housing_deferred_decor_redemption "
"WHERE BattlenetAccountId = {} AND TransactionID = {} LIMIT 1",
battlenetAccountId, packet.TransactionID);
if (existing)
{
Field* fields = existing->Fetch();
uint32 const persistedDecorId = fields[0].GetUInt32();
ObjectGuid persistedDecorGuid = ObjectGuid::FromString(fields[1].GetString());
if (persistedDecorId != packet.DecorID || persistedDecorGuid == ObjectGuid::FromStringFailed ||
!NormalizePersistedRedemptionGuid(battlenetAccountId, packet.TransactionID,
packet.DecorID, persistedDecorGuid))
{
session->SendPacket(response.Write());
return;
}
response.DecorGUID = persistedDecorGuid;
CharacterDatabaseTransaction transaction = CharacterDatabase.BeginTransaction();
transaction->PAppend(
"INSERT INTO account_housing_decor (BattlenetAccountId, DecorId, Quantity) VALUES ({}, {}, 1) "
"ON DUPLICATE KEY UPDATE Quantity = Quantity",
battlenetAccountId, packet.DecorID);
transaction->PAppend(
"INSERT INTO housing_decor_instance "
"(BattlenetAccountId, DecorGuid, DecorID, HouseGuid, PlacementStatus, SourceType, SourceValue) "
"VALUES ({}, '{}', {}, '', 0, 0, '') "
"ON DUPLICATE KEY UPDATE DecorID = VALUES(DecorID)",
battlenetAccountId, response.DecorGUID.ToString(), packet.DecorID);
CharacterDatabase.DirectCommitTransaction(transaction);
}
else
{
response.DecorGUID = CreateDecorGuid(GetDecorGuidNamespace(), packet.DecorID,
MakeDecorInstanceId(battlenetAccountId, packet.TransactionID));
if (!IsDecorGuid(response.DecorGUID) || GetDecorId(response.DecorGUID) != packet.DecorID)
{
session->SendPacket(response.Write());
return;
}
QueryResult collision = CharacterDatabase.PQuery(
"SELECT 1 FROM housing_decor_instance WHERE DecorGuid = '{}' LIMIT 1",
response.DecorGUID.ToString());
if (collision)
{
TC_LOG_ERROR("housing", "Deferred decor GUID collision for Battle.net account {} transaction {}",
battlenetAccountId, packet.TransactionID);
session->SendPacket(response.Write());
return;
}
CharacterDatabaseTransaction transaction = CharacterDatabase.BeginTransaction();
transaction->PAppend(
"INSERT INTO housing_deferred_decor_redemption "
"(BattlenetAccountId, TransactionID, DecorID, DecorGuid) VALUES ({}, {}, {}, '{}')",
battlenetAccountId, packet.TransactionID, packet.DecorID, response.DecorGUID.ToString());
transaction->PAppend(
"INSERT INTO account_housing_decor (BattlenetAccountId, DecorId, Quantity) VALUES ({}, {}, 1) "
"ON DUPLICATE KEY UPDATE Quantity = Quantity + 1",
battlenetAccountId, packet.DecorID);
transaction->PAppend(
"INSERT INTO housing_decor_instance "
"(BattlenetAccountId, DecorGuid, DecorID, HouseGuid, PlacementStatus, SourceType, SourceValue) "
"VALUES ({}, '{}', {}, '', 0, 0, '')",
battlenetAccountId, response.DecorGUID.ToString(), packet.DecorID);
CharacterDatabase.DirectCommitTransaction(transaction);
}
QueryResult committed = CharacterDatabase.PQuery(
"SELECT 1 FROM housing_deferred_decor_redemption r "
"INNER JOIN housing_decor_instance i ON i.BattlenetAccountId = r.BattlenetAccountId "
"AND i.DecorGuid = r.DecorGuid AND i.DecorID = r.DecorID "
"INNER JOIN account_housing_decor a ON a.BattlenetAccountId = r.BattlenetAccountId "
"AND a.DecorId = r.DecorID AND a.Quantity > 0 "
"WHERE r.BattlenetAccountId = {} AND r.TransactionID = {} "
"AND r.DecorID = {} AND r.DecorGuid = '{}' LIMIT 1",
battlenetAccountId, packet.TransactionID, packet.DecorID, response.DecorGUID.ToString());
if (committed)
response.Result = static_cast<uint8>(Result::Success);
else
TC_LOG_ERROR("housing", "Deferred decor redemption verification failed for Battle.net account {} transaction {}",
battlenetAccountId, packet.TransactionID);
session->SendPacket(response.Write());
}
}