Core/Housing: stage 39 create deferred decor GUIDs in retail layout
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user