feat(core): implement 31-bit safe petition_id for improved database integrity (#22774)

This commit is contained in:
Yehonal
2025-09-03 03:31:09 +02:00
committed by GitHub
parent 52f79d8dd7
commit a575101751
6 changed files with 130 additions and 30 deletions

View File

@@ -0,0 +1,14 @@
-- Add petition_id column to petition table
ALTER TABLE `petition` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`;
-- Populate petition_id based on petitionguid
UPDATE `petition` SET `petition_id` = CASE WHEN `petitionguid` <= 2147483647 THEN `petitionguid` ELSE `petitionguid` - 2147483648 END WHERE `petition_id` = 0;
-- Add index on petition_id
ALTER TABLE `petition` ADD INDEX `idx_petition_id` (`petition_id`);
-- Add petition_id column to petition_sign table
ALTER TABLE `petition_sign` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`;
-- Populate petition_id in petition_sign from petition table
UPDATE `petition_sign` AS `ps` JOIN `petition` AS `p` ON `p`.`petitionguid` = `ps`.`petitionguid` SET `ps`.`petition_id` = `p`.`petition_id` WHERE `ps`.`petition_id` = 0;
-- Add index on petition_id and playerguid in petition_sign
ALTER TABLE `petition_sign` ADD INDEX `idx_petition_id_player` (`petition_id`, `playerguid`);
-- Update enchantments in item_instance with petition_id prefix
UPDATE `item_instance` AS `ii` JOIN `petition` AS `p` ON `p`.`petitionguid` = `ii`.`guid` SET `ii`.`enchantments` = CONCAT(`p`.`petition_id`, SUBSTRING(`ii`.`enchantments`, LOCATE(' ', `ii`.`enchantments`))) WHERE `ii`.`enchantments` IS NOT NULL AND `ii`.`enchantments` <> '';

View File

@@ -353,8 +353,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS, "UPDATE characters SET at_login = at_login | ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_BUG_REPORT, "INSERT INTO bugreport (type, content) VALUES(?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petitionguid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petitionguid, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petition_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petition_id, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_GROUP, "INSERT INTO `groups` (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raidDifficulty, masterLooterGuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_GROUP_MEMBER, "REPLACE INTO group_member (guid, memberGuid, memberFlags, subgroup, roles) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC);
@@ -456,9 +456,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_CHAR_GIFT, "INSERT INTO character_gifts (guid, item_guid, entry, flags) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_INSTANCE_BY_INSTANCE, "DELETE FROM instance WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID, "DELETE FROM petition_sign WHERE petitionguid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (petition_id, ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PETITION_BY_ID, "DELETE FROM petition WHERE petition_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_ID, "DELETE FROM petition_sign WHERE petition_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_DECLINED_NAME, "INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_RACE, "UPDATE characters SET race = ? WHERE guid = ?", CONNECTION_ASYNC);

View File

@@ -381,8 +381,8 @@ enum CharacterDatabaseStatements : uint32
CHAR_DEL_INSTANCE_BY_INSTANCE,
CHAR_DEL_MAIL_ITEM_BY_ID,
CHAR_INS_PETITION,
CHAR_DEL_PETITION_BY_GUID,
CHAR_DEL_PETITION_SIGNATURE_BY_GUID,
CHAR_DEL_PETITION_BY_ID,
CHAR_DEL_PETITION_SIGNATURE_BY_ID,
CHAR_DEL_CHAR_DECLINED_NAME,
CHAR_INS_CHAR_DECLINED_NAME,
CHAR_UPD_CHAR_RACE,

View File

@@ -183,7 +183,9 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData)
if (!charter)
return;
charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, charter->GetGUID().GetCounter());
// Use a 31-bit safe petition id instead of the raw item guid
uint32 petitionId = sPetitionMgr->GeneratePetitionId();
charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, petitionId);
// ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id
// ITEM_FIELD_ENCHANTMENT_1_1+1 is current signatures count (showed on item)
charter->SetState(ITEM_CHANGED, _player);
@@ -211,16 +213,18 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData)
// xinef: petition pointer is invalid from now on
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION);
stmt->SetData(0, _player->GetGUID().GetCounter());
stmt->SetData(1, charter->GetGUID().GetCounter());
stmt->SetData(2, name);
stmt->SetData(3, uint8(type));
// petition_id, ownerguid, petitionguid(item guid), name, type
stmt->SetData(0, petitionId);
stmt->SetData(1, _player->GetGUID().GetCounter());
stmt->SetData(2, charter->GetGUID().GetCounter());
stmt->SetData(3, name);
stmt->SetData(4, uint8(type));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
// xinef: fill petition store
sPetitionMgr->AddPetition(charter->GetGUID(), _player->GetGUID(), name, uint8(type));
// xinef: fill petition store (include petitionId)
sPetitionMgr->AddPetition(charter->GetGUID(), _player->GetGUID(), name, uint8(type), petitionId);
}
void WorldSession::HandlePetitionShowSignOpcode(WorldPacket& recvData)
@@ -249,7 +253,7 @@ void WorldSession::HandlePetitionShowSignOpcode(WorldPacket& recvData)
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8 + 8 + 4 + 1 + signs * 12));
data << petitionguid; // petition guid
data << _player->GetGUID(); // owner guid
data << uint32(petitionguid.GetCounter()); // guild guid
data << uint32(petition->petitionId); // guild/team id (31-bit safe)
data << uint8(signs); // sign's count
if (signs)
@@ -286,7 +290,7 @@ void WorldSession::SendPetitionQueryOpcode(ObjectGuid petitionguid)
uint8 type = petition->petitionType;
WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4 + 8 + petition->petitionName.size() + 1 + 1 + 4 * 12 + 2 + 10));
data << uint32(petitionguid.GetCounter()); // guild/team guid (in Trinity always same as petition low guid
data << uint32(petition->petitionId); // guild/team id (was item low guid)
data << petition->ownerGuid; // charter owner guid
data << petition->petitionName; // name (guild/arena team)
data << uint8(0); // some string
@@ -373,7 +377,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket& recvData)
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME);
stmt->SetData(0, newName);
stmt->SetData(1, petitionGuid.GetCounter());
stmt->SetData(1, petition->petitionId);
CharacterDatabase.Execute(stmt);
@@ -497,7 +501,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE);
stmt->SetData(0, petition->ownerGuid.GetCounter());
stmt->SetData(1, petitionGuid.GetCounter());
stmt->SetData(1, petition->petitionId);
stmt->SetData(2, playerGuid.GetCounter());
stmt->SetData(3, GetAccountId());
@@ -625,7 +629,7 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData)
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8 + 8 + 4 + signs + signs * 12));
data << petitionguid; // petition guid
data << _player->GetGUID(); // owner guid
data << uint32(petitionguid.GetCounter()); // guild guid
data << uint32(petition->petitionId); // guild/team id (31-bit safe)
data << uint8(signs); // sign's count
if (signs)
@@ -790,12 +794,12 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket& recvData)
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID);
stmt->SetData(0, petitionGuid.GetCounter());
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_ID);
stmt->SetData(0, petition->petitionId);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID);
stmt->SetData(0, petitionGuid.GetCounter());
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_ID);
stmt->SetData(0, petition->petitionId);
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);

View File

@@ -41,8 +41,9 @@ void PetitionMgr::LoadPetitions()
{
uint32 oldMSTime = getMSTime();
PetitionStore.clear();
PetitionIdToItemGuid.clear();
QueryResult result = CharacterDatabase.Query("SELECT ownerguid, petitionguid, name, type FROM petition");
QueryResult result = CharacterDatabase.Query("SELECT ownerguid, petitionguid, name, type, petition_id FROM petition");
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 Petitions!");
@@ -51,13 +52,24 @@ void PetitionMgr::LoadPetitions()
}
uint32 count = 0;
uint32 maxId = 0;
do
{
Field* fields = result->Fetch();
AddPetition(ObjectGuid::Create<HighGuid::Item>(fields[1].Get<uint32>()), ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()), fields[2].Get<std::string>(), fields[3].Get<uint8>());
uint32 itemLow = fields[1].Get<uint32>();
uint32 petitionId = fields[4].Get<uint32>();
ObjectGuid itemGuid = ObjectGuid::Create<HighGuid::Item>(itemLow);
ObjectGuid ownerGuid = ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>());
AddPetition(itemGuid, ownerGuid, fields[2].Get<std::string>(), fields[3].Get<uint8>(), petitionId);
PetitionIdToItemGuid[petitionId] = itemGuid;
if (petitionId > maxId)
maxId = petitionId;
++count;
} while (result->NextRow());
// initialize next id (keep within 31-bit safe range)
_nextPetitionId = std::min<uint32>(std::max<uint32>(maxId + 1, 1), 0x7FFFFFFFu);
LOG_INFO("server.loading", ">> Loaded {} Petitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
@@ -67,7 +79,7 @@ void PetitionMgr::LoadSignatures()
uint32 oldMSTime = getMSTime();
SignatureStore.clear();
QueryResult result = CharacterDatabase.Query("SELECT petitionguid, playerguid, player_account FROM petition_sign");
QueryResult result = CharacterDatabase.Query("SELECT petition_id, playerguid, player_account FROM petition_sign");
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 Petition signs!");
@@ -79,7 +91,11 @@ void PetitionMgr::LoadSignatures()
do
{
Field* fields = result->Fetch();
AddSignature(ObjectGuid::Create<HighGuid::Item>(fields[0].Get<uint32>()), fields[2].Get<uint32>(), ObjectGuid::Create<HighGuid::Player>(fields[1].Get<uint32>()));
uint32 petitionId = fields[0].Get<uint32>();
auto it = PetitionIdToItemGuid.find(petitionId);
if (it == PetitionIdToItemGuid.end())
continue; // orphan signature, skip
AddSignature(it->second, fields[2].Get<uint32>(), ObjectGuid::Create<HighGuid::Player>(fields[1].Get<uint32>()));
++count;
} while (result->NextRow());
@@ -87,10 +103,11 @@ void PetitionMgr::LoadSignatures()
LOG_INFO("server.loading", " ");
}
void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type)
void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type, uint32 petitionId)
{
Petition& p = PetitionStore[petitionGUID];
p.petitionGuid = petitionGUID;
p.petitionId = petitionId;
p.ownerGuid = ownerGuid;
p.petitionName = name;
p.petitionType = type;
@@ -102,7 +119,12 @@ void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std
void PetitionMgr::RemovePetition(ObjectGuid petitionGUID)
{
PetitionStore.erase(petitionGUID);
auto it = PetitionStore.find(petitionGUID);
if (it != PetitionStore.end())
{
PetitionIdToItemGuid.erase(it->second.petitionId);
PetitionStore.erase(it);
}
// remove signatures
SignatureStore.erase(petitionGUID);
@@ -143,6 +165,15 @@ Petition const* PetitionMgr::GetPetition(ObjectGuid petitionGUID) const
return nullptr;
}
Petition const* PetitionMgr::GetPetitionById(uint32 petitionId) const
{
auto it = PetitionIdToItemGuid.find(petitionId);
if (it == PetitionIdToItemGuid.end())
return nullptr;
return GetPetition(it->second);
}
Petition const* PetitionMgr::GetPetitionByOwnerWithType(ObjectGuid ownerGuid, uint8 type) const
{
for (PetitionContainer::const_iterator itr = PetitionStore.begin(); itr != PetitionStore.end(); ++itr)
@@ -189,3 +220,37 @@ void PetitionMgr::RemoveSignaturesByPlayerAndType(ObjectGuid playerGuid, uint8 t
itr->second.signatureMap.erase(signItr);
}
}
uint32 PetitionMgr::GeneratePetitionId()
{
// ensure 31-bit range and avoid collisions with already loaded petitions
if (_nextPetitionId == 0 || _nextPetitionId >= 0x7FFFFFFF)
_nextPetitionId = 1;
// find first free id
while (PetitionIdToItemGuid.count(_nextPetitionId))
{
++_nextPetitionId;
if (_nextPetitionId >= 0x7FFFFFFF)
_nextPetitionId = 1;
}
uint32 id = _nextPetitionId++;
if (_nextPetitionId >= 0x7FFFFFFF)
_nextPetitionId = 1;
return id;
}
uint32 PetitionMgr::GetPetitionIdByItemGuid(ObjectGuid petitionItemGuid) const
{
Petition const* p = GetPetition(petitionItemGuid);
return p ? p->petitionId : 0;
}
ObjectGuid PetitionMgr::GetItemGuidByPetitionId(uint32 petitionId) const
{
auto it = PetitionIdToItemGuid.find(petitionId);
if (it == PetitionIdToItemGuid.end())
return ObjectGuid::Empty;
return it->second;
}

View File

@@ -20,6 +20,7 @@
#include "ObjectGuid.h"
#include <unordered_map>
#include <map>
#define CHARTER_DISPLAY_ID 16161
@@ -37,14 +38,21 @@ typedef std::map<ObjectGuid, uint32> SignatureMap;
struct Petition
{
// Item GUID of the charter item (used to find the item in inventory)
ObjectGuid petitionGuid;
// New 31-bit safe petition identifier used in packets/DB relations
uint32 petitionId;
// Owner character GUID
ObjectGuid ownerGuid;
// Petition type (guild / arena)
uint8 petitionType;
// Name associated with the petition (guild/arena name)
std::string petitionName;
};
struct Signatures
{
// Keep keying by item-guid for backward compatibility in code paths
ObjectGuid petitionGuid;
SignatureMap signatureMap;
};
@@ -65,10 +73,11 @@ public:
void LoadSignatures();
// Petitions
void AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type);
void AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type, uint32 petitionId);
void RemovePetition(ObjectGuid petitionGUID);
void RemovePetitionByOwnerAndType(ObjectGuid ownerGuid, uint8 type);
Petition const* GetPetition(ObjectGuid petitionGUID) const;
Petition const* GetPetitionById(uint32 petitionId) const;
Petition const* GetPetitionByOwnerWithType(ObjectGuid ownerGuid, uint8 type) const;
PetitionContainer* GetPetitionStore() { return &PetitionStore; }
@@ -79,9 +88,17 @@ public:
Signatures const* GetSignature(ObjectGuid petitionGUID) const;
SignatureContainer* GetSignatureStore() { return &SignatureStore; }
uint32 GeneratePetitionId();
uint32 GetPetitionIdByItemGuid(ObjectGuid petitionItemGuid) const;
ObjectGuid GetItemGuidByPetitionId(uint32 petitionId) const;
protected:
PetitionContainer PetitionStore;
SignatureContainer SignatureStore;
// Mapping id -> item-guid to support DB-id lookups
std::unordered_map<uint32, ObjectGuid> PetitionIdToItemGuid;
// Next petition id (kept < 2^31)
uint32 _nextPetitionId = 1;
};
#define sPetitionMgr PetitionMgr::instance()