Skip to content

Commit

Permalink
BG: Rework BG queue to be asynchronous and resolve race conditions
Browse files Browse the repository at this point in the history
Thx to insunaa for making it compile on Linux

Thx to mostlikely4r for help bugfixing
  • Loading branch information
killerwife committed Nov 14, 2024
1 parent 7ee4c45 commit eeb446e
Show file tree
Hide file tree
Showing 28 changed files with 2,183 additions and 1,500 deletions.
87 changes: 63 additions & 24 deletions src/game/BattleGround/BattleGround.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,20 @@ BattleGround::~BattleGround()
// skip template bgs as they were never added to visible bg list
BattleGroundBracketId bracketId = GetBracketId();
if (bracketId != BG_BRACKET_ID_TEMPLATE)
sBattleGroundMgr.DeleteClientVisibleInstanceId(GetTypeId(), bracketId, GetClientInstanceId());
{
sWorld.GetBGQueue().GetMessager().AddMessage([bgTypeId = GetTypeId(), bracketId, clientInstanceId = GetClientInstanceId()](BattleGroundQueue* queue)
{
queue->DeleteClientVisibleInstanceId(bgTypeId, bracketId, clientInstanceId);
});
}

// unload map
// map can be null at bg destruction
if (m_bgMap)
m_bgMap->SetUnload();

// remove from bg free slot queue
this->RemoveFromBgFreeSlotQueue();
this->RemovedFromBgFreeSlotQueue(true);

for (BattleGroundScoreMap::const_iterator itr = m_playerScores.begin(); itr != m_playerScores.end(); ++itr)
delete itr->second;
Expand Down Expand Up @@ -691,7 +696,7 @@ void BattleGround::UpdateWorldStateForPlayer(uint32 field, uint32 value, Player*
*/
void BattleGround::EndBattleGround(Team winner)
{
this->RemoveFromBgFreeSlotQueue();
this->RemovedFromBgFreeSlotQueue(true);

uint32 loser_rating = 0;
uint32 winner_rating = 0;
Expand Down Expand Up @@ -832,7 +837,7 @@ void BattleGround::EndBattleGround(Team winner)
plr->GetSession()->SendPacket(data);

BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BgQueueTypeId(GetTypeId());
sBattleGroundMgr.BuildBattleGroundStatusPacket(data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime());
sBattleGroundMgr.BuildBattleGroundStatusPacket(data, true, GetTypeId(), GetClientInstanceId(), GetMapId(), plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime());
plr->GetSession()->SendPacket(data);
}

Expand All @@ -852,6 +857,16 @@ uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
return (uint32)MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
}

void BattleGround::SetStatus(BattleGroundStatus status)
{
m_status = status;
sWorld.GetBGQueue().GetMessager().AddMessage([status, bgTypeId = GetTypeId(), instanceId = GetInstanceId()](BattleGroundQueue* queue)
{
if (BattleGroundInQueueInfo* bgInstance = queue->GetFreeSlotInstance(bgTypeId, instanceId))
bgInstance->status = status;
});
}

/**
Function that returns the battleground master entry
*/
Expand Down Expand Up @@ -1084,7 +1099,7 @@ void BattleGround::RemovePlayerAtLeave(ObjectGuid playerGuid, bool isOnTransport
if (doSendPacket)
{
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundStatusPacket(data, this, player->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0);
sBattleGroundMgr.BuildBattleGroundStatusPacket(data, true, GetTypeId(), GetClientInstanceId(), GetMapId(), player->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0);
player->GetSession()->SendPacket(data);
}

Expand All @@ -1101,13 +1116,24 @@ void BattleGround::RemovePlayerAtLeave(ObjectGuid playerGuid, bool isOnTransport
delete group;
}
}
DecreaseInvitedCount(team);

SetInvitedCount(team, GetInvitedCount(team) - 1); // change ahead of free slot queue - will be synched again after
// we should update battleground queue, but only if bg isn't ending
if (GetStatus() < STATUS_WAIT_LEAVE)
{
// a player has left the battleground, so there are free slots -> add to queue
AddToBgFreeSlotQueue();
sBattleGroundMgr.ScheduleQueueUpdate(bgQueueTypeId, bgTypeId, GetBracketId());
if (!AddToBgFreeSlotQueue()) // avoid setting two messages - if was already in queue, just update count
{
sWorld.GetBGQueue().GetMessager().AddMessage([bgTypeId, instanceId = GetInstanceId(), team](BattleGroundQueue* queue)
{
if (BattleGroundInQueueInfo* bgInstance = queue->GetFreeSlotInstance(bgTypeId, instanceId))
bgInstance->DecreaseInvitedCount(team);
});
}
sWorld.GetBGQueue().GetMessager().AddMessage([bgQueueTypeId, bgTypeId, bracketId = GetBracketId()](BattleGroundQueue* queue)
{
queue->ScheduleQueueUpdate(bgQueueTypeId, bgTypeId, bracketId);
});
}

// Let others know
Expand Down Expand Up @@ -1168,8 +1194,7 @@ void BattleGround::StartBattleGround()
{
SetStartTime(0);

// add BG to free slot queue
AddToBgFreeSlotQueue();
// expects to be already added in free queue

// add bg to update list
// This must be done here, because we need to have already invited some players when first BG::Update() method is executed
Expand Down Expand Up @@ -1293,33 +1318,45 @@ void BattleGround::EventPlayerLoggedOut(Player* player)
/**
Function that returns the number of players that can join a battleground based on the provided team
*/
void BattleGround::AddToBgFreeSlotQueue()
bool BattleGround::AddToBgFreeSlotQueue()
{
// make sure to add only once
if (!m_hasBgFreeSlotQueue)
{
sBattleGroundMgr.BgFreeSlotQueue[m_typeId].push_front(this);
m_hasBgFreeSlotQueue = true;
BattleGroundInQueueInfo bgInfo;
bgInfo.Fill(this);
sWorld.GetBGQueue().GetMessager().AddMessage([bgInfo](BattleGroundQueue* queue)
{
queue->AddBgToFreeSlots(bgInfo);
});
return true;
}
return false;
}

/**
Method that removes this battleground from free queue - it must be called when deleting battleground
*/
void BattleGround::RemoveFromBgFreeSlotQueue()
void BattleGround::RemovedFromBgFreeSlotQueue(bool removeFromQueue)
{
// set to be able to re-add if needed
m_hasBgFreeSlotQueue = false;
BgFreeSlotQueueType& bgFreeSlot = sBattleGroundMgr.BgFreeSlotQueue[m_typeId];

for (BgFreeSlotQueueType::iterator itr = bgFreeSlot.begin(); itr != bgFreeSlot.end(); ++itr)
if (m_hasBgFreeSlotQueue && removeFromQueue)
{
if ((*itr)->GetInstanceId() == GetInstanceId())
sWorld.GetBGQueue().GetMessager().AddMessage([bgTypeId = GetTypeId(), instanceId = GetInstanceId()](BattleGroundQueue* queue)
{
bgFreeSlot.erase(itr);
return;
}
queue->RemoveBgFromFreeSlots(bgTypeId, instanceId);
});
}
m_hasBgFreeSlotQueue = false;
}

void BattleGround::SetInvitedCount(Team team, uint32 count)
{
if (team == ALLIANCE)
m_invitedAlliance = count;
else
m_invitedHorde = count;
}

/**
Expand Down Expand Up @@ -1440,7 +1477,7 @@ Team BattleGround::GetPrematureWinner()
*/
void BattleGround::OnObjectDBLoad(Creature* creature)
{
const BattleGroundEventIdx eventId = sBattleGroundMgr.GetCreatureEventIndex(creature->GetDbGuid());
const BattleGroundEventIdx eventId = GetBgMap()->GetMapDataContainer().GetCreatureEventIndex(creature->GetDbGuid());
if (eventId.event1 == BG_EVENT_NONE)
return;

Expand Down Expand Up @@ -1488,7 +1525,7 @@ uint32 BattleGround::GetSingleGameObjectGuid(uint8 event1, uint8 event2)
*/
void BattleGround::OnObjectDBLoad(GameObject* obj)
{
const BattleGroundEventIdx eventId = sBattleGroundMgr.GetGameObjectEventIndex(obj->GetDbGuid());
const BattleGroundEventIdx eventId = GetBgMap()->GetMapDataContainer().GetGameObjectEventIndex(obj->GetDbGuid());
if (eventId.event1 == BG_EVENT_NONE)
return;

Expand Down Expand Up @@ -1745,7 +1782,7 @@ void BattleGround::SendBcdToTeam(int32 bcdEntry, ChatMsg msgtype, Creature const
*/
void BattleGround::EndNow()
{
RemoveFromBgFreeSlotQueue();
RemovedFromBgFreeSlotQueue(true);
SetStatus(STATUS_WAIT_LEAVE);
SetEndTime(0);
}
Expand Down Expand Up @@ -1838,6 +1875,8 @@ void BattleGround::PlayerAddedToBgCheckIfBgIsRunning(Player* player)
sBattleGroundMgr.BuildPvpLogDataPacket(data, this);
player->GetSession()->SendPacket(data);

sBattleGroundMgr.BuildBattleGroundStatusPacket(data, true, GetTypeId(), GetClientInstanceId(), GetMapId(), player->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime());
player->GetSession()->SendPacket(data);
}

/**
Expand Down
17 changes: 9 additions & 8 deletions src/game/BattleGround/BattleGround.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "Maps/Map.h"
#include "Util/ByteBuffer.h"
#include "Entities/ObjectGuid.h"
#include "BattleGround/BattleGroundDefines.h"

// magic event-numbers
#define BG_EVENT_NONE 255
Expand Down Expand Up @@ -298,9 +299,9 @@ class BattleGround

// Set methods:
void SetName(char const* name) { m_name = name; }
void SetTypeID(BattleGroundTypeId typeId) { m_typeId = typeId; }
void SetBracketId(BattleGroundBracketId Id) { m_bracketId = Id; }
void SetStatus(BattleGroundStatus status) { m_status = status; }
void SetTypeId(BattleGroundTypeId typeId) { m_typeId = typeId; }
void SetBracketId(BattleGroundBracketId id) { m_bracketId = id; }
void SetStatus(BattleGroundStatus status);
void SetClientInstanceId(uint32 instanceId) { m_clientInstanceId = instanceId; }
void SetStartTime(uint32 time) { m_startTime = time; }
void SetEndTime(uint32 time) { m_endTime = time; }
Expand All @@ -315,11 +316,11 @@ class BattleGround
void SetMaxPlayersPerTeam(uint32 maxPlayers) { m_maxPlayersPerTeam = maxPlayers; }
void SetMinPlayersPerTeam(uint32 minPlayers) { m_minPlayersPerTeam = minPlayers; }

void AddToBgFreeSlotQueue(); // this queue will be useful when more battlegrounds instances will be available
void RemoveFromBgFreeSlotQueue(); // this method could delete whole BG instance, if another free is available
bool AddToBgFreeSlotQueue(); // this queue will be useful when more battlegrounds instances will be available
void RemovedFromBgFreeSlotQueue(bool removeFromQueue); // this method could delete whole BG instance, if another free is available

void DecreaseInvitedCount(Team team) { (team == ALLIANCE) ? --m_invitedAlliance : --m_invitedHorde; }
void IncreaseInvitedCount(Team team) { (team == ALLIANCE) ? ++m_invitedAlliance : ++m_invitedHorde; }
// Functions to decrease or increase player count
void SetInvitedCount(Team team, uint32 count);
uint32 GetInvitedCount(Team team) const
{
if (team == ALLIANCE)
Expand Down Expand Up @@ -540,7 +541,7 @@ class BattleGround
// returns the other team index
static PvpTeamIndex GetOtherTeamIndex(PvpTeamIndex teamIdx) { return teamIdx == TEAM_INDEX_ALLIANCE ? TEAM_INDEX_HORDE : TEAM_INDEX_ALLIANCE; }

// checke if player is inside battleground
// check if player is inside battleground
bool IsPlayerInBattleGround(ObjectGuid /*playerGuid*/);

// Handle script condition fulfillment
Expand Down
2 changes: 1 addition & 1 deletion src/game/BattleGround/BattleGroundAB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ void BattleGroundAB::HandlePlayerClickedOnFlag(Player* player, GameObject* go)
uint32 factionStrig = 0;

// process battleground event
uint8 event = (sBattleGroundMgr.GetGameObjectEventIndex(go->GetDbGuid())).event1;
uint8 event = (GetBgMap()->GetMapDataContainer().GetGameObjectEventIndex(go->GetDbGuid())).event1;
if (event >= BG_AB_MAX_NODES) // not a node
return;

Expand Down
4 changes: 2 additions & 2 deletions src/game/BattleGround/BattleGroundAV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,12 +616,12 @@ void BattleGroundAV::HandlePlayerClickedOnFlag(Player* player, GameObject* go)

DEBUG_LOG("BattleGroundAV: Player from team %u clicked on gameobject entry %u", player->GetTeam(), go->GetEntry());

uint8 event = (sBattleGroundMgr.GetGameObjectEventIndex(go->GetDbGuid())).event1;
uint8 event = (GetBgMap()->GetMapDataContainer().GetGameObjectEventIndex(go->GetDbGuid())).event1;
if (event >= BG_AV_MAX_NODES) // not a node
return;
AVNodeIds node = AVNodeIds(event);

switch ((sBattleGroundMgr.GetGameObjectEventIndex(go->GetDbGuid())).event2 % BG_AV_MAX_STATES)
switch ((GetBgMap()->GetMapDataContainer().GetGameObjectEventIndex(go->GetDbGuid())).event2 % BG_AV_MAX_STATES)
{
case POINT_CONTROLLED:
ProcessPlayerAssaultsPoint(player, node);
Expand Down
94 changes: 94 additions & 0 deletions src/game/BattleGround/BattleGroundDefines.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* This file is part of the CMaNGOS Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#ifndef _BG_DEFINES_H
#define _BG_DEFINES_H

#include "Common.h"

#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day
#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10

enum BattleGroundQueueGroupTypes
{
BG_QUEUE_PREMADE_ALLIANCE = 0,
BG_QUEUE_PREMADE_HORDE = 1,
BG_QUEUE_NORMAL_ALLIANCE = 2,
BG_QUEUE_NORMAL_HORDE = 3
};

#define BG_QUEUE_GROUP_TYPES_COUNT 4

enum BattleGroundGroupJoinStatus
{
BG_GROUP_JOIN_STATUS_TEAM_LEFT_QUEUE = -7,
BG_GROUP_JOIN_STATUS_QUEUED_FOR_RATED = -6,
BG_GROUP_JOIN_STATUS_CANNOT_QUEUE_FOR_RATED = -5,
BG_GROUP_JOIN_STATUS_TOO_MANY_QUEUES = -4,
BG_GROUP_JOIN_STATUS_NOT_IN_TEAM = -3,
BG_GROUP_JOIN_STATUS_DESERTERS = -2,
BG_GROUP_JOIN_STATUS_NOT_ELIGIBLE = -1,
BG_GROUP_JOIN_STATUS_SUCCESS = 0,
};

// indexes of BattlemasterList.dbc
enum BattleGroundTypeId
{
BATTLEGROUND_TYPE_NONE = 0,
BATTLEGROUND_AV = 1,
BATTLEGROUND_WS = 2,
BATTLEGROUND_AB = 3,
};
#define MAX_BATTLEGROUND_TYPE_ID 4

inline BattleGroundTypeId GetBattleGroundTypeIdByMapId(uint32 mapId)
{
switch (mapId)
{
case 30: return BATTLEGROUND_AV;
case 489: return BATTLEGROUND_WS;
case 529: return BATTLEGROUND_AB;
default: return BATTLEGROUND_TYPE_NONE;
}
}

inline uint32 GetBattleGrounMapIdByTypeId(BattleGroundTypeId bgTypeId)
{
switch (bgTypeId)
{
case BATTLEGROUND_AV: return 30;
case BATTLEGROUND_WS: return 489;
case BATTLEGROUND_AB: return 529;
default: return 0; // none
}

// impossible, just make compiler happy
return 0;
}

enum ArenaType
{
ARENA_TYPE_NONE = 0, // used for mark non-arenas or problematic cases
ARENA_TYPE_2v2 = 2,
ARENA_TYPE_3v3 = 3,
ARENA_TYPE_5v5 = 5
};

inline bool IsArenaTypeValid(ArenaType type) { return type == ARENA_TYPE_2v2 || type == ARENA_TYPE_3v3 || type == ARENA_TYPE_5v5; }

#endif
Loading

0 comments on commit eeb446e

Please sign in to comment.