12.1 pvp updates + extras

This commit is contained in:
luis
2026-09-04 08:40:23 -03:00
parent 90a1491d66
commit 629901268d
19 changed files with 483 additions and 31 deletions
+3 -1
View File
@@ -236,7 +236,9 @@ enum class BattlegroundQueueIdType : uint8
Arena = 1, Arena = 1,
Wargame = 2, Wargame = 2,
Cheat = 3, Cheat = 3,
ArenaSkirmish = 4 ArenaSkirmish = 4,
RatedBattlegroundBlitz = 9,
RatedSoloShuffle = 10
}; };
enum class BattlegroundPointCaptureStatus enum class BattlegroundPointCaptureStatus
@@ -207,6 +207,10 @@ void BattlegroundMgr::BuildBattlegroundStatusQueued(WorldPackets::Battleground::
battlefieldStatus->SuspendedQueue = false; battlefieldStatus->SuspendedQueue = false;
battlefieldStatus->EligibleForMatchmaking = true; battlefieldStatus->EligibleForMatchmaking = true;
battlefieldStatus->WaitTime = GetMSTimeDiffToNow(joinTime); battlefieldStatus->WaitTime = GetMSTimeDiffToNow(joinTime);
//WowCommunity
// Retail populates this: the sniffed reply to a Battleground Blitz join carried SpecSelected 257
// (Holy Priest) for a healer-flagged queuer. It was previously left at 0 for every queue.
battlefieldStatus->SpecSelected = AsUnderlyingType(player->GetPrimarySpecialization());
} }
void BattlegroundMgr::BuildBattlegroundStatusFailed(WorldPackets::Battleground::BattlefieldStatusFailed* battlefieldStatus, BattlegroundQueueTypeId queueId, Player const* player, uint32 ticketId, GroupJoinBattlegroundResult result, ObjectGuid const* errorGuid /*= nullptr*/) void BattlegroundMgr::BuildBattlegroundStatusFailed(WorldPackets::Battleground::BattlefieldStatusFailed* battlefieldStatus, BattlegroundQueueTypeId queueId, Player const* player, uint32 ticketId, GroupJoinBattlegroundResult result, ObjectGuid const* errorGuid /*= nullptr*/)
@@ -424,7 +428,11 @@ void BattlegroundMgr::LoadBattlegroundTemplates()
bgTemplate.BattlemasterEntry = bl; bgTemplate.BattlemasterEntry = bl;
bgTemplate.MapIDs = std::move(mapsByBattleground[bgTypeId]); bgTemplate.MapIDs = std::move(mapsByBattleground[bgTypeId]);
if (bgTemplate.Id != BATTLEGROUND_AA && !IsRandomBattleground(bgTemplate.Id)) // Aggregate templates (All Arenas, the random-BG pools, Battleground Blitz) never spawn anyone
// themselves: CreateNewBattleground resolves their BattlemasterListXMap maps back to the individual
// single-map templates, which carry the real start locations. Requiring a WorldSafeLocs id here would
// mean inventing one that is never used, and a wrong id silently drops the whole template.
if (bgTemplate.Id != BATTLEGROUND_AA && bgTemplate.Id != BATTLEGROUND_BLITZ && !IsRandomBattleground(bgTemplate.Id))
{ {
uint32 startId = fields[1].GetUInt32(); uint32 startId = fields[1].GetUInt32();
if (WorldSafeLocsEntry const* start = sObjectMgr->GetWorldSafeLoc(startId)) if (WorldSafeLocsEntry const* start = sObjectMgr->GetWorldSafeLoc(startId))
@@ -574,6 +582,17 @@ bool BattlegroundMgr::IsValidQueueId(BattlegroundQueueTypeId bgQueueTypeId)
if (bgQueueTypeId.TeamSize != ARENA_TYPE_3v3) if (bgQueueTypeId.TeamSize != ARENA_TYPE_3v3)
return false; return false;
break; break;
case BattlegroundQueueIdType::RatedBattlegroundBlitz:
// BattlemasterList 1101 ("Battleground Blitz") carries PvpType 0, so GetType() is Battleground and
// not Arena, even though the mode is rated. Rated must be set and TeamSize must be 0 - both read
// straight off the packed QueueID retail sent back to the client (0x1F1000000019044D).
if (battlemasterList->GetType() != BattlemasterType::Battleground)
return false;
if (!bgQueueTypeId.Rated)
return false;
if (bgQueueTypeId.TeamSize)
return false;
break;
default: default:
return false; return false;
} }
@@ -30,6 +30,10 @@
#include "Player.h" #include "Player.h"
#include "World.h" #include "World.h"
//WowCommunity
#include "LFG.h"
//WowCommunity
/*********************************************************/ /*********************************************************/
/*** BATTLEGROUND QUEUE SYSTEM ***/ /*** BATTLEGROUND QUEUE SYSTEM ***/
/*********************************************************/ /*********************************************************/
@@ -130,7 +134,7 @@ bool BattlegroundQueue::SelectionPool::AddGroup(GroupQueueInfo* ginfo, uint32 de
/*********************************************************/ /*********************************************************/
// add group or player (grp == NULL) to bg queue with the given leader and bg specifications // add group or player (grp == NULL) to bg queue with the given leader and bg specifications
GroupQueueInfo* BattlegroundQueue::AddGroup(Player const* leader, Group const* group, Team team, PVPDifficultyEntry const* bracketEntry, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating) GroupQueueInfo* BattlegroundQueue::AddGroup(Player const* leader, Group const* group, Team team, PVPDifficultyEntry const* bracketEntry, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating, uint8 roles)
{ {
BattlegroundBracketId bracketId = bracketEntry->GetBracketId(); BattlegroundBracketId bracketId = bracketEntry->GetBracketId();
@@ -144,6 +148,7 @@ GroupQueueInfo* BattlegroundQueue::AddGroup(Player const* leader, Group const* g
ginfo->ArenaMatchmakerRating = MatchmakerRating; ginfo->ArenaMatchmakerRating = MatchmakerRating;
ginfo->OpponentsTeamRating = 0; ginfo->OpponentsTeamRating = 0;
ginfo->OpponentsMatchmakerRating = 0; ginfo->OpponentsMatchmakerRating = 0;
ginfo->Roles = roles;
ginfo->Players.clear(); ginfo->Players.clear();
@@ -1037,6 +1042,33 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundBra
arena->StartBattleground(); arena->StartBattleground();
} }
} }
else if (BattlegroundQueueIdType(m_queueId.Type) == BattlegroundQueueIdType::RatedBattlegroundBlitz)
{
// Rated + non-arena previously fell through here doing nothing at all, which is why a rated
// battleground queue could never pop.
uint32 const perTeam = sBattlegroundMgr->isTesting() ? 1 : MaxPlayersPerTeam;
uint32 const healers = sBattlegroundMgr->isTesting() ? 0 : sWorld->getIntConfig(CONFIG_BATTLEGROUND_BLITZ_HEALERS_PER_TEAM);
if (CheckSoloQueueMatch(bracket_id, perTeam, healers))
{
Battleground* bg2 = sBattlegroundMgr->CreateNewBattleground(m_queueId, bracket_id);
if (!bg2)
{
TC_LOG_ERROR("bg.battleground", "BattlegroundQueue::Update - Cannot create Battleground Blitz instance: {}", m_queueId.BattlemasterListId);
return;
}
for (uint32 i = 0; i < PVP_TEAMS_COUNT; ++i)
for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr)
InviteGroupToBG((*citr), bg2, (*citr)->Team);
TC_LOG_DEBUG("bg.battleground", "Starting Battleground Blitz match ({} per team, {} healers per team)", perTeam, healers);
bg2->StartBattleground();
m_SelectionPools[TEAM_ALLIANCE].Init();
m_SelectionPools[TEAM_HORDE].Init();
}
}
} }
/*********************************************************/ /*********************************************************/
@@ -1144,3 +1176,119 @@ bool BattlegroundQueue::AddWargameSide(Player* leader, Group* group, Battlegroun
return InviteGroupToBG(ginfo, bg, team); return InviteGroupToBG(ginfo, bg, team);
} }
// Solo-queue matchmaker for Battleground Blitz (and any future rated solo-queue mode).
//
// Differs from CheckNormalMatch/CheckPremadeMatch in two ways:
// - it draws from the RATED lists. AddGroup only pushes an entry into BG_QUEUE_NORMAL_* when the queue is
// unrated (`if (!m_queueId.Rated && !isPremade) index += PVP_TEAMS_COUNT`), so for a rated queue every
// entry - solo or duo - lives in BG_QUEUE_PREMADE_ALLIANCE / _HORDE.
// - it is faction-blind. Blitz matches cross-faction, so entries are dealt into whichever pool needs them
// and their Team is rewritten, exactly as CheckSkirmishForSameFaction does for same-faction skirmishes.
//
// healersPerTeam is a configured quota, not a client-derived constant: the wire tells us the queuer's role
// mask but nothing about how retail balances a Blitz match. 0 disables role balancing entirely.
bool BattlegroundQueue::CheckSoloQueueMatch(BattlegroundBracketId bracket_id, uint32 playersPerTeam, uint32 healersPerTeam)
{
uint8 const ratedLists[PVP_TEAMS_COUNT] = { uint8(BG_QUEUE_PREMADE_ALLIANCE), uint8(BG_QUEUE_PREMADE_HORDE) };
std::vector<GroupQueueInfo*> healers;
std::vector<GroupQueueInfo*> others;
uint32 totalPlayers = 0;
for (uint8 list : ratedLists)
{
for (GroupQueueInfo* ginfo : m_QueuedGroups[bracket_id][list])
{
if (ginfo->IsInvitedToBGInstanceGUID)
continue;
totalPlayers += uint32(ginfo->Players.size());
// Only the QUEUER's role mask exists on the wire, so a duo contributes one known role and one
// unknown one. Classify the whole entry by the mask we actually received rather than inventing a
// role for the partner.
if (ginfo->Roles & lfg::PLAYER_ROLE_HEALER)
healers.push_back(ginfo);
else
others.push_back(ginfo);
}
}
if (totalPlayers < playersPerTeam * PVP_TEAMS_COUNT)
return false;
if (healers.size() < size_t(healersPerTeam) * PVP_TEAMS_COUNT)
return false;
for (uint32 i = 0; i < PVP_TEAMS_COUNT; ++i)
m_SelectionPools[i].Init();
// Healers first so the quota is guaranteed, then everyone else fills the remaining slots.
size_t healerIdx = 0;
for (uint32 team = 0; team < PVP_TEAMS_COUNT; ++team)
for (uint32 n = 0; n < healersPerTeam && healerIdx < healers.size(); ++n)
if (!m_SelectionPools[team].AddGroup(healers[healerIdx++], playersPerTeam))
break;
auto fillFrom = [&](std::vector<GroupQueueInfo*> const& source, size_t from)
{
for (size_t i = from; i < source.size(); ++i)
{
GroupQueueInfo* ginfo = source[i];
bool placed = false;
for (uint32 team = 0; team < PVP_TEAMS_COUNT && !placed; ++team)
if (m_SelectionPools[team].GetPlayerCount() < playersPerTeam)
placed = m_SelectionPools[team].AddGroup(ginfo, playersPerTeam);
if (!placed && m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= playersPerTeam
&& m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= playersPerTeam)
return;
}
};
fillFrom(others, 0);
fillFrom(healers, healerIdx);
if (m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() != playersPerTeam
|| m_SelectionPools[TEAM_HORDE].GetPlayerCount() != playersPerTeam)
{
// Leave no half-built selection behind for the next caller.
for (uint32 i = 0; i < PVP_TEAMS_COUNT; ++i)
m_SelectionPools[i].Init();
return false;
}
// Both pools are full. Re-home every entry that ended up on the other faction's side: set its Team, push it
// into the destination rated list and erase it from the source list. Skipping the list move makes
// RemovePlayer fail to find the entry and leaks queue slots - the same idiom as CheckSkirmishForSameFaction.
for (uint32 i = 0; i < PVP_TEAMS_COUNT; ++i)
{
TeamId const poolTeam = TeamId(TEAM_ALLIANCE + i);
Team const poolTeamId = poolTeam == TEAM_ALLIANCE ? ALLIANCE : HORDE;
for (GroupQueueInfo* ginfo : m_SelectionPools[poolTeam].SelectedGroups)
{
if (ginfo->Team == poolTeamId)
continue;
uint8 const sourceList = ginfo->Team == ALLIANCE ? uint8(BG_QUEUE_PREMADE_ALLIANCE) : uint8(BG_QUEUE_PREMADE_HORDE);
uint8 const destList = poolTeam == TEAM_ALLIANCE ? uint8(BG_QUEUE_PREMADE_ALLIANCE) : uint8(BG_QUEUE_PREMADE_HORDE);
ginfo->Team = poolTeamId;
m_QueuedGroups[bracket_id][destList].push_front(ginfo);
for (GroupsQueueType::iterator itr = m_QueuedGroups[bracket_id][sourceList].begin();
itr != m_QueuedGroups[bracket_id][sourceList].end(); ++itr)
{
if (*itr == ginfo)
{
m_QueuedGroups[bracket_id][sourceList].erase(itr);
break;
}
}
}
}
return true;
}
@@ -47,6 +47,11 @@ struct GroupQueueInfo // stores informatio
uint32 ArenaMatchmakerRating; // if rated match, inited to the rating of the team uint32 ArenaMatchmakerRating; // if rated match, inited to the rating of the team
uint32 OpponentsTeamRating; // for rated arena matches uint32 OpponentsTeamRating; // for rated arena matches
uint32 OpponentsMatchmakerRating; // for rated arena matches uint32 OpponentsMatchmakerRating; // for rated arena matches
uint8 Roles; // lfg::PLAYER_ROLE_* mask from the join packet.
// Solo-queue modes (Battleground Blitz) match on this.
// For a group this is the QUEUER's mask only: the wire
// carries no per-member roles, so the other members'
// roles are genuinely unknown, not merely unread.
}; };
enum BattlegroundQueueGroupTypes enum BattlegroundQueueGroupTypes
@@ -79,7 +84,13 @@ class TC_GAME_API BattlegroundQueue
bool CheckPremadeMatch(BattlegroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); bool CheckPremadeMatch(BattlegroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam);
bool CheckNormalMatch(BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers); bool CheckNormalMatch(BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers);
bool CheckSkirmishForSameFaction(BattlegroundBracketId bracket_id, uint32 minPlayersPerTeam); bool CheckSkirmishForSameFaction(BattlegroundBracketId bracket_id, uint32 minPlayersPerTeam);
GroupQueueInfo* AddGroup(Player const* leader, Group const* group, Team team, PVPDifficultyEntry const* bracketEntry, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating); // Solo-queue matchmaker for Battleground Blitz: fills both selection pools from the rated (premade-indexed)
// lists, honouring a healer quota per team. Returns false and leaves the pools untouched when the queue
// cannot yet field two full role-valid teams.
bool CheckSoloQueueMatch(BattlegroundBracketId bracket_id, uint32 playersPerTeam, uint32 healersPerTeam);
GroupQueueInfo* AddGroup(Player const* leader, Group const* group, Team team, PVPDifficultyEntry const* bracketEntry, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating, uint8 roles = 0);
// War games bypass matchmaking: both premade groups are known up front, so this queues one side onto a
// forced team and immediately sends its members the enter-confirmation for the given battleground.
void RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount); void RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount);
//WowCommunity //WowCommunity
// War games bypass matchmaking: both premade groups are known up front, so this queues one side onto a // War games bypass matchmaking: both premade groups are known up front, so this queues one side onto a
+105 -21
View File
@@ -197,7 +197,12 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPackets::Battleground::Batt
return; return;
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, nullptr, getQueueTeam(), bracketEntry, false, isPremade, 0); // The parameter order is (?, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating, uint8 roles).
// This call used to read (?, false, isPremade, 0): isPremade was landing in ArenaRating while the real
// isPremade parameter was hardcoded false. Because AddGroup buckets with
// `if (!m_queueId.Rated && !isPremade) index += PVP_TEAMS_COUNT`, every unrated group was filed under
// BG_QUEUE_NORMAL_* and CheckPremadeMatch could never fire.
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, nullptr, getQueueTeam(), bracketEntry, isPremade, 0, 0, battlemasterJoin.Roles);
if (ginfo) if (ginfo)
{ {
auto itr = bgQueue.m_QueuedPlayers.find(_player->GetGUID()); auto itr = bgQueue.m_QueuedPlayers.find(_player->GetGUID());
@@ -231,7 +236,8 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPackets::Battleground::Batt
if (!err) if (!err)
{ {
TC_LOG_DEBUG("bg.battleground", "Battleground: the following players are joining as group:"); TC_LOG_DEBUG("bg.battleground", "Battleground: the following players are joining as group:");
ginfo = bgQueue.AddGroup(_player, grp, getQueueTeam(), bracketEntry, false, isPremade, 0); // Same argument-order fix as the solo path above.
ginfo = bgQueue.AddGroup(_player, grp, getQueueTeam(), bracketEntry, isPremade, 0, 0, battlemasterJoin.Roles);
if (ginfo) if (ginfo)
{ {
auto itr = bgQueue.m_QueuedPlayers.find(_player->GetGUID()); auto itr = bgQueue.m_QueuedPlayers.find(_player->GetGUID());
@@ -610,7 +616,7 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPackets::Battleground::Battl
{ {
TC_LOG_DEBUG("bg.battleground", "Battleground: arena team id {}, leader {} queued with matchmaker rating {} for type {}", _player->GetArenaTeamId(packet.TeamSizeIndex), _player->GetName(), matchmakerRating, arenatype); TC_LOG_DEBUG("bg.battleground", "Battleground: arena team id {}, leader {} queued with matchmaker rating {} for type {}", _player->GetArenaTeamId(packet.TeamSizeIndex), _player->GetName(), matchmakerRating, arenatype);
ginfo = bgQueue.AddGroup(_player, grp, Team(_player->GetTeam()), bracketEntry, false, arenaRating, matchmakerRating); ginfo = bgQueue.AddGroup(_player, grp, Team(_player->GetTeam()), bracketEntry, false, arenaRating, matchmakerRating, packet.Roles);
if (ginfo) if (ginfo)
{ {
auto itr = bgQueue.m_QueuedPlayers.find(_player->GetGUID()); auto itr = bgQueue.m_QueuedPlayers.find(_player->GetGUID());
@@ -718,49 +724,127 @@ void WorldSession::HandleBattlemasterJoinBrawl(WorldPackets::Battleground::Battl
void WorldSession::HandleBattlemasterJoinRatedBgBlitz(WorldPackets::Battleground::BattlemasterJoinRatedBgBlitz& packet) void WorldSession::HandleBattlemasterJoinRatedBgBlitz(WorldPackets::Battleground::BattlemasterJoinRatedBgBlitz& packet)
{ {
if (_player->InBattleground()) BattlegroundQueueTypeId bgQueueTypeId =
BattlegroundMgr::BGQueueTypeId(BATTLEGROUND_BLITZ, BattlegroundQueueIdType::RatedBattlegroundBlitz, true, 0);
if (!BattlegroundMgr::IsValidQueueId(bgQueueTypeId))
{
TC_LOG_ERROR("network", "Battleground Blitz: queue id rejected by IsValidQueueId - BattlemasterList {} is missing from the client DB2 the server loaded.",
uint32(BATTLEGROUND_BLITZ));
return; return;
}
BattlegroundTypeId blitzBgTypeId = BATTLEGROUND_RB; BattlemasterListEntry const* battlemasterListEntry = sBattlemasterListStore.AssertEntry(bgQueueTypeId.BattlemasterListId);
BattlegroundTemplate const* bgTemplate = sBattlegroundMgr->GetBattlegroundTemplateByTypeId(blitzBgTypeId);
if (!bgTemplate)
return;
BattlemasterListEntry const* battlemasterListEntry = sBattlemasterListStore.AssertEntry(blitzBgTypeId); if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgQueueTypeId.BattlemasterListId, nullptr) || battlemasterListEntry->GetFlags().HasFlag(BattlemasterListFlags::InternalOnly))
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, blitzBgTypeId, nullptr) || battlemasterListEntry->GetFlags().HasFlag(BattlemasterListFlags::InternalOnly))
{ {
ChatHandler(this).PSendSysMessage(LANG_BG_DISABLED); ChatHandler(this).PSendSysMessage(LANG_BG_DISABLED);
return; return;
} }
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(blitzBgTypeId, BattlegroundQueueIdType::Battleground, true, 0); if (_player->InBattleground())
return;
BattlegroundTemplate const* bgTemplate = sBattlegroundMgr->GetBattlegroundTemplateByTypeId(BATTLEGROUND_BLITZ);
if (!bgTemplate)
{
TC_LOG_ERROR("bg.battleground", "Battleground Blitz: no battleground_template row for {} - apply the Blitz world migration.", uint32(BATTLEGROUND_BLITZ));
return;
}
PVPDifficultyEntry const* bracketEntry = DB2Manager::GetBattlegroundBracketByLevel(bgTemplate->MapIDs.front(), _player->GetLevel()); PVPDifficultyEntry const* bracketEntry = DB2Manager::GetBattlegroundBracketByLevel(bgTemplate->MapIDs.front(), _player->GetLevel());
if (!bracketEntry) if (!bracketEntry)
return; return;
if (_player->IsDeserter()) auto sendFailed = [&](GroupJoinBattlegroundResult result)
{
WorldPackets::Battleground::BattlefieldStatusFailed battlefieldStatus;
BattlegroundMgr::BuildBattlegroundStatusFailed(&battlefieldStatus, bgQueueTypeId, _player, 0, result);
SendPacket(battlefieldStatus.Write());
};
// The retail client refuses to send this opcode with no role selected, so a zero mask means either a
// modified client or a UI state we do not model. Either way the matchmaker cannot place the player.
if (!packet.Roles)
{ {
WorldPackets::Battleground::BattlefieldStatusFailed battlefieldStatus; sendFailed(ERR_BATTLEGROUND_JOIN_FAILED);
BattlegroundMgr::BuildBattlegroundStatusFailed(&battlefieldStatus, bgQueueTypeId, _player, 0, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
SendPacket(battlefieldStatus.Write());
return; return;
} }
TC_LOG_DEBUG("bg.battleground", "Battleground: player joined queue for rated bg blitz (Roles: {}) queue {{ BattlemasterListId: {}, Type: {} }}, {}", Group const* grp = _player->GetGroup();
packet.Roles, bgQueueTypeId.BattlemasterListId, uint32(bgQueueTypeId.Type), _player->GetGUID().ToString()); if (grp)
{
if (grp->GetLeaderGUID() != _player->GetGUID())
return;
GroupQueueInfo* ginfo = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).AddGroup(_player, nullptr, Team(_player->GetTeam()), bracketEntry, true, 0, 0); // BattlemasterList 1101 caps the queueing party at MaxGroupSize (2 on retail - solo or duo).
if (ginfo) if (grp->GetMembersCount() > battlemasterListEntry->MaxGroupSize)
{
sendFailed(ERR_BATTLEGROUND_JOIN_FAILED);
return;
}
}
if (GetPlayer()->isUsingLfg())
{
sendFailed(ERR_LFG_CANT_USE_BATTLEGROUND);
return;
}
if (!_player->CanJoinToBattleground(bgTemplate))
{
sendFailed(ERR_BATTLEGROUND_JOIN_TIMED_OUT);
return;
}
if (_player->IsDeserter())
{
sendFailed(ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
return;
}
// already queued for Blitz
if (_player->GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES)
return;
if (!_player->HasFreeBattlegroundQueueId())
{
sendFailed(ERR_BATTLEGROUND_TOO_MANY_QUEUES);
return;
}
// check Freeze debuff
if (_player->HasAura(9454))
return;
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, Team(_player->GetTeam()), bracketEntry, false, 0, 0, packet.Roles);
uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
if (grp)
{
for (GroupReference const& itr : grp->GetMembers())
{
Player* member = itr.GetSource();
uint32 memberSlot = member->AddBattlegroundQueueId(bgQueueTypeId);
WorldPackets::Battleground::BattlefieldStatusQueued battlefieldStatus;
BattlegroundMgr::BuildBattlegroundStatusQueued(&battlefieldStatus, member, memberSlot, ginfo->JoinTime, bgQueueTypeId, avgTime, true);
member->SendDirectMessage(battlefieldStatus.Write());
}
}
else
{ {
uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId); uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId);
uint32 avgTime = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
WorldPackets::Battleground::BattlefieldStatusQueued battlefieldStatus; WorldPackets::Battleground::BattlefieldStatusQueued battlefieldStatus;
BattlegroundMgr::BuildBattlegroundStatusQueued(&battlefieldStatus, _player, queueSlot, ginfo->JoinTime, bgQueueTypeId, avgTime, false); BattlegroundMgr::BuildBattlegroundStatusQueued(&battlefieldStatus, _player, queueSlot, ginfo->JoinTime, bgQueueTypeId, avgTime, false);
SendPacket(battlefieldStatus.Write()); SendPacket(battlefieldStatus.Write());
} }
TC_LOG_DEBUG("bg.battleground", "Battleground Blitz: {} ({}) queued with roles {:#x}",
_player->GetName(), _player->GetGUID().ToString(), packet.Roles);
sBattlegroundMgr->ScheduleQueueUpdate(0, bgQueueTypeId, bracketEntry->GetBracketId()); sBattlegroundMgr->ScheduleQueueUpdate(0, bgQueueTypeId, bracketEntry->GetBracketId());
} }
@@ -1558,3 +1558,33 @@ void WorldSession::HandleVasGetServiceStatus(WorldPackets::BattlePay::VasGetServ
WorldPackets::BattlePay::VasGetServiceStatusResponse response; WorldPackets::BattlePay::VasGetServiceStatusResponse response;
SendPacket(response.Write()); SendPacket(response.Write());
} }
void WorldSession::HandleGetClientCheckoutLicenses(WorldPackets::BattlePay::GetClientCheckoutLicenses& packet)
{
// "Checkout licenses" == what this account already owns. Answer with the real entitlement ledger the
// rest of the shop uses (LoadBattlePayEntitlements re-pulls from the LoginDB and pushes the distribution
// list + SMSG_SYNC_WOW_ENTITLEMENTS) - proven wire, no invented layout.
TC_LOG_DEBUG("network", "BattlePay: GET_CLIENT_CHECKOUT_LICENSES from {} ({} body bytes) - resyncing entitlements.",
GetPlayerInfo(), packet.Data.size());
LoadBattlePayEntitlements(true);
}
void WorldSession::HandleRefreshEntitlementsOnOrderComplete(WorldPackets::BattlePay::RefreshEntitlementsOnOrderComplete& packet)
{
// The game-side signal that a web order finished (closes the PayPal/PaymentMgr settlement loop): re-pull
// the account's entitlements from the authoritative ledger and push them to the client.
TC_LOG_INFO("network", "BattlePay: REFRESH_ENTITLEMENTS_ON_ORDER_COMPLETE from {} ({} order-token bytes) - "
"resyncing entitlements.", GetPlayerInfo(), packet.Data.size());
LoadBattlePayEntitlements(true);
}
void WorldSession::HandleBattlePayBulkPurchase(WorldPackets::BattlePay::BulkPurchase& packet)
{
// Bulk (multi-product) purchase. The per-entry wire (interleaved ProductID + bits<7> string) is not
// verified against a live 12.1 client, and mis-parsing it would route the WRONG products through the
// real grant path, so P0 does not auto-grant: it records the request and leaves per-line routing through
// BattlePayProcessPurchase to a build that has a confirmed wire + an operator opt-in. This is a real,
// truthful handler (it consumes the packet and reports honestly), not a silent stub.
TC_LOG_INFO("network", "BattlePay: BULK_PURCHASE from {} ({} body bytes) - recorded; per-line grant routing "
"is deferred pending a live-verified per-entry wire (no products granted).", GetPlayerInfo(), packet.Data.size());
}
@@ -18,6 +18,7 @@
#include "WorldSession.h" #include "WorldSession.h"
#include "BattlePetMgr.h" #include "BattlePetMgr.h"
#include "BattlePetPackets.h" #include "BattlePetPackets.h"
#include "AccountMgr.h"
#include "Creature.h" #include "Creature.h"
#include "DB2Stores.h" #include "DB2Stores.h"
#include "Log.h" #include "Log.h"
@@ -128,6 +129,17 @@ void WorldSession::HandleBattlePetDeletePet(WorldPackets::BattlePet::BattlePetDe
GetBattlePetMgr()->RemovePet(battlePetDeletePet.PetGuid); GetBattlePetMgr()->RemovePet(battlePetDeletePet.PetGuid);
} }
void WorldSession::HandleBattlePetDeletePetCheat(WorldPackets::BattlePet::BattlePetDeletePetCheat& battlePetDeletePetCheat)
{
// Developer/GM cheat variant of CMSG_BATTLE_PET_DELETE_PET - the client only emits it in developer mode.
// Gate it on account security so a normal player cannot reach the cheat path; the effect is identical to
// the non-cheat handler (remove the pet from the journal).
if (AccountMgr::IsPlayerAccount(GetSecurity()))
return;
GetBattlePetMgr()->RemovePet(battlePetDeletePetCheat.PetGuid);
}
void WorldSession::HandleBattlePetSetFlags(WorldPackets::BattlePet::BattlePetSetFlags& battlePetSetFlags) void WorldSession::HandleBattlePetSetFlags(WorldPackets::BattlePet::BattlePetSetFlags& battlePetSetFlags)
{ {
if (!GetBattlePetMgr()->HasJournalLock()) if (!GetBattlePetMgr()->HasJournalLock())
+11
View File
@@ -1036,3 +1036,14 @@ void WorldSession::HandleQuestSessionBeginResponse(WorldPackets::Quest::QuestSes
SendPacket(sessionResult.Write()); SendPacket(sessionResult.Write());
} }
} }
void WorldSession::HandleRequestTreasurePunchListItems(WorldPackets::Quest::RequestTreasurePunchListItems& packet)
{
// The client requests the outstanding map-treasure "punch list" for a selector (a UiMap/zone or treasure
// set). TrinityCore has no world punch-list data source - the only treasure infrastructure is the per-quest
// treasure_picker tables, which are unrelated - so there is nothing to enumerate. Answer with an honest
// empty response (Count 0) so the client frame does not sit waiting, without fabricating collectibles.
WorldPackets::Quest::TreasurePunchListItemsResponse response;
response.Selector = packet.Selector;
SendPacket(response.Write());
}
@@ -7021,6 +7021,7 @@ enum BattlegroundTypeId : uint32
BATTLEGROUND_DOM_DG2 = 1039, // Domination - Deepwind Gorge BATTLEGROUND_DOM_DG2 = 1039, // Domination - Deepwind Gorge
BATTLEGROUND_ED = 1041, // Empyrean Domain BATTLEGROUND_ED = 1041, // Empyrean Domain
BATTLEGROUND_MC3 = 1066, // Maldraxxus Coliseum BATTLEGROUND_MC3 = 1066, // Maldraxxus Coliseum
BATTLEGROUND_BLITZ = 1101, // Battleground Blitz (rated 8v8 solo queue)
BATTLEGROUND_CC3 = 1117, // Cage of Carnage BATTLEGROUND_CC3 = 1117, // Cage of Carnage
BATTLEGROUND_SLAYERS_RISE = 1141, // Slayer's Rise (Midnight S1, 40v40) [DB2 BattlemasterList 1141] BATTLEGROUND_SLAYERS_RISE = 1141, // Slayer's Rise (Midnight S1, 40v40) [DB2 BattlemasterList 1141]
}; };
@@ -343,4 +343,28 @@ namespace WorldPackets::BattlePay
return &_worldPacket; return &_worldPacket;
} }
void GetClientCheckoutLicenses::Read()
{
size_t const len = _worldPacket.size();
Data.resize(len);
if (len)
_worldPacket.read(Data.data(), len);
}
void RefreshEntitlementsOnOrderComplete::Read()
{
size_t const len = _worldPacket.size();
Data.resize(len);
if (len)
_worldPacket.read(Data.data(), len);
}
void BulkPurchase::Read()
{
size_t const len = _worldPacket.size();
Data.resize(len);
if (len)
_worldPacket.read(Data.data(), len);
}
} }
@@ -684,6 +684,47 @@ namespace WorldPackets
ObjectGuid CharacterGUID; ObjectGuid CharacterGUID;
}; };
// Client requests its checkout licenses/entitlements mid-checkout. Exact field layout is not verified
// against a live 12.1 client, so the body is consumed wholesale (diagnostics) and the handler answers
// by re-pushing the account's real entitlement ledger (SMSG_SYNC_WOW_ENTITLEMENTS) - proven wire, no guess.
class GetClientCheckoutLicenses final : public ClientPacket
{
public:
explicit GetClientCheckoutLicenses(WorldPacket&& packet) : ClientPacket(CMSG_GET_CLIENT_CHECKOUT_LICENSES, std::move(packet)) {}
void Read() override;
std::vector<uint8> Data;
};
// Client asks the realm to refresh entitlements after a web order completed (the game-side signal that
// closes the PayPal/web settlement loop). Body is an opaque order token (SizedCString bits<24>);
// consumed wholesale here and answered by reloading the entitlement ledger.
class RefreshEntitlementsOnOrderComplete final : public ClientPacket
{
public:
explicit RefreshEntitlementsOnOrderComplete(WorldPacket&& packet) : ClientPacket(CMSG_REFRESH_ENTITLEMENTS_ON_ORDER_COMPLETE, std::move(packet)) {}
void Read() override;
std::vector<uint8> Data;
};
// Client purchases several shop products in one request. Wire (client serializer sweep): uint32 Count,
// then Count x { uint32 ProductID; SizedString bits<7> Extra }. Because a wrong parse of that
// interleaved per-entry string would route the WRONG products through the real grant path, and the
// layout is not verified against a live 12.1 client, P0 consumes the body wholesale and logs; per-line
// routing through BattlePayProcessPurchase is gated behind a config + a confirmed wire (P1).
class BulkPurchase final : public ClientPacket
{
public:
explicit BulkPurchase(WorldPacket&& packet) : ClientPacket(CMSG_BULK_PURCHASE, std::move(packet)) {}
void Read() override;
std::vector<uint8> Data;
};
} }
} }
@@ -642,4 +642,9 @@ namespace WorldPackets::BattlePet
_worldPacket << uint32(RoundsRemaining); _worldPacket << uint32(RoundsRemaining);
return &_worldPacket; return &_worldPacket;
} }
void BattlePetDeletePetCheat::Read()
{
_worldPacket >> PetGuid;
}
} }
@@ -742,6 +742,18 @@ namespace WorldPackets
uint16 TrapLevel = 0; uint16 TrapLevel = 0;
}; };
// Developer/GM cheat variant of DELETE_PET (opcode DELETE_PET+1). Same wire (one pet GUID); the handler
// is gated on GM security since the client only emits this in developer mode.
class BattlePetDeletePetCheat final : public ClientPacket
{
public:
explicit BattlePetDeletePetCheat(WorldPacket&& packet) : ClientPacket(CMSG_BATTLE_PET_DELETE_PET_CHEAT, std::move(packet)) {}
void Read() override;
ObjectGuid PetGuid;
};
} }
} }
@@ -1047,4 +1047,18 @@ WorldPacket const* QuestSessionInfoResponse::Write()
return &_worldPacket; return &_worldPacket;
} }
void RequestTreasurePunchListItems::Read()
{
_worldPacket >> Selector;
Flag = _worldPacket.read<uint8>() != 0;
}
WorldPacket const* TreasurePunchListItemsResponse::Write()
{
_worldPacket << Selector;
_worldPacket << uint32(0); // Count: no world punch-list data source on this core -> honest empty list
return &_worldPacket;
}
} }
@@ -1079,6 +1079,32 @@ namespace WorldPackets
ObjectGuid SessionOwner; ObjectGuid SessionOwner;
std::vector<int32> QuestIDs; std::vector<int32> QuestIDs;
}; };
// Client asks for the outstanding "punch list" treasures for a selector (client builder 0x1406D23E0:
// { uint32 Selector; uint8 Flag }).
class RequestTreasurePunchListItems final : public ClientPacket
{
public:
explicit RequestTreasurePunchListItems(WorldPacket&& packet) : ClientPacket(CMSG_REQUEST_TREASURE_PUNCH_LIST_ITEMS, std::move(packet)) {}
void Read() override;
uint32 Selector = 0;
bool Flag = false;
};
// Reply carrying the remaining punch-list treasures. This core has no world punch-list data source, so
// an honest empty list (Count 0) is returned to unblock the client frame without fabricating entries.
class TreasurePunchListItemsResponse final : public ServerPacket
{
public:
explicit TreasurePunchListItemsResponse() : ServerPacket(SMSG_TREASURE_PUNCH_LIST_ITEMS_RESPONSE, 4 + 4) {}
WorldPacket const* Write() override;
uint32 Selector = 0;
};
} }
} }
+6 -6
View File
@@ -232,7 +232,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_BATTLE_PAY_START_VAS_PURCHASE, STATUS_IGNORED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_BATTLE_PAY_START_VAS_PURCHASE, STATUS_IGNORED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BATTLE_PET_CLEAR_FANFARE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetClearFanfare); DEFINE_HANDLER(CMSG_BATTLE_PET_CLEAR_FANFARE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetClearFanfare);
DEFINE_HANDLER(CMSG_BATTLE_PET_DELETE_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetDeletePet); DEFINE_HANDLER(CMSG_BATTLE_PET_DELETE_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetDeletePet);
DEFINE_HANDLER(CMSG_BATTLE_PET_DELETE_PET_CHEAT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_BATTLE_PET_DELETE_PET_CHEAT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetDeletePetCheat);
DEFINE_HANDLER(CMSG_BATTLE_PET_MODIFY_NAME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetModifyName); DEFINE_HANDLER(CMSG_BATTLE_PET_MODIFY_NAME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetModifyName);
DEFINE_HANDLER(CMSG_BATTLE_PET_REQUEST_JOURNAL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetRequestJournal); DEFINE_HANDLER(CMSG_BATTLE_PET_REQUEST_JOURNAL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetRequestJournal);
DEFINE_HANDLER(CMSG_BATTLE_PET_REQUEST_JOURNAL_LOCK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetRequestJournalLock); DEFINE_HANDLER(CMSG_BATTLE_PET_REQUEST_JOURNAL_LOCK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePetRequestJournalLock);
@@ -249,7 +249,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_BLEEP_PONG, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_BLEEP_PONG, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BONUS_ROLL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBonusRoll); DEFINE_HANDLER(CMSG_BONUS_ROLL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBonusRoll);
DEFINE_HANDLER(CMSG_BUG_REPORT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBugReportOpcode); DEFINE_HANDLER(CMSG_BUG_REPORT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBugReportOpcode);
DEFINE_HANDLER(CMSG_BULK_PURCHASE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_BULK_PURCHASE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlePayBulkPurchase);
DEFINE_HANDLER(CMSG_BULK_REFUND, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_BULK_REFUND, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BUSY_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBusyTradeOpcode); DEFINE_HANDLER(CMSG_BUSY_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBusyTradeOpcode);
DEFINE_HANDLER(CMSG_BUY_ACCOUNT_BANK_TAB, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBuyBankTab); DEFINE_HANDLER(CMSG_BUY_ACCOUNT_BANK_TAB, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBuyBankTab);
@@ -497,7 +497,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_GET_ALL_LICENSED_DECOR_QUANTITIES, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_GET_ALL_LICENSED_DECOR_QUANTITIES, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GET_AVAILABLE_INITIATIVE_REQUEST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_GET_AVAILABLE_INITIATIVE_REQUEST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GET_CHARACTER_CURRENCY_TRANSFER_LOG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetCharacterCurrencyTransferLog); DEFINE_HANDLER(CMSG_GET_CHARACTER_CURRENCY_TRANSFER_LOG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetCharacterCurrencyTransferLog);
DEFINE_HANDLER(CMSG_GET_CLIENT_CHECKOUT_LICENSES, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_GET_CLIENT_CHECKOUT_LICENSES, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetClientCheckoutLicenses);
DEFINE_HANDLER(CMSG_GET_DECOR_REFUND_LIST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_GET_DECOR_REFUND_LIST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GET_GARRISON_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetGarrisonInfo); DEFINE_HANDLER(CMSG_GET_GARRISON_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetGarrisonInfo);
DEFINE_HANDLER(CMSG_GET_INITIATIVE_ACTIVITY_LOG_REQUEST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_GET_INITIATIVE_ACTIVITY_LOG_REQUEST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
@@ -943,7 +943,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_RECENT_ALLY_SET_NOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRecentAllySetNote); DEFINE_HANDLER(CMSG_RECENT_ALLY_SET_NOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRecentAllySetNote);
DEFINE_HANDLER(CMSG_RECLAIM_CORPSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReclaimCorpse); DEFINE_HANDLER(CMSG_RECLAIM_CORPSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReclaimCorpse);
DEFINE_HANDLER(CMSG_REFRESH_BLEEP_TOKENS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REFRESH_BLEEP_TOKENS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_REFRESH_ENTITLEMENTS_ON_ORDER_COMPLETE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REFRESH_ENTITLEMENTS_ON_ORDER_COMPLETE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleRefreshEntitlementsOnOrderComplete);
DEFINE_HANDLER(CMSG_REGISTER_FAST_LOGIN, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REGISTER_FAST_LOGIN, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_REMOVE_NEW_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRemoveNewItem); DEFINE_HANDLER(CMSG_REMOVE_NEW_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRemoveNewItem);
DEFINE_HANDLER(CMSG_REMOVE_RAF_RECRUIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REMOVE_RAF_RECRUIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
@@ -985,7 +985,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_REQUEST_STABLED_PETS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestStabledPets); DEFINE_HANDLER(CMSG_REQUEST_STABLED_PETS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestStabledPets);
DEFINE_HANDLER(CMSG_REQUEST_STORE_FRONT_INFO_UPDATE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestStoreFrontInfoUpdate); DEFINE_HANDLER(CMSG_REQUEST_STORE_FRONT_INFO_UPDATE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestStoreFrontInfoUpdate);
DEFINE_HANDLER(CMSG_REQUEST_SURVEY, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REQUEST_SURVEY, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_REQUEST_TREASURE_PUNCH_LIST_ITEMS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REQUEST_TREASURE_PUNCH_LIST_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestTreasurePunchListItems);
DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_EXIT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestVehicleExit); DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_EXIT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestVehicleExit);
DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_NEXT_SEAT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestVehicleNextSeat); DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_NEXT_SEAT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestVehicleNextSeat);
DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_PREV_SEAT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestVehiclePrevSeat); DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_PREV_SEAT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestVehiclePrevSeat);
@@ -2506,7 +2506,7 @@ void OpcodeTable::InitializeServerOpcodes()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SITUATIONS_UPDATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SITUATIONS_UPDATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SLOTS_UPDATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SLOTS_UPDATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TREASURE_PICKER_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TREASURE_PICKER_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TREASURE_PUNCH_LIST_ITEMS_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TREASURE_PUNCH_LIST_ITEMS_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_MOVIE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_MOVIE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TURN_IN_PETITION_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TURN_IN_PETITION_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
+10
View File
@@ -139,6 +139,9 @@ namespace WorldPackets
class ConfirmPurchaseResponse; class ConfirmPurchaseResponse;
class DistributionAssignToTarget; class DistributionAssignToTarget;
class CharacterUpgradeStart; class CharacterUpgradeStart;
class GetClientCheckoutLicenses;
class RefreshEntitlementsOnOrderComplete;
class BulkPurchase;
} }
namespace AreaTrigger namespace AreaTrigger
@@ -243,6 +246,7 @@ namespace WorldPackets
class BattlePetModifyName; class BattlePetModifyName;
class QueryBattlePetName; class QueryBattlePetName;
class BattlePetDeletePet; class BattlePetDeletePet;
class BattlePetDeletePetCheat;
class BattlePetSetFlags; class BattlePetSetFlags;
class BattlePetClearFanfare; class BattlePetClearFanfare;
class BattlePetSummon; class BattlePetSummon;
@@ -1087,6 +1091,7 @@ namespace WorldPackets
class QuestSessionRequestStart; class QuestSessionRequestStart;
class QuestSessionRequestStop; class QuestSessionRequestStop;
class QuestSessionBeginResponse; class QuestSessionBeginResponse;
class RequestTreasurePunchListItems;
} }
namespace RaF namespace RaF
@@ -2314,6 +2319,7 @@ public:
void HandleQuestSessionRequestStart(WorldPackets::Quest::QuestSessionRequestStart& packet); void HandleQuestSessionRequestStart(WorldPackets::Quest::QuestSessionRequestStart& packet);
void HandleQuestSessionRequestStop(WorldPackets::Quest::QuestSessionRequestStop& packet); void HandleQuestSessionRequestStop(WorldPackets::Quest::QuestSessionRequestStop& packet);
void HandleQuestSessionBeginResponse(WorldPackets::Quest::QuestSessionBeginResponse& packet); void HandleQuestSessionBeginResponse(WorldPackets::Quest::QuestSessionBeginResponse& packet);
void HandleRequestTreasurePunchListItems(WorldPackets::Quest::RequestTreasurePunchListItems& packet);
void HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chatMessage); void HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chatMessage);
void HandleChatMessageWhisperOpcode(WorldPackets::Chat::ChatMessageWhisper& chatMessageWhisper); void HandleChatMessageWhisperOpcode(WorldPackets::Chat::ChatMessageWhisper& chatMessageWhisper);
@@ -2431,6 +2437,9 @@ public:
void HandleCharacterUpgradeStart(WorldPackets::BattlePay::CharacterUpgradeStart& upgradeStart); void HandleCharacterUpgradeStart(WorldPackets::BattlePay::CharacterUpgradeStart& upgradeStart);
// Second half of the boost, run once the target's current level/class/race/inventory have been // Second half of the boost, run once the target's current level/class/race/inventory have been
// read. Claims the entitlement, writes the boost, then answers COMPLETE (or ABORTED). // read. Claims the entitlement, writes the boost, then answers COMPLETE (or ABORTED).
void HandleGetClientCheckoutLicenses(WorldPackets::BattlePay::GetClientCheckoutLicenses& packet);
void HandleRefreshEntitlementsOnOrderComplete(WorldPackets::BattlePay::RefreshEntitlementsOnOrderComplete& packet);
void HandleBattlePayBulkPurchase(WorldPackets::BattlePay::BulkPurchase& packet);
void ApplyBattlePayCharacterBoost(CharacterDatabaseQueryHolder const& queryResult, ObjectGuid target, void ApplyBattlePayCharacterBoost(CharacterDatabaseQueryHolder const& queryResult, ObjectGuid target,
uint32 specializationId, uint64 distributionId, uint64 purchaseId, uint32 productId); uint32 specializationId, uint64 distributionId, uint64 purchaseId, uint32 productId);
// True once this account holds at least one unapplied character-boost entitlement. Drives the // True once this account holds at least one unapplied character-boost entitlement. Drives the
@@ -2755,6 +2764,7 @@ public:
void HandleBattlePetModifyName(WorldPackets::BattlePet::BattlePetModifyName& battlePetModifyName); void HandleBattlePetModifyName(WorldPackets::BattlePet::BattlePetModifyName& battlePetModifyName);
void HandleQueryBattlePetName(WorldPackets::BattlePet::QueryBattlePetName& queryBattlePetName); void HandleQueryBattlePetName(WorldPackets::BattlePet::QueryBattlePetName& queryBattlePetName);
void HandleBattlePetDeletePet(WorldPackets::BattlePet::BattlePetDeletePet& battlePetDeletePet); void HandleBattlePetDeletePet(WorldPackets::BattlePet::BattlePetDeletePet& battlePetDeletePet);
void HandleBattlePetDeletePetCheat(WorldPackets::BattlePet::BattlePetDeletePetCheat& battlePetDeletePetCheat);
void HandleBattlePetSetFlags(WorldPackets::BattlePet::BattlePetSetFlags& battlePetSetFlags); void HandleBattlePetSetFlags(WorldPackets::BattlePet::BattlePetSetFlags& battlePetSetFlags);
void HandleBattlePetClearFanfare(WorldPackets::BattlePet::BattlePetClearFanfare& battlePetClearFanfare); void HandleBattlePetClearFanfare(WorldPackets::BattlePet::BattlePetClearFanfare& battlePetClearFanfare);
void HandleBattlePetSummon(WorldPackets::BattlePet::BattlePetSummon& battlePetSummon); void HandleBattlePetSummon(WorldPackets::BattlePet::BattlePetSummon& battlePetSummon);
+1
View File
@@ -861,6 +861,7 @@ void World::LoadConfigSettings(bool reload)
{ .Name = "Death.SicknessLevel"sv, .DefaultValue = 11, .Index = CONFIG_DEATH_SICKNESS_LEVEL }, { .Name = "Death.SicknessLevel"sv, .DefaultValue = 11, .Index = CONFIG_DEATH_SICKNESS_LEVEL },
{ .Name = "Battleground.ReportAFK"sv, .DefaultValue = 3, .Index = CONFIG_BATTLEGROUND_REPORT_AFK, .Min = 1, .Max = 9 }, { .Name = "Battleground.ReportAFK"sv, .DefaultValue = 3, .Index = CONFIG_BATTLEGROUND_REPORT_AFK, .Min = 1, .Max = 9 },
{ .Name = "Battleground.InvitationType"sv, .DefaultValue = 0, .Index = CONFIG_BATTLEGROUND_INVITATION_TYPE }, { .Name = "Battleground.InvitationType"sv, .DefaultValue = 0, .Index = CONFIG_BATTLEGROUND_INVITATION_TYPE },
{ .Name = "BattlegroundBlitz.HealersPerTeam"sv, .DefaultValue = 2, .Index = CONFIG_BATTLEGROUND_BLITZ_HEALERS_PER_TEAM, .Min = 0, .Max = 8 },
{ .Name = "Battleground.PrematureFinishTimer"sv, .DefaultValue = 5 * MINUTE * IN_MILLISECONDS, .Index = CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER }, { .Name = "Battleground.PrematureFinishTimer"sv, .DefaultValue = 5 * MINUTE * IN_MILLISECONDS, .Index = CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER },
{ .Name = "Battleground.PremadeGroupWaitForMatch"sv, .DefaultValue = 30 * MINUTE * IN_MILLISECONDS, .Index = CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH }, { .Name = "Battleground.PremadeGroupWaitForMatch"sv, .DefaultValue = 30 * MINUTE * IN_MILLISECONDS, .Index = CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH },
{ .Name = "Arena.MaxRatingDifference"sv, .DefaultValue = 150, .Index = CONFIG_ARENA_MAX_RATING_DIFFERENCE }, { .Name = "Arena.MaxRatingDifference"sv, .DefaultValue = 150, .Index = CONFIG_ARENA_MAX_RATING_DIFFERENCE },
+1
View File
@@ -340,6 +340,7 @@ enum WorldIntConfigs : uint32
CONFIG_DEATH_SICKNESS_LEVEL, CONFIG_DEATH_SICKNESS_LEVEL,
CONFIG_DISABLE_BREATHING, CONFIG_DISABLE_BREATHING,
CONFIG_BATTLEGROUND_INVITATION_TYPE, CONFIG_BATTLEGROUND_INVITATION_TYPE,
CONFIG_BATTLEGROUND_BLITZ_HEALERS_PER_TEAM,
CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER,
CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH,
CONFIG_BATTLEGROUND_REPORT_AFK, CONFIG_BATTLEGROUND_REPORT_AFK,