aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/bnetserver/Server/Session.cpp4
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp4
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h9
-rw-r--r--src/server/game/Accounts/RBAC.cpp23
-rw-r--r--src/server/game/Accounts/RBAC.h4
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp30
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp26
-rw-r--r--src/server/game/Battlegrounds/BattlegroundQueue.cpp7
-rw-r--r--src/server/game/Chat/Chat.cpp1
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp5
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp6
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp5
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp3
-rw-r--r--src/server/game/Entities/Object/Object.cpp8
-rw-r--r--src/server/game/Entities/Player/Player.cpp32
-rw-r--r--src/server/game/Entities/Player/Player.h3
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp1
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp61
-rw-r--r--src/server/game/Entities/Vehicle/VehicleDefines.h1
-rw-r--r--src/server/game/Garrison/Garrison.cpp185
-rw-r--r--src/server/game/Garrison/Garrison.h6
-rw-r--r--src/server/game/Garrison/GarrisonMgr.cpp101
-rw-r--r--src/server/game/Garrison/GarrisonMgr.h14
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp4
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h6
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiersImpl.h12
-rw-r--r--src/server/game/Groups/Group.cpp21
-rw-r--r--src/server/game/Handlers/BattleGroundHandler.cpp8
-rw-r--r--src/server/game/Handlers/GroupHandler.cpp2
-rw-r--r--src/server/game/Handlers/LootHandler.cpp2
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp82
-rw-r--r--src/server/game/Instances/InstanceScript.cpp21
-rw-r--r--src/server/game/Instances/InstanceScript.h1
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h2
-rw-r--r--src/server/game/Movement/PathGenerator.cpp4
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.cpp11
-rw-r--r--src/server/game/Scripting/MapScripts.cpp1
-rw-r--r--src/server/game/Server/Packet.h1
-rw-r--r--src/server/game/Server/Packets/ChatPackets.cpp69
-rw-r--r--src/server/game/Server/Packets/ChatPackets.h15
-rw-r--r--src/server/game/Server/Packets/GarrisonPackets.cpp15
-rw-r--r--src/server/game/Server/Packets/GarrisonPackets.h21
-rw-r--r--src/server/game/Server/Packets/MiscPackets.cpp17
-rw-r--r--src/server/game/Server/Packets/MiscPackets.h22
-rw-r--r--src/server/game/Server/Packets/SpellPackets.cpp36
-rw-r--r--src/server/game/Server/Packets/SpellPackets.h44
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp24
-rw-r--r--src/server/game/Server/Protocol/Opcodes.h8
-rw-r--r--src/server/game/Server/WorldSession.cpp151
-rw-r--r--src/server/game/Server/WorldSession.h15
-rw-r--r--src/server/game/Server/WorldSocket.cpp333
-rw-r--r--src/server/game/Server/WorldSocket.h17
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp29
-rw-r--r--src/server/game/Spells/Spell.cpp4
-rw-r--r--src/server/game/Spells/Spell.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp14
-rw-r--r--src/server/game/Spells/SpellInfo.cpp35
-rw-r--r--src/server/game/Spells/SpellScript.cpp4
-rw-r--r--src/server/game/Texts/ChatTextBuilder.h18
-rw-r--r--src/server/game/Texts/CreatureTextMgr.cpp18
-rw-r--r--src/server/game/Texts/CreatureTextMgr.h8
-rw-r--r--src/server/game/World/World.cpp35
-rw-r--r--src/server/scripts/Commands/cs_go.cpp13
-rw-r--r--src/server/scripts/Commands/cs_tele.cpp2
-rw-r--r--src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp1
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp2
-rw-r--r--src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp4
-rw-r--r--src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp2
-rw-r--r--src/server/shared/Database/DatabaseLoader.cpp3
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.cpp4
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.cpp21
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.h2
-rw-r--r--src/server/shared/Debugging/WheatyExceptionReport.cpp10
-rw-r--r--src/server/shared/Debugging/WheatyExceptionReport.h2
-rw-r--r--src/server/shared/Networking/Socket.h2
-rw-r--r--src/server/shared/Updater/DBUpdater.cpp13
-rw-r--r--src/server/shared/Updater/DBUpdater.h13
-rw-r--r--src/server/shared/Updater/UpdateFetcher.cpp19
-rw-r--r--src/server/shared/Updater/UpdateFetcher.h2
-rw-r--r--src/server/shared/Utilities/TaskScheduler.cpp34
-rw-r--r--src/server/shared/Utilities/TaskScheduler.h48
82 files changed, 1359 insertions, 510 deletions
diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp
index 89818873335..822a90b5222 100644
--- a/src/server/bnetserver/Server/Session.cpp
+++ b/src/server/bnetserver/Server/Session.cpp
@@ -677,8 +677,8 @@ bool Battlenet::Session::Update()
if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
- _queryCallback(_queryFuture.get());
- _queryCallback = nullptr;
+ auto callback = std::move(_queryCallback);
+ callback(_queryFuture.get());
}
return true;
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 74d9e5b4a9c..c66762e64a1 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -585,7 +585,11 @@ void SmartAI::JustDied(Unit* killer)
{
GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer);
if (HasEscortState(SMART_ESCORT_ESCORTING))
+ {
EndPath(true);
+ me->StopMoving();//force stop
+ me->GetMotionMaster()->MoveIdle();
+ }
}
void SmartAI::KilledUnit(Unit* victim)
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
index c5cc5aedb43..e1a61438194 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.h
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -246,7 +246,14 @@ class SmartScript
DecPhase(abs(p));
}
- void DecPhase(int32 p = 1) { mEventPhase -= (mEventPhase < (uint32)p ? (uint32)p - mEventPhase : (uint32)p); }
+ void DecPhase(int32 p = 1)
+ {
+ if(mEventPhase > (uint32)p)
+ mEventPhase -= (uint32)p;
+ else
+ mEventPhase = 0;
+ }
+
bool IsInPhase(uint32 p) const { return ((1 << (mEventPhase - 1)) & p) != 0; }
void SetPhase(uint32 p = 0) { mEventPhase = p; }
diff --git a/src/server/game/Accounts/RBAC.cpp b/src/server/game/Accounts/RBAC.cpp
index 74ff060636e..c520564f0fa 100644
--- a/src/server/game/Accounts/RBAC.cpp
+++ b/src/server/game/Accounts/RBAC.cpp
@@ -17,7 +17,6 @@
#include "RBAC.h"
#include "AccountMgr.h"
-#include "DatabaseEnv.h"
#include "Log.h"
namespace rbac
@@ -180,7 +179,24 @@ void RBACData::LoadFromDB()
stmt->setUInt32(0, GetId());
stmt->setInt32(1, GetRealmId());
- PreparedQueryResult result = LoginDatabase.Query(stmt);
+ LoadFromDBCallback(LoginDatabase.Query(stmt));
+}
+
+PreparedQueryResultFuture RBACData::LoadFromDBAsync()
+{
+ ClearData();
+
+ TC_LOG_DEBUG("rbac", "RBACData::LoadFromDB [Id: %u Name: %s]: Loading permissions", GetId(), GetName().c_str());
+ // Load account permissions (granted and denied) that affect current realm
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS);
+ stmt->setUInt32(0, GetId());
+ stmt->setInt32(1, GetRealmId());
+
+ return LoginDatabase.AsyncQuery(stmt);
+}
+
+void RBACData::LoadFromDBCallback(PreparedQueryResult result)
+{
if (result)
{
do
@@ -190,8 +206,7 @@ void RBACData::LoadFromDB()
GrantPermission(fields[0].GetUInt32());
else
DenyPermission(fields[0].GetUInt32());
- }
- while (result->NextRow());
+ } while (result->NextRow());
}
// Add default permissions
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index 6d56774e447..db0fc711fc2 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -40,7 +40,7 @@
#ifndef _RBAC_H
#define _RBAC_H
-#include "Define.h"
+#include "DatabaseEnv.h"
#include <string>
#include <set>
#include <map>
@@ -907,6 +907,8 @@ class RBACData
/// Loads all permissions assigned to current account
void LoadFromDB();
+ PreparedQueryResultFuture LoadFromDBAsync();
+ void LoadFromDBCallback(PreparedQueryResult result);
/// Sets security level
void SetSecurityLevel(uint8 id)
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index 9c347879768..68d76272e22 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -1056,6 +1056,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type,
case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: // This also behaves like ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
+ case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING:
SetCriteriaProgress(achievementCriteria, 1, referencePlayer, PROGRESS_ACCUMULATE);
break;
// std case: increment at miscValue1
@@ -1289,7 +1290,6 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type,
case ACHIEVEMENT_CRITERIA_TYPE_ENTER_AREA:
case ACHIEVEMENT_CRITERIA_TYPE_LEAVE_AREA:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DUNGEON_ENCOUNTER:
- case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING:
case ACHIEVEMENT_CRITERIA_TYPE_UPGRADE_GARRISON_BUILDING:
case ACHIEVEMENT_CRITERIA_TYPE_CONSTRUCT_GARRISON_BUILDING:
case ACHIEVEMENT_CRITERIA_TYPE_UPGRADE_GARRISON:
@@ -1443,6 +1443,7 @@ bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteria const* achieveme
case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS:
case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY:
+ case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING:
return progress->counter >= requiredAmount;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
@@ -2381,6 +2382,10 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteria const* achieve
break;
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
return false;
+ case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING:
+ if (miscValue1 != achievementCriteria->Entry->Asset.GarrBuildingID)
+ return false;
+ break;
default:
break;
}
@@ -2955,7 +2960,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
CriteriaTreeEntry const* cur = tree;
while (achievementItr == achievementCriteriaTreeIds.end())
{
- if (!tree->Parent)
+ if (!cur->Parent)
break;
cur = sCriteriaTreeStore.LookupEntry(cur->Parent);
@@ -2974,8 +2979,6 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
achievementCriteriaTree->Entry = tree;
_achievementCriteriaTrees[achievementCriteriaTree->Entry->ID] = achievementCriteriaTree;
- if (sCriteriaStore.LookupEntry(tree->CriteriaID))
- _achievementCriteriaTreeByCriteria[tree->CriteriaID].push_back(achievementCriteriaTree);
}
// Build tree
@@ -2986,7 +2989,21 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
auto parent = _achievementCriteriaTrees.find(itr->second->Entry->Parent);
if (parent != _achievementCriteriaTrees.end())
+ {
parent->second->Children.push_back(itr->second);
+ while (parent != _achievementCriteriaTrees.end())
+ {
+ auto cur = parent;
+ parent = _achievementCriteriaTrees.find(parent->second->Entry->Parent);
+ if (parent == _achievementCriteriaTrees.end())
+ {
+ if (sCriteriaStore.LookupEntry(itr->second->Entry->CriteriaID))
+ _achievementCriteriaTreeByCriteria[itr->second->Entry->CriteriaID].push_back(cur->second);
+ }
+ }
+ }
+ else if (sCriteriaStore.LookupEntry(itr->second->Entry->CriteriaID))
+ _achievementCriteriaTreeByCriteria[itr->second->Entry->CriteriaID].push_back(itr->second);
}
// Load criteria
@@ -3013,8 +3030,6 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
for (AchievementCriteriaTree const* tree : treeItr->second)
{
- const_cast<AchievementCriteriaTree*>(tree)->Criteria = achievementCriteria;
-
if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_GUILD)
achievementCriteria->FlagsCu |= ACHIEVEMENT_CRITERIA_FLAG_CU_GUILD;
else if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT)
@@ -3039,6 +3054,9 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
_achievementCriteriasByTimedType[criteria->StartEvent].push_back(achievementCriteria);
}
+ for (auto& p : _achievementCriteriaTrees)
+ const_cast<AchievementCriteriaTree*>(p.second)->Criteria = GetAchievementCriteria(p.second->Entry->CriteriaID);
+
TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime));
}
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index f37526ae71b..2b5c335bce0 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -48,7 +48,7 @@ namespace Trinity
BattlegroundChatBuilder(ChatMsg msgtype, uint32 textId, Player const* source, va_list* args = NULL)
: _msgtype(msgtype), _textId(textId), _source(source), _args(args) { }
- void operator()(WorldPacket& data, LocaleConstant loc_idx)
+ WorldPackets::Packet* operator()(LocaleConstant loc_idx)
{
char const* text = sObjectMgr->GetTrinityString(_textId, loc_idx);
if (_args)
@@ -61,19 +61,18 @@ namespace Trinity
vsnprintf(str, 2048, text, ap);
va_end(ap);
- do_helper(data, &str[0]);
+ return do_helper(&str[0]);
}
- else
- do_helper(data, text);
+
+ return do_helper(text);
}
private:
- void do_helper(WorldPacket& data, char const* text)
+ WorldPackets::Packet* do_helper(char const* text)
{
- WorldPackets::Chat::Chat packet;
- packet.Initialize(_msgtype, LANG_UNIVERSAL, _source, _source, text);
- packet.Write();
- data = packet.Move();
+ WorldPackets::Chat::Chat* packet = new WorldPackets::Chat::Chat();
+ packet->Initialize(_msgtype, LANG_UNIVERSAL, _source, _source, text);
+ return packet;
}
ChatMsg _msgtype;
@@ -88,7 +87,7 @@ namespace Trinity
Battleground2ChatBuilder(ChatMsg msgtype, uint32 textId, Player const* source, uint32 arg1, uint32 arg2)
: _msgtype(msgtype), _textId(textId), _source(source), _arg1(arg1), _arg2(arg2) { }
- void operator()(WorldPacket& data, LocaleConstant loc_idx)
+ WorldPackets::Packet* operator()(LocaleConstant loc_idx)
{
char const* text = sObjectMgr->GetTrinityString(_textId, loc_idx);
char const* arg1str = _arg1 ? sObjectMgr->GetTrinityString(_arg1, loc_idx) : "";
@@ -97,10 +96,9 @@ namespace Trinity
char str[2048];
snprintf(str, 2048, text, arg1str, arg2str);
- WorldPackets::Chat::Chat packet;
- packet.Initialize(_msgtype, LANG_UNIVERSAL, _source, _source, str);
- packet.Write();
- data = packet.Move();
+ WorldPackets::Chat::Chat* packet = new WorldPackets::Chat::Chat();
+ packet->Initialize(_msgtype, LANG_UNIVERSAL, _source, _source, str);
+ return packet;
}
private:
diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp
index 06dd5ff6996..d888485887e 100644
--- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp
@@ -890,7 +890,8 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundTyp
// (after what time the ratings aren't taken into account when making teams) then
// the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account
// else leave the discard time on 0, this way all ratings will be discarded
- uint32 discardTime = getMSTime() - sBattlegroundMgr->GetRatingDiscardTimer();
+ // this has to be signed value - when the server starts, this value would be negative and thus overflow
+ int32 discardTime = getMSTime() - sBattlegroundMgr->GetRatingDiscardTimer();
// we need to find 2 teams which will play next game
GroupsQueueType::iterator itr_teams[BG_TEAMS_COUNT];
@@ -906,7 +907,7 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundTyp
// if group match conditions, then add it to pool
if (!(*itr2)->IsInvitedToBGInstanceGUID
&& (((*itr2)->ArenaMatchmakerRating >= arenaMinRating && (*itr2)->ArenaMatchmakerRating <= arenaMaxRating)
- || (*itr2)->JoinTime < discardTime))
+ || (int32)(*itr2)->JoinTime < discardTime))
{
itr_teams[found++] = itr2;
team = i;
@@ -924,7 +925,7 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundTyp
{
if (!(*itr3)->IsInvitedToBGInstanceGUID
&& (((*itr3)->ArenaMatchmakerRating >= arenaMinRating && (*itr3)->ArenaMatchmakerRating <= arenaMaxRating)
- || (*itr3)->JoinTime < discardTime)
+ || (int32)(*itr3)->JoinTime < discardTime)
&& (*itr_teams[0])->ArenaTeamId != (*itr3)->ArenaTeamId)
{
itr_teams[found++] = itr3;
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 52c52726b73..0c4c621bff2 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -81,6 +81,7 @@ ChatCommand* ChatHandler::getCommandTable()
// cache top-level commands
size_t added = 0;
+ free(commandTableCache);
commandTableCache = (ChatCommand*)malloc(sizeof(ChatCommand) * total);
ASSERT(commandTableCache);
memset(commandTableCache, 0, sizeof(ChatCommand) * total);
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index babbbbce153..c8ccc15019d 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -1565,6 +1565,9 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond)
if (!effect)
continue;
+ if (effect->ChainTargets > 0)
+ continue;
+
switch (effect->TargetA.GetSelectionCategory())
{
case TARGET_SELECT_CATEGORY_NEARBY:
@@ -1585,7 +1588,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond)
break;
}
- TC_LOG_ERROR("sql.sql", "SourceEntry %u SourceGroup %u in `condition` table - spell %u does not have implicit targets of types: _AREA_, _CONE_, _NEARBY_ for effect %u, SourceGroup needs correction, ignoring.", cond->SourceEntry, origGroup, cond->SourceEntry, uint32(i));
+ TC_LOG_ERROR("sql.sql", "SourceEntry %u SourceGroup %u in `condition` table - spell %u does not have implicit targets of types: _AREA_, _CONE_, _NEARBY_, __CHAIN__ for effect %u, SourceGroup needs correction, ignoring.", cond->SourceEntry, origGroup, cond->SourceEntry, uint32(i));
cond->SourceGroup &= ~(1 << i);
}
// all effects were removed, no need to add the condition at all
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index e786c3b9c7c..ba21168086e 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -410,6 +410,8 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
joinData.result = LFG_JOIN_RANDOM_COOLDOWN;
else if (dungeons.empty())
joinData.result = LFG_JOIN_NOT_MEET_REQS;
+ else if (player->HasAura(9454)) // check Freeze debuff
+ joinData.result = LFG_JOIN_NOT_MEET_REQS;
else if (grp)
{
if (grp->GetMembersCount() > MAX_GROUP_SIZE)
@@ -429,6 +431,8 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
joinData.result = LFG_JOIN_PARTY_RANDOM_COOLDOWN;
else if (plrg->InBattleground() || plrg->InArena() || plrg->InBattlegroundQueue())
joinData.result = LFG_JOIN_USING_BG_SYSTEM;
+ else if (plrg->HasAura(9454)) // check Freeze debuff
+ joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS;
++memberCount;
players.insert(plrg->GetGUID());
}
@@ -1293,6 +1297,8 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false*
error = LFG_TELEPORTERROR_IN_VEHICLE;
else if (!player->GetCharmGUID().IsEmpty())
error = LFG_TELEPORTERROR_CHARMING;
+ else if (player->HasAura(9454)) // check Freeze debuff
+ error = LFG_TELEPORTERROR_INVALID_LOCATION;
else if (player->GetMapId() != uint32(dungeon->map)) // Do not teleport players in dungeon to the entrance
{
uint32 mapid = dungeon->map;
diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h
index 5fd0decadaf..34996410fbe 100644
--- a/src/server/game/DungeonFinding/LFGMgr.h
+++ b/src/server/game/DungeonFinding/LFGMgr.h
@@ -98,7 +98,7 @@ enum LfgJoinResult
LFG_JOIN_GROUPFULL = 0x1C, // Your group is full
LFG_JOIN_INTERNAL_ERROR = 0x1E, // Internal LFG Error
LFG_JOIN_NOT_MEET_REQS = 0x1F, // You do not meet the requirements for the chosen dungeons
- //LFG_JOIN_PARTY_NOT_MEET_REQS = 6, // One or more party members do not meet the requirements for the chosen dungeons
+ LFG_JOIN_PARTY_NOT_MEET_REQS = 6, // One or more party members do not meet the requirements for the chosen dungeons (FIXME)
LFG_JOIN_MIXED_RAID_DUNGEON = 0x20, // You cannot mix dungeons, raids, and random when picking dungeons
LFG_JOIN_MULTI_REALM = 0x21, // The dungeon you chose does not support players from multiple realms
LFG_JOIN_DISCONNECTED = 0x22, // One or more party members are pending invites or disconnected
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 70a2ab2307a..a9f91ede933 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -1407,9 +1407,6 @@ bool Creature::CanStartAttack(Unit const* who, bool force) const
if (IsCivilian())
return false;
- if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC))
- return false;
-
// Do not attack non-combat pets
if (who->GetTypeId() == TYPEID_UNIT && who->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET)
return false;
@@ -1547,9 +1544,9 @@ void Creature::setDeathState(DeathState s)
SetUInt64Value(UNIT_NPC_FLAGS, cinfo->npcflag);
ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_IGNORE_PATHFINDING));
SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
- LoadCreaturesAddon(true);
Motion_Initialize();
Unit::setDeathState(ALIVE);
+ LoadCreaturesAddon(true);
}
}
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 948579cd419..7d044d24567 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -1815,6 +1815,9 @@ void GameObject::Use(Unit* user)
return;
}
+ if (Player* player = user->ToPlayer())
+ sOutdoorPvPMgr->HandleCustomSpell(player, spellId, this);
+
if (spellCaster)
spellCaster->CastSpell(user, spellInfo, triggered);
else
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 655af613220..92597cf66ae 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -3112,10 +3112,10 @@ void WorldObject::SetAIAnimKitId(uint16 animKitId)
m_aiAnimKitId = animKitId;
- WorldPacket data(SMSG_SET_AI_ANIM_KIT, 8 + 2);
- data << GetPackGUID();
- data << uint16(animKitId);
- SendMessageToSet(&data, true);
+ WorldPackets::Misc::SetAIAnimKit data;
+ data.Unit = GetGUID();
+ data.AnimKitID = animKitId;
+ SendMessageToSet(data.Write(), true);
}
void WorldObject::SetMovementAnimKitId(uint16 animKitId)
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 3c0510cf3c5..0f4058d6bd2 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -4575,21 +4575,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt64(0, guid);
trans->Append(stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON);
- stmt->setUInt64(0, guid);
- trans->Append(stmt);
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BLUEPRINTS);
- stmt->setUInt64(0, guid);
- trans->Append(stmt);
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BUILDINGS);
- stmt->setUInt64(0, guid);
- trans->Append(stmt);
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS);
- stmt->setUInt64(0, guid);
- trans->Append(stmt);
+ Garrison::DeleteFromDB(guid, trans);
CharacterDatabase.CommitTransaction(trans);
@@ -23656,8 +23642,8 @@ uint32 Player::GetResurrectionSpellId()
}
}
- // Reincarnation (passive spell) // prio: 1 // Glyph of Renewed Life
- if (prio < 1 && HasSpell(20608) && !GetSpellHistory()->HasCooldown(21169) && (HasAura(58059) || HasItemCount(17030)))
+ // Reincarnation (passive spell) // prio: 1
+ if (prio < 1 && HasSpell(20608) && !GetSpellHistory()->HasCooldown(21169))
spell_id = 21169;
return spell_id;
@@ -23768,6 +23754,9 @@ bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
if (player->GetMapId() != pRewardSource->GetMapId() || player->GetInstanceId() != pRewardSource->GetInstanceId())
return false;
+ if (player->GetMap()->IsDungeon())
+ return true;
+
return pRewardSource->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE);
}
@@ -26205,6 +26194,15 @@ void Player::CreateGarrison(uint32 garrSiteId)
_garrison = std::move(garrison);
}
+void Player::DeleteGarrison()
+{
+ if (_garrison)
+ {
+ _garrison->Delete();
+ _garrison.reset();
+ }
+}
+
void Player::SendMovementSetCanTransitionBetweenSwimAndFly(bool apply)
{
WorldPackets::Movement::MoveSetFlag packet(apply ? SMSG_MOVE_ENABLE_TRANSITION_BETWEEN_SWIM_AND_FLY : SMSG_MOVE_DISABLE_TRANSITION_BETWEEN_SWIM_AND_FLY);
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 5bae50edb68..c91861a98e5 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1966,7 +1966,7 @@ class Player : public Unit, public GridObject<Player>
if (!IsResurrectRequested())
return false;
- return _resurrectionData->GUID == guid;
+ return !_resurrectionData->GUID.IsEmpty() && _resurrectionData->GUID == guid;
}
bool IsResurrectRequested() const { return _resurrectionData != NULL; }
@@ -2629,6 +2629,7 @@ class Player : public Unit, public GridObject<Player>
void OnCombatExit();
void CreateGarrison(uint32 garrSiteId);
+ void DeleteGarrison();
Garrison* GetGarrison() { return _garrison.get(); }
protected:
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index e61c23c8c37..1e6ee06e661 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -363,6 +363,7 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat
go->m_movementInfo.transport.pos.Relocate(x, y, z, o);
CalculatePassengerPosition(x, y, z, &o);
go->Relocate(x, y, z, o);
+ go->RelocateStationaryPosition(x, y, z, o);
if (!go->IsPositionValid())
{
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 4c61f23126f..e6d69115a13 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -10403,6 +10403,13 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
/// @todo possible affect only on MOVE_RUN
if (int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
{
+ if (Creature* creature = ToCreature())
+ {
+ uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask;
+ if (immuneMask & (1 << MECHANIC_SNARE) || immuneMask & (1 << MECHANIC_DAZE))
+ break;
+ }
+
// Use speed from aura
float max_speed = normalization / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
if (speed > max_speed)
@@ -10424,15 +10431,15 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
// Apply strongest slow aura mod to speed
int32 slow = GetMaxNegativeAuraModifier(SPELL_AURA_MOD_DECREASE_SPEED);
if (slow)
- {
AddPct(speed, slow);
- if (float minSpeedMod = (float)GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MINIMUM_SPEED))
- {
- float min_speed = minSpeedMod / 100.0f;
- if (speed < min_speed)
- speed = min_speed;
- }
+
+ if (float minSpeedMod = (float)GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MINIMUM_SPEED))
+ {
+ float min_speed = minSpeedMod / 100.0f;
+ if (speed < min_speed)
+ speed = min_speed;
}
+
SetSpeed(mtype, speed, forced);
}
@@ -13680,18 +13687,11 @@ void Unit::SetControlled(bool apply, UnitState state)
{
case UNIT_STATE_STUNNED:
SetStunned(true);
- // i need to stop fear on stun and root or i will get teleport to destination issue as MVMGEN for fear keeps going on
- if (HasUnitState(UNIT_STATE_FLEEING))
- SetFeared(false);
CastStop();
break;
case UNIT_STATE_ROOT:
if (!HasUnitState(UNIT_STATE_STUNNED))
- {
SetRooted(true);
- if (HasUnitState(UNIT_STATE_FLEEING))
- SetFeared(false);
- }
break;
case UNIT_STATE_CONFUSED:
if (!HasUnitState(UNIT_STATE_STUNNED))
@@ -13775,10 +13775,9 @@ void Unit::SetStunned(bool apply)
SetTarget(ObjectGuid::Empty);
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
- // Creature specific
- if (GetTypeId() != TYPEID_PLAYER)
- StopMoving();
- else
+ StopMoving();
+
+ if (GetTypeId() == TYPEID_PLAYER)
SetStandState(UNIT_STAND_STATE_STAND);
SetRooted(true);
@@ -13811,6 +13810,7 @@ void Unit::SetRooted(bool apply, bool packetOnly /*= false*/)
// setting MOVEMENTFLAG_ROOT
RemoveUnitMovementFlag(MOVEMENTFLAG_MASK_MOVING);
AddUnitMovementFlag(MOVEMENTFLAG_ROOT);
+ StopMoving();
}
else
RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT);
@@ -16004,28 +16004,11 @@ void Unit::SendSetVehicleRecId(uint32 vehicleId)
void Unit::SendSetPlayHoverAnim(bool enable)
{
- ObjectGuid guid = GetGUID();
- WorldPacket data(SMSG_SET_PLAY_HOVER_ANIM, 10);
- data.WriteBit(guid[4]);
- data.WriteBit(guid[0]);
- data.WriteBit(guid[1]);
- data.WriteBit(enable);
- data.WriteBit(guid[3]);
- data.WriteBit(guid[7]);
- data.WriteBit(guid[5]);
- data.WriteBit(guid[2]);
- data.WriteBit(guid[6]);
+ WorldPackets::Misc::SetPlayHoverAnim data;
+ data.UnitGUID = GetGUID();
+ data.PlayHoverAnim = enable;
- data.WriteByteSeq(guid[3]);
- data.WriteByteSeq(guid[2]);
- data.WriteByteSeq(guid[1]);
- data.WriteByteSeq(guid[7]);
- data.WriteByteSeq(guid[0]);
- data.WriteByteSeq(guid[5]);
- data.WriteByteSeq(guid[4]);
- data.WriteByteSeq(guid[6]);
-
- SendMessageToSet(&data, true);
+ SendMessageToSet(data.Write(), true);
}
bool Unit::IsSplineEnabled() const
diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h
index 5569fcbf353..d0221e3e812 100644
--- a/src/server/game/Entities/Vehicle/VehicleDefines.h
+++ b/src/server/game/Entities/Vehicle/VehicleDefines.h
@@ -129,7 +129,6 @@ public:
/// This method transforms supplied global coordinates into local offsets
virtual void CalculatePassengerOffset(float& x, float& y, float& z, float* o = NULL) const = 0;
-protected:
static void CalculatePassengerPosition(float& x, float& y, float& z, float* o, float transX, float transY, float transZ, float transO)
{
float inx = x, iny = y, inz = z;
diff --git a/src/server/game/Garrison/Garrison.cpp b/src/server/game/Garrison/Garrison.cpp
index 749ebb562c5..7040ae65b19 100644
--- a/src/server/game/Garrison/Garrison.cpp
+++ b/src/server/game/Garrison/Garrison.cpp
@@ -16,10 +16,12 @@
*/
#include "Garrison.h"
+#include "Creature.h"
#include "GameObject.h"
#include "GarrisonMgr.h"
#include "MapManager.h"
#include "ObjectMgr.h"
+#include "VehicleDefines.h"
Garrison::Garrison(Player* owner) : _owner(owner), _siteLevel(nullptr), _followerActivationsRemainingToday(1)
{
@@ -133,25 +135,11 @@ bool Garrison::LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blue
return true;
}
-void Garrison::SaveToDB(SQLTransaction& trans)
+void Garrison::SaveToDB(SQLTransaction trans)
{
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON);
- stmt->setUInt64(0, _owner->GetGUID().GetCounter());
- trans->Append(stmt);
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BLUEPRINTS);
- stmt->setUInt64(0, _owner->GetGUID().GetCounter());
- trans->Append(stmt);
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BUILDINGS);
- stmt->setUInt64(0, _owner->GetGUID().GetCounter());
- trans->Append(stmt);
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS);
- stmt->setUInt64(0, _owner->GetGUID().GetCounter());
- trans->Append(stmt);
+ DeleteFromDB(_owner->GetGUID().GetCounter(), trans);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON);
stmt->setUInt64(0, _owner->GetGUID().GetCounter());
stmt->setUInt32(1, _siteLevel->ID);
stmt->setUInt32(2, _followerActivationsRemainingToday);
@@ -210,6 +198,25 @@ void Garrison::SaveToDB(SQLTransaction& trans)
}
}
+void Garrison::DeleteFromDB(ObjectGuid::LowType ownerGuid, SQLTransaction trans)
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON);
+ stmt->setUInt64(0, ownerGuid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BLUEPRINTS);
+ stmt->setUInt64(0, ownerGuid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BUILDINGS);
+ stmt->setUInt64(0, ownerGuid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS);
+ stmt->setUInt64(0, ownerGuid);
+ trans->Append(stmt);
+}
+
bool Garrison::Create(uint32 garrSiteId)
{
_siteLevel = sGarrisonMgr.GetGarrSiteLevelEntry(garrSiteId, 1);
@@ -226,6 +233,18 @@ bool Garrison::Create(uint32 garrSiteId)
return true;
}
+void Garrison::Delete()
+{
+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
+ DeleteFromDB(_owner->GetGUID().GetCounter(), trans);
+ CharacterDatabase.CommitTransaction(trans);
+
+ WorldPackets::Garrison::GarrisonDeleteResult garrisonDelete;
+ garrisonDelete.Result = GARRISON_SUCCESS;
+ garrisonDelete.GarrSiteID = _siteLevel->SiteID;
+ _owner->SendDirectMessage(garrisonDelete.Write());
+}
+
void Garrison::InitializePlots()
{
if (std::vector<GarrSiteLevelPlotInstEntry const*> const* plots = sGarrisonMgr.GetGarrPlotInstForSiteLevel(_siteLevel->ID))
@@ -378,6 +397,8 @@ void Garrison::PlaceBuilding(uint32 garrPlotInstanceId, uint32 garrBuildingId)
buildingRemoved.GarrBuildingID = oldBuildingId;
_owner->SendDirectMessage(buildingRemoved.Write());
}
+
+ _owner->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING, garrBuildingId);
}
_owner->SendDirectMessage(placeBuildingResult.Write());
@@ -431,6 +452,27 @@ void Garrison::CancelBuildingConstruction(uint32 garrPlotInstanceId)
_owner->SendDirectMessage(buildingRemoved.Write());
}
+void Garrison::ActivateBuilding(uint32 garrPlotInstanceId)
+{
+ if (Plot* plot = GetPlot(garrPlotInstanceId))
+ {
+ if (plot->BuildingInfo.CanActivate() && plot->BuildingInfo.PacketInfo && !plot->BuildingInfo.PacketInfo->Active)
+ {
+ plot->BuildingInfo.PacketInfo->Active = true;
+ if (Map* map = FindMap())
+ {
+ plot->DeleteGameObject(map);
+ if (GameObject* go = plot->CreateGameObject(map, GetFaction()))
+ map->AddToMap(go);
+ }
+
+ WorldPackets::Garrison::GarrisonBuildingActivated buildingActivated;
+ buildingActivated.GarrPlotInstanceID = garrPlotInstanceId;
+ _owner->SendDirectMessage(buildingActivated.Write());
+ }
+ }
+}
+
void Garrison::AddFollower(uint32 garrFollowerId)
{
WorldPackets::Garrison::GarrisonAddFollowerResult addFollowerResult;
@@ -607,6 +649,40 @@ GarrisonError Garrison::CheckBuildingRemoval(uint32 garrPlotInstanceId) const
return GARRISON_SUCCESS;
}
+template<class T, void(T::*SecondaryRelocate)(float,float,float,float)>
+T* BuildingSpawnHelper(GameObject* building, ObjectGuid::LowType spawnId, Map* map)
+{
+ T* spawn = new T();
+ if (!spawn->LoadFromDB(spawnId, map))
+ {
+ delete spawn;
+ return nullptr;
+ }
+
+ float x = spawn->GetPositionX();
+ float y = spawn->GetPositionY();
+ float z = spawn->GetPositionZ();
+ float o = spawn->GetOrientation();
+ TransportBase::CalculatePassengerPosition(x, y, z, &o, building->GetPositionX(), building->GetPositionY(), building->GetPositionZ(), building->GetOrientation());
+
+ spawn->Relocate(x, y, z, o);
+ (spawn->*SecondaryRelocate)(x, y, z, o);
+
+ if (!spawn->IsPositionValid())
+ {
+ delete spawn;
+ return nullptr;
+ }
+
+ if (!map->AddToMap(spawn))
+ {
+ delete spawn;
+ return nullptr;
+ }
+
+ return spawn;
+}
+
GameObject* Garrison::Plot::CreateGameObject(Map* map, GarrisonFactionIndex faction)
{
uint32 entry = EmptyGameObjectId;
@@ -615,10 +691,9 @@ GameObject* Garrison::Plot::CreateGameObject(Map* map, GarrisonFactionIndex fact
GarrPlotInstanceEntry const* plotInstance = sGarrPlotInstanceStore.AssertEntry(PacketInfo.GarrPlotInstanceID);
GarrPlotEntry const* plot = sGarrPlotStore.AssertEntry(plotInstance->GarrPlotID);
GarrBuildingEntry const* building = sGarrBuildingStore.AssertEntry(BuildingInfo.PacketInfo->GarrBuildingID);
- if (BuildingInfo.PacketInfo->Active)
+ entry = faction == GARRISON_FACTION_INDEX_HORDE ? plot->HordeConstructionGameObjectID : plot->AllianceConstructionGameObjectID;
+ if (BuildingInfo.PacketInfo->Active || !entry)
entry = faction == GARRISON_FACTION_INDEX_HORDE ? building->HordeGameObjectID : building->AllianceGameObjectID;
- else
- entry = faction == GARRISON_FACTION_INDEX_HORDE ? plot->HordeConstructionGameObjectID : plot->AllianceConstructionGameObjectID;
}
if (!sObjectMgr->GetGameObjectTemplate(entry))
@@ -628,16 +703,53 @@ GameObject* Garrison::Plot::CreateGameObject(Map* map, GarrisonFactionIndex fact
}
Position const& pos = PacketInfo.PlotPos;
- GameObject* go = new GameObject();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, 0, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(),
- 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_ACTIVE))
+ GameObject* building = new GameObject();
+ if (!building->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, 0, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(),
+ 0.0f, 0.0f, 0.0f, 0.0f, 255, GO_STATE_READY))
{
- delete go;
+ delete building;
return nullptr;
}
- BuildingInfo.Guid = go->GetGUID();
- return go;
+ if (BuildingInfo.CanActivate() && BuildingInfo.PacketInfo && !BuildingInfo.PacketInfo->Active)
+ {
+ if (FinalizeGarrisonPlotGOInfo const* finalizeInfo = sGarrisonMgr.GetPlotFinalizeGOInfo(PacketInfo.GarrPlotInstanceID))
+ {
+ Position const& pos2 = finalizeInfo->FactionInfo[faction].Pos;
+ GameObject* finalizer = new GameObject();
+ if (finalizer->Create(map->GenerateLowGuid<HighGuid::GameObject>(), finalizeInfo->FactionInfo[faction].GameObjectId, map, 0, pos2.GetPositionX(), pos2.GetPositionY(),
+ pos2.GetPositionZ(), pos2.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 255, GO_STATE_READY))
+ {
+ // set some spell id to make the object delete itself after use
+ finalizer->SetSpellId(finalizer->GetGOInfo()->goober.spell);
+ finalizer->SetRespawnTime(0);
+
+ if (uint16 animKit = finalizeInfo->FactionInfo[faction].AnimKitId)
+ finalizer->SetAIAnimKitId(animKit);
+
+ map->AddToMap(finalizer);
+ }
+ else
+ delete finalizer;
+ }
+ }
+
+ if (building->GetGoType() == GAMEOBJECT_TYPE_GARRISON_BUILDING && building->GetGOInfo()->garrisonBuilding.mapID)
+ {
+ for (CellObjectGuidsMap::value_type const& cellGuids : sObjectMgr->GetMapObjectGuids(building->GetGOInfo()->garrisonBuilding.mapID, map->GetSpawnMode()))
+ {
+ for (ObjectGuid::LowType spawnId : cellGuids.second.creatures)
+ if (Creature* spawn = BuildingSpawnHelper<Creature, &Creature::SetHomePosition>(building, spawnId, map))
+ BuildingInfo.Spawns.insert(spawn->GetGUID());
+
+ for (ObjectGuid::LowType spawnId : cellGuids.second.gameobjects)
+ if (GameObject* spawn = BuildingSpawnHelper<GameObject, &GameObject::RelocateStationaryPosition>(building, spawnId, map))
+ BuildingInfo.Spawns.insert(spawn->GetGUID());
+ }
+ }
+
+ BuildingInfo.Guid = building->GetGUID();
+ return building;
}
void Garrison::Plot::DeleteGameObject(Map* map)
@@ -645,6 +757,27 @@ void Garrison::Plot::DeleteGameObject(Map* map)
if (BuildingInfo.Guid.IsEmpty())
return;
+ for (ObjectGuid const& guid : BuildingInfo.Spawns)
+ {
+ WorldObject* object = nullptr;
+ switch (guid.GetHigh())
+ {
+ case HighGuid::Creature:
+ object = map->GetCreature(guid);
+ break;
+ case HighGuid::GameObject:
+ object = map->GetGameObject(guid);
+ break;
+ default:
+ continue;
+ }
+
+ if (object)
+ object->AddObjectToRemoveList();
+ }
+
+ BuildingInfo.Spawns.clear();
+
if (GameObject* oldBuilding = map->GetGameObject(BuildingInfo.Guid))
oldBuilding->Delete();
diff --git a/src/server/game/Garrison/Garrison.h b/src/server/game/Garrison/Garrison.h
index 71d67a1b6b5..7123854cbc6 100644
--- a/src/server/game/Garrison/Garrison.h
+++ b/src/server/game/Garrison/Garrison.h
@@ -82,6 +82,7 @@ public:
bool CanActivate() const;
ObjectGuid Guid;
+ std::unordered_set<ObjectGuid> Spawns;
Optional<WorldPackets::Garrison::GarrisonBuildingInfo> PacketInfo;
};
@@ -109,9 +110,11 @@ public:
bool LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blueprints, PreparedQueryResult buildings,
PreparedQueryResult followers, PreparedQueryResult abilities);
- void SaveToDB(SQLTransaction& trans);
+ void SaveToDB(SQLTransaction trans);
+ static void DeleteFromDB(ObjectGuid::LowType ownerGuid, SQLTransaction trans);
bool Create(uint32 garrSiteId);
+ void Delete();
void Upgrade();
void Enter() const;
@@ -129,6 +132,7 @@ public:
void UnlearnBlueprint(uint32 garrBuildingId);
void PlaceBuilding(uint32 garrPlotInstanceId, uint32 garrBuildingId);
void CancelBuildingConstruction(uint32 garrPlotInstanceId);
+ void ActivateBuilding(uint32 garrPlotInstanceId);
// Followers
void AddFollower(uint32 garrFollowerId);
diff --git a/src/server/game/Garrison/GarrisonMgr.cpp b/src/server/game/Garrison/GarrisonMgr.cpp
index f95acef952c..a27574f4df4 100644
--- a/src/server/game/Garrison/GarrisonMgr.cpp
+++ b/src/server/game/Garrison/GarrisonMgr.cpp
@@ -21,6 +21,8 @@
#include "Garrison.h"
#include "ObjectDefines.h"
#include "World.h"
+#include "GameObject.h"
+#include "ObjectMgr.h"
void GarrisonMgr::Initialize()
{
@@ -58,6 +60,7 @@ void GarrisonMgr::Initialize()
}
InitializeDbIdSequences();
+ LoadPlotFinalizeGOInfo();
LoadFollowerClassSpecAbilities();
}
@@ -121,6 +124,15 @@ GarrBuildingEntry const* GarrisonMgr::GetPreviousLevelBuilding(uint32 buildingTy
return nullptr;
}
+FinalizeGarrisonPlotGOInfo const* GarrisonMgr::GetPlotFinalizeGOInfo(uint32 garrPlotInstanceID) const
+{
+ auto itr = _finalizePlotGOInfo.find(garrPlotInstanceID);
+ if (itr != _finalizePlotGOInfo.end())
+ return &itr->second;
+
+ return nullptr;
+}
+
uint64 GarrisonMgr::GenerateFollowerDbId()
{
if (_followerDbIdGenerator >= std::numeric_limits<uint64>::max())
@@ -312,6 +324,92 @@ void GarrisonMgr::InitializeDbIdSequences()
_followerDbIdGenerator = (*result)[0].GetUInt64() + 1;
}
+void GarrisonMgr::LoadPlotFinalizeGOInfo()
+{
+ // 0 1 2 3 4 5 6
+ QueryResult result = WorldDatabase.Query("SELECT garrPlotInstanceId, hordeGameObjectId, hordeX, hordeY, hordeZ, hordeO, hordeAnimKitId, "
+ // 7 8 9 10 11 12
+ "allianceGameObjectId, allianceX, allianceY, allianceZ, allianceO, allianceAnimKitId FROM garrison_plot_finalize_info");
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 garrison follower class spec abilities. DB table `garrison_plot_finalize_info` is empty.");
+ return;
+ }
+
+ uint32 msTime = getMSTime();
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 garrPlotInstanceId = fields[0].GetUInt32();
+ uint32 hordeGameObjectId = fields[1].GetUInt32();
+ uint32 allianceGameObjectId = fields[7].GetUInt32();
+ uint16 hordeAnimKitId = fields[6].GetUInt16();
+ uint16 allianceAnimKitId = fields[12].GetUInt16();
+
+ if (!sGarrPlotInstanceStore.LookupEntry(garrPlotInstanceId))
+ {
+ TC_LOG_ERROR("sql.sql", "Non-existing GarrPlotInstance.db2 entry %u was referenced in `garrison_plot_finalize_info`.", garrPlotInstanceId);
+ continue;
+ }
+
+ GameObjectTemplate const* goTemplate = sObjectMgr->GetGameObjectTemplate(hordeGameObjectId);
+ if (!goTemplate)
+ {
+ TC_LOG_ERROR("sql.sql", "Non-existing gameobject_template entry %u was referenced in `garrison_plot_finalize_info`.`hordeGameObjectId` for garrPlotInstanceId %u.",
+ hordeGameObjectId, garrPlotInstanceId);
+ continue;
+ }
+
+ if (goTemplate->type != GAMEOBJECT_TYPE_GOOBER)
+ {
+ TC_LOG_ERROR("sql.sql", "Invalid gameobject type %u (entry %u) was referenced in `garrison_plot_finalize_info`.`hordeGameObjectId` for garrPlotInstanceId %u.",
+ goTemplate->type, hordeGameObjectId, garrPlotInstanceId);
+ continue;
+ }
+
+ goTemplate = sObjectMgr->GetGameObjectTemplate(allianceGameObjectId);
+ if (!goTemplate)
+ {
+ TC_LOG_ERROR("sql.sql", "Non-existing gameobject_template entry %u was referenced in `garrison_plot_finalize_info`.`allianceGameObjectId` for garrPlotInstanceId %u.",
+ allianceGameObjectId, garrPlotInstanceId);
+ continue;
+ }
+
+ if (goTemplate->type != GAMEOBJECT_TYPE_GOOBER)
+ {
+ TC_LOG_ERROR("sql.sql", "Invalid gameobject type %u (entry %u) was referenced in `garrison_plot_finalize_info`.`allianceGameObjectId` for garrPlotInstanceId %u.",
+ goTemplate->type, allianceGameObjectId, garrPlotInstanceId);
+ continue;
+ }
+
+ if (hordeAnimKitId && !sAnimKitStore.LookupEntry(hordeAnimKitId))
+ {
+ TC_LOG_ERROR("sql.sql", "Non-existing AnimKit.dbc entry %u was referenced in `garrison_plot_finalize_info`.`hordeAnimKitId` for garrPlotInstanceId %u.",
+ hordeAnimKitId, garrPlotInstanceId);
+ continue;
+ }
+
+ if (allianceAnimKitId && !sAnimKitStore.LookupEntry(allianceAnimKitId))
+ {
+ TC_LOG_ERROR("sql.sql", "Non-existing AnimKit.dbc entry %u was referenced in `garrison_plot_finalize_info`.`allianceAnimKitId` for garrPlotInstanceId %u.",
+ allianceAnimKitId, garrPlotInstanceId);
+ continue;
+ }
+
+ FinalizeGarrisonPlotGOInfo& info = _finalizePlotGOInfo[garrPlotInstanceId];
+ info.FactionInfo[GARRISON_FACTION_INDEX_HORDE].GameObjectId = hordeGameObjectId;
+ info.FactionInfo[GARRISON_FACTION_INDEX_HORDE].Pos.Relocate(fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat());
+ info.FactionInfo[GARRISON_FACTION_INDEX_HORDE].AnimKitId = hordeAnimKitId;
+
+ info.FactionInfo[GARRISON_FACTION_INDEX_ALLIANCE].GameObjectId = allianceGameObjectId;
+ info.FactionInfo[GARRISON_FACTION_INDEX_ALLIANCE].Pos.Relocate(fields[8].GetFloat(), fields[9].GetFloat(), fields[10].GetFloat(), fields[11].GetFloat());
+ info.FactionInfo[GARRISON_FACTION_INDEX_ALLIANCE].AnimKitId = allianceAnimKitId;
+
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u garrison plot finalize entries in %u.", uint32(_finalizePlotGOInfo.size()), GetMSTimeDiffToNow(msTime));
+}
+
void GarrisonMgr::LoadFollowerClassSpecAbilities()
{
QueryResult result = WorldDatabase.Query("SELECT classSpecId, abilityId FROM garrison_follower_class_spec_abilities");
@@ -321,6 +419,7 @@ void GarrisonMgr::LoadFollowerClassSpecAbilities()
return;
}
+ uint32 msTime = getMSTime();
uint32 count = 0;
do
{
@@ -349,5 +448,5 @@ void GarrisonMgr::LoadFollowerClassSpecAbilities()
for (auto& pair : _garrisonFollowerClassSpecAbilities)
pair.second.sort();
- TC_LOG_INFO("server.loading", ">> Loaded %u garrison follower class spec abilities.", count);
+ TC_LOG_INFO("server.loading", ">> Loaded %u garrison follower class spec abilities in %u.", count, GetMSTimeDiffToNow(msTime));
}
diff --git a/src/server/game/Garrison/GarrisonMgr.h b/src/server/game/Garrison/GarrisonMgr.h
index 63810e3c3ab..3ef90cc7e66 100644
--- a/src/server/game/Garrison/GarrisonMgr.h
+++ b/src/server/game/Garrison/GarrisonMgr.h
@@ -20,6 +20,17 @@
#include "DB2Stores.h"
#include <unordered_set>
+#include "Position.h"
+
+struct FinalizeGarrisonPlotGOInfo
+{
+ struct
+ {
+ uint32 GameObjectId;
+ Position Pos;
+ uint16 AnimKitId;
+ } FactionInfo[2];
+};
struct GarrAbilities
{
@@ -44,12 +55,14 @@ public:
bool IsPlotMatchingBuilding(uint32 garrPlotId, uint32 garrBuildingId) const;
uint32 GetGarrBuildingPlotInst(uint32 garrBuildingId, uint32 garrSiteLevelPlotInstId) const;
GarrBuildingEntry const* GetPreviousLevelBuilding(uint32 buildingType, uint32 currentLevel) const;
+ FinalizeGarrisonPlotGOInfo const* GetPlotFinalizeGOInfo(uint32 garrPlotInstanceID) const;
uint64 GenerateFollowerDbId();
std::list<GarrAbilityEntry const*> RollFollowerAbilities(GarrFollowerEntry const* follower, uint32 quality, uint32 faction, bool initial) const;
std::list<GarrAbilityEntry const*> GetClassSpecAbilities(GarrFollowerEntry const* follower, uint32 faction) const;
private:
void InitializeDbIdSequences();
+ void LoadPlotFinalizeGOInfo();
void LoadFollowerClassSpecAbilities();
std::unordered_map<uint32 /*garrSiteId*/, std::vector<GarrSiteLevelPlotInstEntry const*>> _garrisonPlotInstBySiteLevel;
@@ -57,6 +70,7 @@ private:
std::unordered_map<uint32 /*garrPlotId*/, std::unordered_set<uint32/*garrBuildingId*/>> _garrisonBuildingsByPlot;
std::unordered_map<uint64 /*garrBuildingId | garrSiteLevelPlotInstId << 32*/, uint32 /*garrBuildingPlotInstId*/> _garrisonBuildingPlotInstances;
std::unordered_map<uint32 /*buildingType*/, std::vector<GarrBuildingEntry const*>> _garrisonBuildingsByType;
+ std::unordered_map<uint32 /*garrPlotInstanceId*/, FinalizeGarrisonPlotGOInfo> _finalizePlotGOInfo;
std::unordered_map<uint32 /*garrFollowerId*/, GarrAbilities> _garrisonFollowerAbilities[2];
std::unordered_map<uint32 /*classSpecId*/, std::list<GarrAbilityEntry const*>> _garrisonFollowerClassSpecAbilities;
std::set<GarrAbilityEntry const*> _garrisonFollowerRandomTraits;
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index bdfd66429cb..fd160532616 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -6678,6 +6678,10 @@ void ObjectMgr::LoadGameObjectTemplate()
case GAMEOBJECT_TYPE_BARBER_CHAIR: //32
CheckAndFixGOChairHeightId(&got, got.barberChair.chairheight, 0);
break;
+ case GAMEOBJECT_TYPE_GARRISON_BUILDING:
+ if (uint32 transportMap = got.garrisonBuilding.mapID)
+ _transportMaps.insert(transportMap);
+ break;
}
++count;
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index aea2d8ec383..e6a574efe74 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -33,7 +33,7 @@
#include "CreatureAI.h"
#include "Spell.h"
#include "WorldSession.h"
-#include "Packets/ChatPackets.h"
+#include "Packet.h"
class Player;
//class Map;
@@ -1411,7 +1411,7 @@ namespace Trinity
private:
Builder& i_builder;
- std::vector<WorldPacket*> i_data_cache; // 0 = default, i => i-1 locale index
+ std::vector<WorldPackets::Packet*> i_data_cache; // 0 = default, i => i-1 locale index
};
// Prepare using Builder localized packets with caching and send to player
@@ -1419,7 +1419,7 @@ namespace Trinity
class LocalizedPacketListDo
{
public:
- typedef std::vector<WorldPacket*> WorldPacketList;
+ typedef std::vector<WorldPackets::Packet*> WorldPacketList;
explicit LocalizedPacketListDo(Builder& builder) : i_builder(builder) { }
~LocalizedPacketListDo()
diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h
index 30c5e523f21..3e00cb4741a 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h
@@ -563,7 +563,7 @@ void Trinity::LocalizedPacketDo<Builder>::operator()(Player* p)
{
LocaleConstant loc_idx = p->GetSession()->GetSessionDbLocaleIndex();
uint32 cache_idx = loc_idx+1;
- WorldPacket* data;
+ WorldPackets::Packet* data;
// create if not cached yet
if (i_data_cache.size() < cache_idx + 1 || !i_data_cache[cache_idx])
@@ -571,18 +571,18 @@ void Trinity::LocalizedPacketDo<Builder>::operator()(Player* p)
if (i_data_cache.size() < cache_idx + 1)
i_data_cache.resize(cache_idx + 1);
- data = new WorldPacket();
+ data = i_builder(loc_idx);
- i_builder(*data, loc_idx);
+ ASSERT(data->GetSize() == 0);
- ASSERT(data->GetOpcode() != NULL_OPCODE);
+ data->Write();
i_data_cache[cache_idx] = data;
}
else
data = i_data_cache[cache_idx];
- p->SendDirectMessage(data);
+ p->SendDirectMessage(data->GetRawPacket());
}
template<class Builder>
@@ -606,7 +606,7 @@ void Trinity::LocalizedPacketListDo<Builder>::operator()(Player* p)
data_list = &i_data_cache[cache_idx];
for (size_t i = 0; i < data_list->size(); ++i)
- p->SendDirectMessage((*data_list)[i]);
+ p->SendDirectMessage((*data_list)[i]->GetRawPacket());
}
#endif // TRINITY_GRIDNOTIFIERSIMPL_H
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index fadf7e1970e..dca2a887200 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -979,7 +979,7 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject)
continue;
if (i->AllowedForPlayer(member))
{
- if (member->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (member->IsAtGroupRewardDistance(pLootedObject))
{
r->totalPlayersRolling++;
@@ -1064,7 +1064,7 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject)
if (i->AllowedForPlayer(member))
{
- if (member->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (member->IsAtGroupRewardDistance(pLootedObject))
{
r->totalPlayersRolling++;
r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
@@ -1123,7 +1123,7 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject)
continue;
bool allowedForPlayer = i->AllowedForPlayer(playerToRoll);
- if (allowedForPlayer && playerToRoll->IsWithinDistInMap(lootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (allowedForPlayer && playerToRoll->IsAtGroupRewardDistance(lootedObject))
{
r->totalPlayersRolling++;
if (playerToRoll->GetPassOnGroupLoot())
@@ -1198,7 +1198,7 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject)
continue;
bool allowedForPlayer = i->AllowedForPlayer(playerToRoll);
- if (allowedForPlayer && playerToRoll->IsWithinDistInMap(lootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (allowedForPlayer && playerToRoll->IsAtGroupRewardDistance(lootedObject))
{
r->totalPlayersRolling++;
r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
@@ -1274,7 +1274,7 @@ void Group::MasterLoot(Loot* loot, WorldObject* pLootedObject)
if (!looter->IsInWorld())
continue;
- if (looter->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (looter->IsAtGroupRewardDistance(pLootedObject))
{
data << looter->GetGUID();
++real_count;
@@ -1286,7 +1286,7 @@ void Group::MasterLoot(Loot* loot, WorldObject* pLootedObject)
for (GroupReference* itr = GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* looter = itr->GetSource();
- if (looter->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (looter->IsAtGroupRewardDistance(pLootedObject))
looter->GetSession()->SendPacket(&data);
}
}
@@ -1835,7 +1835,7 @@ void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed)
{
// not update if only update if need and ok
Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid);
- if (looter && looter->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (looter && looter->IsAtGroupRewardDistance(pLootedObject))
return;
}
++guid_itr;
@@ -1846,7 +1846,7 @@ void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed)
for (member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr)
{
if (Player* player = ObjectAccessor::FindPlayer(itr->guid))
- if (player->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (player->IsAtGroupRewardDistance(pLootedObject))
{
pNewLooter = player;
break;
@@ -1859,7 +1859,7 @@ void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed)
for (member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr)
{
if (Player* player = ObjectAccessor::FindPlayer(itr->guid))
- if (player->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (player->IsAtGroupRewardDistance(pLootedObject))
{
pNewLooter = player;
break;
@@ -1952,6 +1952,9 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const*
// check if someone in party is using dungeon system
if (member->isUsingLfg())
return ERR_LFG_CANT_USE_BATTLEGROUND;
+ // check Freeze debuff
+ if (member->HasAura(9454))
+ return ERR_BATTLEGROUND_JOIN_FAILED;
}
// only check for MinPlayerCount since MinPlayerCount == MaxPlayerCount for arenas...
diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp
index ced3265a31c..c064c0cd555 100644
--- a/src/server/game/Handlers/BattleGroundHandler.cpp
+++ b/src/server/game/Handlers/BattleGroundHandler.cpp
@@ -153,6 +153,10 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPackets::Battleground::Batt
return;
}
+ // check Freeze debuff
+ if (_player->HasAura(9454))
+ return;
+
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0);
@@ -338,6 +342,10 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPackets::Battleground::Battl
if (battlefieldPort.AcceptedInvite)
{
+ // check Freeze debuff
+ if (_player->HasAura(9454))
+ return;
+
if (!_player->IsInvitedForBattlegroundQueueType(bgQueueTypeId))
return; // cheating?
diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp
index eefe89fef74..9df20126790 100644
--- a/src/server/game/Handlers/GroupHandler.cpp
+++ b/src/server/game/Handlers/GroupHandler.cpp
@@ -433,7 +433,7 @@ void WorldSession::HandleUpdateRaidTargetOpcode(WorldPackets::Party::UpdateRaidT
if (!group)
return;
- if (packet.Symbol == 0xFF) // target icon request
+ if (packet.Symbol == -1) // target icon request
group->SendTargetIconList(this, packet.PartyIndex);
else // target icon update
{
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index ddd084cbd9d..d53c20329b3 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -178,7 +178,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet
if (!member)
continue;
- if (player->IsWithinDistInMap(member, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
+ if (player->IsAtGroupRewardDistance(member))
playersNear.push_back(member);
}
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 2d3090825da..6d3f35d407d 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -501,11 +501,9 @@ void WorldSession::HandleSpellClick(WorldPacket& recvData)
unit->HandleSpellClick(_player);
}
-void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
+void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorImageData& packet)
{
- ObjectGuid guid;
- recvData >> guid;
- recvData.read_skip<uint32>(); // DisplayId ?
+ ObjectGuid guid = packet.UnitGUID;
// Get unit for which data is needed by client
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
@@ -520,27 +518,25 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
if (!creator)
return;
- WorldPacket data(SMSG_MIRROR_IMAGE_COMPONENTED_DATA, 68);
- data << guid;
- data << uint32(creator->GetDisplayId());
- data << uint8(creator->getRace());
- data << uint8(creator->getGender());
- data << uint8(creator->getClass());
-
- if (creator->GetTypeId() == TYPEID_PLAYER)
+ if (Player* player = creator->ToPlayer())
{
- Player* player = creator->ToPlayer();
- Guild* guild = NULL;
+ WorldPackets::Spells::MirrorImageComponentedData packet;
+ packet.UnitGUID = guid;
+ packet.DisplayID = creator->GetDisplayId();
+ packet.RaceID = creator->getRace();
+ packet.Gender = creator->getGender();
+ packet.ClassID = creator->getClass();
+
+ Guild* guild = player->GetGuild();
- if (ObjectGuid::LowType guildId = player->GetGuildId())
- guild = sGuildMgr->GetGuildById(guildId);
+ packet.SkinColor = player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
+ packet.FaceVariation = player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID);
+ packet.HairVariation = player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID);
+ packet.HairColor = player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
+ packet.BeardVariation = player->GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE);
+ packet.GuildGUID = (guild ? guild->GetGUID() : ObjectGuid::Empty);
- data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
- data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
- data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
- data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
- data << uint8(player->GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
- data << (guild ? guild->GetGUID() : ObjectGuid::Empty);
+ packet.ItemDisplayID.reserve(11);
static EquipmentSlots const itemSlots[] =
{
@@ -553,44 +549,34 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
EQUIPMENT_SLOT_FEET,
EQUIPMENT_SLOT_WRISTS,
EQUIPMENT_SLOT_HANDS,
- EQUIPMENT_SLOT_BACK,
EQUIPMENT_SLOT_TABARD,
+ EQUIPMENT_SLOT_BACK,
EQUIPMENT_SLOT_END
};
// Display items in visible slots
- for (EquipmentSlots const* itr = &itemSlots[0]; *itr != EQUIPMENT_SLOT_END; ++itr)
+ for (auto const& slot : itemSlots)
{
- if (*itr == EQUIPMENT_SLOT_HEAD && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
- data << uint32(0);
- else if (*itr == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
- data << uint32(0);
- else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr))
- data << uint32(item->GetDisplayId());
+ uint32 itemDisplayId;
+ if ((slot == EQUIPMENT_SLOT_HEAD && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM)) ||
+ (slot == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)))
+ itemDisplayId = 0;
+ else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
+ itemDisplayId = item->GetDisplayId();
else
- data << uint32(0);
+ itemDisplayId = 0;
+
+ packet.ItemDisplayID.push_back(itemDisplayId);
}
+ SendPacket(packet.Write());
}
else
{
- // Skip player data for creatures
- data << uint8(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
+ WorldPackets::Spells::MirrorImageCreatureData packet;
+ packet.UnitGUID = guid;
+ packet.DisplayID = creator->GetDisplayId();
+ SendPacket(packet.Write());
}
-
- SendPacket(&data);
}
void WorldSession::HandleUpdateProjectilePosition(WorldPacket& recvPacket)
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index c151183da8a..80024879a43 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -426,7 +426,26 @@ void InstanceScript::DoUseDoorOrButton(ObjectGuid guid, uint32 withRestoreTime /
TC_LOG_ERROR("scripts", "InstanceScript: DoUseDoorOrButton can't use gameobject entry %u, because type is %u.", go->GetEntry(), go->GetGoType());
}
else
- TC_LOG_DEBUG("scripts", "InstanceScript: HandleGameObject failed");
+ TC_LOG_DEBUG("scripts", "InstanceScript: DoUseDoorOrButton failed");
+}
+
+void InstanceScript::DoCloseDoorOrButton(ObjectGuid guid)
+{
+ if (!guid)
+ return;
+
+ if (GameObject* go = instance->GetGameObject(guid))
+ {
+ if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR || go->GetGoType() == GAMEOBJECT_TYPE_BUTTON)
+ {
+ if (go->getLootState() == GO_ACTIVATED)
+ go->ResetDoorOrButton();
+ }
+ else
+ TC_LOG_ERROR("scripts", "InstanceScript: DoCloseDoorOrButton can't use gameobject entry %u, because type is %u.", go->GetEntry(), go->GetGoType());
+ }
+ else
+ TC_LOG_DEBUG("scripts", "InstanceScript: DoCloseDoorOrButton failed");
}
void InstanceScript::DoRespawnGameObject(ObjectGuid guid, uint32 timeToDespawn /*= MINUTE*/)
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index be1956efc79..d932c24abf3 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -195,6 +195,7 @@ class InstanceScript : public ZoneScript
// Change active state of doors or buttons
void DoUseDoorOrButton(ObjectGuid guid, uint32 withRestoreTime = 0, bool useAlternativeState = false);
+ void DoCloseDoorOrButton(ObjectGuid guid);
// Respawns a GO having negative spawntimesecs in gameobject-table
void DoRespawnGameObject(ObjectGuid guid, uint32 timeToDespawn = MINUTE);
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 86e1c80bbd1..848ca1409a2 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -742,7 +742,7 @@ enum SpellAttr10
SPELL_ATTR10_UNK26 = 0x04000000, // 26
SPELL_ATTR10_UNK27 = 0x08000000, // 27
SPELL_ATTR10_UNK28 = 0x10000000, // 28
- SPELL_ATTR10_UNK29 = 0x20000000, // 29
+ SPELL_ATTR10_MOUNT_IS_NOT_ACCOUNT_WIDE = 0x20000000, // 29 This mount is stored per-character
SPELL_ATTR10_UNK30 = 0x40000000, // 30
SPELL_ATTR10_UNK31 = 0x80000000 // 31
};
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
index 0e436715622..66034e75fc3 100644
--- a/src/server/game/Movement/PathGenerator.cpp
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -117,7 +117,7 @@ dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32
}
if (distance)
- *distance = dtSqrt(minDist3d);
+ *distance = dtMathSqrtf(minDist3d);
return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF;
}
@@ -800,7 +800,7 @@ dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPo
// Find movement delta.
float delta[VERTEX_SIZE];
dtVsub(delta, steerPos, iterPos);
- float len = dtSqrt(dtVdot(delta, delta));
+ float len = dtMathSqrtf(dtVdot(delta, delta));
// If the steer target is end of path or off-mesh link, do not move past the location.
if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
len = 1.0f;
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
index 117891140da..d91dc2bc469 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
@@ -26,6 +26,7 @@
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
+#include "Packets/ChatPackets.h"
class DefenseMessageBuilder
{
@@ -33,14 +34,14 @@ class DefenseMessageBuilder
DefenseMessageBuilder(uint32 zoneId, uint32 id)
: _zoneId(zoneId), _id(id) { }
- void operator()(WorldPacket& data, LocaleConstant locale) const
+ WorldPackets::Chat::DefenseMessage* operator()(LocaleConstant locale) const
{
std::string text = sOutdoorPvPMgr->GetDefenseMessage(_zoneId, _id, locale);
- data.Initialize(SMSG_DEFENSE_MESSAGE, 4 + 4 + text.length());
- data.append<uint32>(_zoneId);
- data.append<uint32>(text.length());
- data << text;
+ WorldPackets::Chat::DefenseMessage* defenseMessage = new WorldPackets::Chat::DefenseMessage();
+ defenseMessage->ZoneID = _zoneId;
+ defenseMessage->MessageText = text;
+ return defenseMessage;
}
private:
diff --git a/src/server/game/Scripting/MapScripts.cpp b/src/server/game/Scripting/MapScripts.cpp
index ecfc069ca78..225ab7386c6 100644
--- a/src/server/game/Scripting/MapScripts.cpp
+++ b/src/server/game/Scripting/MapScripts.cpp
@@ -799,7 +799,6 @@ void Map::ScriptsProcess()
}
Creature* cTarget = NULL;
- WorldObject* wSource = dynamic_cast<WorldObject*>(source);
auto creatureBounds = _creatureBySpawnIdStore.equal_range(step.script->CallScript.CreatureEntry);
if (creatureBounds.first != creatureBounds.second)
{
diff --git a/src/server/game/Server/Packet.h b/src/server/game/Server/Packet.h
index 85d65e967be..89435db31e3 100644
--- a/src/server/game/Server/Packet.h
+++ b/src/server/game/Server/Packet.h
@@ -35,6 +35,7 @@ namespace WorldPackets
virtual WorldPacket const* Write() = 0;
virtual void Read() = 0;
+ WorldPacket const* GetRawPacket() const { return &_worldPacket; }
size_t GetSize() const { return _worldPacket.size(); }
ConnectionType GetConnection() const { return _worldPacket.GetConnection(); }
diff --git a/src/server/game/Server/Packets/ChatPackets.cpp b/src/server/game/Server/Packets/ChatPackets.cpp
index 4204278d514..726e1b87cc6 100644
--- a/src/server/game/Server/Packets/ChatPackets.cpp
+++ b/src/server/game/Server/Packets/ChatPackets.cpp
@@ -92,6 +92,15 @@ void WorldPackets::Chat::ChatMessageEmote::Read()
Text = _worldPacket.ReadString(len);
}
+WorldPackets::Chat::Chat::Chat(Chat const& chat) : ServerPacket(SMSG_CHAT, chat._worldPacket.size()),
+ SlashCmd(chat.SlashCmd), _Language(chat._Language), SenderGUID(chat.SenderGUID),
+ SenderGuildGUID(chat.SenderGuildGUID), SenderAccountGUID(chat.SenderAccountGUID), TargetGUID(chat.TargetGUID), PartyGUID(chat.PartyGUID),
+ SenderVirtualAddress(chat.SenderVirtualAddress), TargetVirtualAddress(chat.TargetVirtualAddress), SenderName(chat.SenderName), TargetName(chat.TargetName),
+ Prefix(chat.Prefix), _Channel(chat._Channel), ChatText(chat.ChatText), AchievementID(chat.AchievementID), _ChatFlags(chat._ChatFlags),
+ DisplayTime(chat.DisplayTime), HideChatLog(chat.HideChatLog), FakeSenderName(chat.FakeSenderName)
+{
+}
+
void WorldPackets::Chat::Chat::Initialize(ChatMsg chatType, Language language, WorldObject const* sender, WorldObject const* receiver, std::string message,
uint32 achievementId /*= 0*/, std::string channelName /*= ""*/, LocaleConstant locale /*= DEFAULT_LOCALE*/, std::string addonPrefix /*= ""*/)
{
@@ -111,30 +120,10 @@ void WorldPackets::Chat::Chat::Initialize(ChatMsg chatType, Language language, W
_Language = language;
if (sender)
- {
- SenderGUID = sender->GetGUID();
-
- if (Creature const* creatureSender = sender->ToCreature())
- SenderName = creatureSender->GetNameForLocaleIdx(locale);
-
- if (Player const* playerSender = sender->ToPlayer())
- {
- SenderAccountGUID = playerSender->GetSession()->GetAccountGUID();
- _ChatFlags = playerSender->GetChatFlags();
-
- SenderGuildGUID = ObjectGuid::Create<HighGuid::Guild>(playerSender->GetGuildId());
-
- if (Group const* group = playerSender->GetGroup())
- PartyGUID = group->GetGUID();
- }
- }
+ SetSender(sender, locale);
if (receiver)
- {
- TargetGUID = receiver->GetGUID();
- if (Creature const* creatureReceiver = receiver->ToCreature())
- TargetName = creatureReceiver->GetNameForLocaleIdx(locale);
- }
+ SetReceiver(receiver, locale);
SenderVirtualAddress = GetVirtualRealmAddress();
TargetVirtualAddress = GetVirtualRealmAddress();
@@ -144,6 +133,32 @@ void WorldPackets::Chat::Chat::Initialize(ChatMsg chatType, Language language, W
ChatText = std::move(message);
}
+void WorldPackets::Chat::Chat::SetSender(WorldObject const* sender, LocaleConstant locale)
+{
+ SenderGUID = sender->GetGUID();
+
+ if (Creature const* creatureSender = sender->ToCreature())
+ SenderName = creatureSender->GetNameForLocaleIdx(locale);
+
+ if (Player const* playerSender = sender->ToPlayer())
+ {
+ SenderAccountGUID = playerSender->GetSession()->GetAccountGUID();
+ _ChatFlags = playerSender->GetChatFlags();
+
+ SenderGuildGUID = ObjectGuid::Create<HighGuid::Guild>(playerSender->GetGuildId());
+
+ if (Group const* group = playerSender->GetGroup())
+ PartyGUID = group->GetGUID();
+ }
+}
+
+void WorldPackets::Chat::Chat::SetReceiver(WorldObject const* receiver, LocaleConstant locale)
+{
+ TargetGUID = receiver->GetGUID();
+ if (Creature const* creatureReceiver = receiver->ToCreature())
+ TargetName = creatureReceiver->GetNameForLocaleIdx(locale);
+}
+
WorldPacket const* WorldPackets::Chat::Chat::Write()
{
_worldPacket << SlashCmd;
@@ -245,3 +260,13 @@ void WorldPackets::Chat::ChatRegisterAddonPrefixes::Read()
Prefixes.push_back(_worldPacket.ReadString(lenghts));
}
}
+
+WorldPacket const* WorldPackets::Chat::DefenseMessage::Write()
+{
+ _worldPacket << int32(ZoneID);
+ _worldPacket.WriteBits(MessageText.length(), 12);
+ _worldPacket.FlushBits();
+ _worldPacket.WriteString(MessageText);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/ChatPackets.h b/src/server/game/Server/Packets/ChatPackets.h
index c8bb038ce19..0686fe63c00 100644
--- a/src/server/game/Server/Packets/ChatPackets.h
+++ b/src/server/game/Server/Packets/ChatPackets.h
@@ -150,8 +150,12 @@ namespace WorldPackets
{
public:
Chat() : ServerPacket(SMSG_CHAT, 100) { }
+ Chat(Chat const& chat);
void Initialize(ChatMsg chatType, Language language, WorldObject const* sender, WorldObject const* receiver, std::string message, uint32 achievementId = 0, std::string channelName = "", LocaleConstant locale = DEFAULT_LOCALE, std::string addonPrefix = "");
+ void SetSender(WorldObject const* sender, LocaleConstant locale);
+ void SetReceiver(WorldObject const* receiver, LocaleConstant locale);
+
WorldPacket const* Write() override;
uint8 SlashCmd = 0; ///< @see enum ChatMsg
@@ -268,6 +272,17 @@ namespace WorldPackets
void Read() override { }
};
+
+ class DefenseMessage final : public ServerPacket
+ {
+ public:
+ DefenseMessage() : ServerPacket(SMSG_DEFENSE_MESSAGE) { }
+
+ WorldPacket const* Write() override;
+
+ int32 ZoneID = 0;
+ std::string MessageText;
+ };
}
}
diff --git a/src/server/game/Server/Packets/GarrisonPackets.cpp b/src/server/game/Server/Packets/GarrisonPackets.cpp
index 88374e7ce62..041655aa4c6 100644
--- a/src/server/game/Server/Packets/GarrisonPackets.cpp
+++ b/src/server/game/Server/Packets/GarrisonPackets.cpp
@@ -25,6 +25,14 @@ WorldPacket const* WorldPackets::Garrison::GarrisonCreateResult::Write()
return &_worldPacket;
}
+WorldPacket const* WorldPackets::Garrison::GarrisonDeleteResult::Write()
+{
+ _worldPacket << uint32(Result);
+ _worldPacket << uint32(GarrSiteID);
+
+ return &_worldPacket;
+}
+
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Garrison::GarrisonPlotInfo& plotInfo)
{
data << uint32(plotInfo.GarrPlotInstanceID);
@@ -245,3 +253,10 @@ WorldPacket const* WorldPackets::Garrison::GarrisonAddFollowerResult::Write()
return &_worldPacket;
}
+
+WorldPacket const* WorldPackets::Garrison::GarrisonBuildingActivated::Write()
+{
+ _worldPacket << uint32(GarrPlotInstanceID);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/GarrisonPackets.h b/src/server/game/Server/Packets/GarrisonPackets.h
index 1bb04c7ba95..a783b1bc1af 100644
--- a/src/server/game/Server/Packets/GarrisonPackets.h
+++ b/src/server/game/Server/Packets/GarrisonPackets.h
@@ -39,6 +39,17 @@ namespace WorldPackets
uint32 Result = 0;
};
+ class GarrisonDeleteResult final : public ServerPacket
+ {
+ public:
+ GarrisonDeleteResult() : ServerPacket(SMSG_GARRISON_DELETE_RESULT, 4 + 4) { }
+
+ WorldPacket const* Write() override;
+
+ uint32 Result = 0;
+ uint32 GarrSiteID = 0;
+ };
+
class GetGarrisonInfo final : public ClientPacket
{
public:
@@ -279,6 +290,16 @@ namespace WorldPackets
GarrisonFollower Follower;
uint32 Result = 0;
};
+
+ class GarrisonBuildingActivated final : public ServerPacket
+ {
+ public:
+ GarrisonBuildingActivated() : ServerPacket(SMSG_GARRISON_BUILDING_ACTIVATED, 4) { }
+
+ WorldPacket const* Write() override;
+
+ uint32 GarrPlotInstanceID = 0;
+ };
}
}
diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp
index bbd3fcb2ba7..346f7e0e568 100644
--- a/src/server/game/Server/Packets/MiscPackets.cpp
+++ b/src/server/game/Server/Packets/MiscPackets.cpp
@@ -520,3 +520,20 @@ WorldPacket const* WorldPackets::Misc::LoadCUFProfiles::Write()
return &_worldPacket;
}
+
+WorldPacket const* WorldPackets::Misc::SetAIAnimKit::Write()
+{
+ _worldPacket << Unit;
+ _worldPacket << uint16(AnimKitID);
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::Misc::SetPlayHoverAnim::Write()
+{
+ _worldPacket << UnitGUID;
+ _worldPacket.WriteBit(PlayHoverAnim);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h
index 93bac0d208b..858b2962ea9 100644
--- a/src/server/game/Server/Packets/MiscPackets.h
+++ b/src/server/game/Server/Packets/MiscPackets.h
@@ -663,6 +663,28 @@ namespace WorldPackets
std::vector<CUFProfile const*> CUFProfiles;
};
+
+ class SetAIAnimKit final : public ServerPacket
+ {
+ public:
+ SetAIAnimKit() : ServerPacket(SMSG_SET_AI_ANIM_KIT, 16 + 2) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid Unit;
+ uint16 AnimKitID = 0;
+ };
+
+ class SetPlayHoverAnim final : public ServerPacket
+ {
+ public:
+ SetPlayHoverAnim() : ServerPacket(SMSG_SET_PLAY_HOVER_ANIM, 16 + 1) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid UnitGUID;
+ bool PlayHoverAnim = false;
+ };
}
}
diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp
index 421a2e9a46c..832dfc82b06 100644
--- a/src/server/game/Server/Packets/SpellPackets.cpp
+++ b/src/server/game/Server/Packets/SpellPackets.cpp
@@ -706,3 +706,39 @@ void WorldPackets::Spells::UnlearnSkill::Read()
{
_worldPacket >> SkillLine;
}
+
+void WorldPackets::Spells::GetMirrorImageData::Read()
+{
+ _worldPacket >> UnitGUID;
+ _worldPacket >> DisplayID;
+}
+
+WorldPacket const* WorldPackets::Spells::MirrorImageComponentedData::Write()
+{
+ _worldPacket << UnitGUID;
+ _worldPacket << DisplayID;
+ _worldPacket << RaceID;
+ _worldPacket << Gender;
+ _worldPacket << ClassID;
+ _worldPacket << SkinColor;
+ _worldPacket << FaceVariation;
+ _worldPacket << HairVariation;
+ _worldPacket << HairColor;
+ _worldPacket << BeardVariation;
+ _worldPacket << GuildGUID;
+
+ _worldPacket << uint32(ItemDisplayID.size());
+
+ for (auto const& itemDisplayId : ItemDisplayID)
+ _worldPacket << itemDisplayId;
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::Spells::MirrorImageCreatureData::Write()
+{
+ _worldPacket << UnitGUID;
+ _worldPacket << DisplayID;
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h
index 9408ac13398..a3fa6c10925 100644
--- a/src/server/game/Server/Packets/SpellPackets.h
+++ b/src/server/game/Server/Packets/SpellPackets.h
@@ -694,6 +694,50 @@ namespace WorldPackets
void Read() override { }
};
+
+ class GetMirrorImageData final : public ClientPacket
+ {
+ public:
+ GetMirrorImageData(WorldPacket&& packet) : ClientPacket(CMSG_GET_MIRROR_IMAGE_DATA, std::move(packet)) {}
+
+ void Read() override;
+
+ ObjectGuid UnitGUID;
+ uint32 DisplayID = 0;
+ };
+
+ class MirrorImageComponentedData final : public ServerPacket
+ {
+ public:
+ MirrorImageComponentedData() : ServerPacket(SMSG_MIRROR_IMAGE_COMPONENTED_DATA, 8 + 4 + 8 * 1 + 8 + 11 * 4) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid UnitGUID;
+ uint32 DisplayID = 0;
+ uint8 RaceID = 0;
+ uint8 Gender = 0;
+ uint8 ClassID = 0;
+ uint8 SkinColor = 0;
+ uint8 FaceVariation = 0;
+ uint8 HairVariation = 0;
+ uint8 HairColor = 0;
+ uint8 BeardVariation = 0;
+ ObjectGuid GuildGUID;
+
+ std::vector<uint32> ItemDisplayID;
+ };
+
+ class MirrorImageCreatureData final : public ServerPacket
+ {
+ public:
+ MirrorImageCreatureData() : ServerPacket(SMSG_MIRROR_IMAGE_CREATURE_DATA, 8 + 4) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid UnitGUID;
+ uint32 DisplayID = 0;
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 45071d14cd4..c9b9bf3a284 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -394,7 +394,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_GET_CHALLENGE_MODE_REWARDS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GET_GARRISON_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Garrison::GetGarrisonInfo, &WorldSession::HandleGetGarrisonInfo);
DEFINE_HANDLER(CMSG_GET_ITEM_PURCHASE_DATA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::GetItemPurchaseData, &WorldSession::HandleGetItemPurchaseData);
- DEFINE_OPCODE_HANDLER_OLD(CMSG_GET_MIRROR_IMAGE_DATA, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleMirrorImageDataRequest );
+ DEFINE_HANDLER(CMSG_GET_MIRROR_IMAGE_DATA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::GetMirrorImageData, &WorldSession::HandleMirrorImageDataRequest);
DEFINE_HANDLER(CMSG_GET_PVP_OPTIONS_ENABLED, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Battleground::GetPVPOptionsEnabled, &WorldSession::HandleGetPVPOptionsEnabled);
DEFINE_HANDLER(CMSG_GET_REMAINING_GAME_TIME, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GET_TROPHY_LIST, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
@@ -1055,7 +1055,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_DANCE_STUDIO_CREATE_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_DB_REPLY, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_DEATH_RELEASE_LOC, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_DEFENSE_MESSAGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_DEFENSE_MESSAGE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_DELETE_CHAR, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_DESTROY_ARENA_UNIT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_DESTRUCTIBLE_BUILDING_DAMAGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
@@ -1113,18 +1113,20 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_ADD_FOLLOWER_RESULT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_ADD_MISSION_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_ASSIGN_FOLLOWER_TO_BUILDING_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_BUILDING_ACTIVATED, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_BUILDING_ACTIVATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_BUILDING_LANDMARKS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_BUILDING_REMOVED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_BUILDING_SET_ACTIVE_SPECIALIZATION_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_CLEAR_ALL_FOLLOWERS_EXHAUSTION, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_COMPLETE_MISSION_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_CREATE_RESULT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_DELETE_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_DELETE_RESULT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_FOLLOWER_CHANGED_ABILITIES, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_FOLLOWER_CHANGED_ITEM_LEVEL, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_FOLLOWER_CHANGED_ITEM_LEVEL2, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_FOLLOWER_CHANGED_STATUS, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_FOLLOWER_CHANGED_XP, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_IS_UPGRADEABLE_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_LANDINGPAGE_SHIPMENTS, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_LANDING_PAGE_SHIPMENT_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_LEARN_BLUEPRINT_RESULT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_LEARN_SPECIALIZATION_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_LIST_FOLLOWERS_CHEAT_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
@@ -1133,6 +1135,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_NUM_FOLLOWER_ACTIVATIONS_REMAINING, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_OPEN_ARCHITECT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_OPEN_MISSION_NPC, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_OPEN_RECRUITMENT_NPC, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_OPEN_TRADESKILL_NPC, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_PLACE_BUILDING_RESULT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_PLOT_PLACED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
@@ -1329,8 +1332,8 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MASTER_LOOT_CANDIDATE_LIST, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MESSAGE_BOX, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MINIMAP_PING, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_MIRROR_IMAGE_COMPONENTED_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_MIRROR_IMAGE_CREATURE_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MIRROR_IMAGE_COMPONENTED_DATA, STATUS_NEVER, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MIRROR_IMAGE_CREATURE_DATA, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MISSILE_CANCEL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MODIFY_COOLDOWN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOTD, STATUS_NEVER, CONNECTION_TYPE_REALM);
@@ -1605,7 +1608,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SERVER_TIME, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SETUP_CURRENCY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SETUP_RESEARCH_HISTORY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_AI_ANIM_KIT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_AI_ANIM_KIT, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_ALL_TASK_PROGRESS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_ANIM_TIER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_CURRENCY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
@@ -1626,7 +1629,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PCT_SPELL_MODIFIER, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PET_SPECIALIZATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAY_HOVER_ANIM, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAY_HOVER_ANIM, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PROFICIENCY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_SPELL_CHARGES, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_TASK_COMPLETE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
@@ -1640,6 +1643,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SOCKET_GEMS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SOR_START_EXPERIENCE_INCOMPLETE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPECIAL_MOUNT_ANIM, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_ABSORB_LOG, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_CHANNEL_START, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_CHANNEL_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_COOLDOWN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h
index a17fe288ce6..39befe6fd9a 100644
--- a/src/server/game/Server/Protocol/Opcodes.h
+++ b/src/server/game/Server/Protocol/Opcodes.h
@@ -1007,14 +1007,16 @@ enum OpcodeServer : uint32
SMSG_GARRISON_BUILDING_LANDMARKS = 0x0987,
SMSG_GARRISON_BUILDING_REMOVED = 0x08F7,
SMSG_GARRISON_BUILDING_SET_ACTIVE_SPECIALIZATION_RESULT = 0x00F8,
+ SMSG_GARRISON_CLEAR_ALL_FOLLOWERS_EXHAUSTION = 0x0883,
SMSG_GARRISON_COMPLETE_MISSION_RESULT = 0x00F7,
SMSG_GARRISON_CREATE_RESULT = 0x01BB,
SMSG_GARRISON_DELETE_RESULT = 0x01FC,
+ SMSG_GARRISON_FOLLOWER_CHANGED_ABILITIES = 0x0093,
SMSG_GARRISON_FOLLOWER_CHANGED_ITEM_LEVEL = 0x01B4,
- SMSG_GARRISON_FOLLOWER_CHANGED_ITEM_LEVEL2 = 0x0093,
+ SMSG_GARRISON_FOLLOWER_CHANGED_STATUS = 0x01E8,
SMSG_GARRISON_FOLLOWER_CHANGED_XP = 0x00AC,
SMSG_GARRISON_IS_UPGRADEABLE_RESULT = 0x01A8,
- SMSG_GARRISON_LANDINGPAGE_SHIPMENTS = 0x1CA5,
+ SMSG_GARRISON_LANDING_PAGE_SHIPMENT_INFO = 0x1CA5,
SMSG_GARRISON_LEARN_BLUEPRINT_RESULT = 0x08D8,
SMSG_GARRISON_LEARN_SPECIALIZATION_RESULT = 0x08AB,
SMSG_GARRISON_LIST_FOLLOWERS_CHEAT_RESULT = 0x01FB,
@@ -1023,6 +1025,7 @@ enum OpcodeServer : uint32
SMSG_GARRISON_NUM_FOLLOWER_ACTIVATIONS_REMAINING = 0x088F,
SMSG_GARRISON_OPEN_ARCHITECT = 0x08FB,
SMSG_GARRISON_OPEN_MISSION_NPC = 0x08C0,
+ SMSG_GARRISON_OPEN_RECRUITMENT_NPC = 0x01D7,
SMSG_GARRISON_OPEN_TRADESKILL_NPC = 0x018F,
SMSG_GARRISON_PLACE_BUILDING_RESULT = 0x08A4,
SMSG_GARRISON_PLOT_PLACED = 0x00E7,
@@ -1530,6 +1533,7 @@ enum OpcodeServer : uint32
SMSG_SOCKET_GEMS = 0x1DF6,
SMSG_SOR_START_EXPERIENCE_INCOMPLETE = 0x1640,
SMSG_SPECIAL_MOUNT_ANIM = 0x1319,
+ SMSG_SPELL_ABSORB_LOG = 0x1C8D,
SMSG_SPELL_CHANNEL_START = 0x103E,
SMSG_SPELL_CHANNEL_UPDATE = 0x10D9,
SMSG_SPELL_COOLDOWN = 0x1D2A,
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 650e100f514..f551d111744 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -104,7 +104,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
}
/// WorldSession constructor
-WorldSession::WorldSession(uint32 id, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter):
+WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter):
m_muteTime(mute_time),
m_timeOutTime(0),
AntiDOS(this),
@@ -112,6 +112,7 @@ WorldSession::WorldSession(uint32 id, uint32 battlenetAccountId, std::shared_ptr
_player(NULL),
_security(sec),
_accountId(id),
+ _accountName(std::move(name)),
_battlenetAccountId(battlenetAccountId),
m_expansion(expansion),
_warden(NULL),
@@ -184,13 +185,13 @@ std::string WorldSession::GetPlayerInfo() const
{
std::ostringstream ss;
- ss << "[Player: " << GetPlayerName() << " (";
- if (_player)
- ss << _player->GetGUID().ToString() << ", ";
- else if (!m_playerLoading.IsEmpty())
+ ss << "[Player: ";
+ if (!m_playerLoading.IsEmpty())
ss << "Logging in: " << m_playerLoading.ToString() << ", ";
+ else if (_player)
+ ss << _player->GetName() << ' ' << _player->GetGUID().ToString() << ", ";
- ss << "Account: " << GetAccountId() << ")]";
+ ss << "Account: " << GetAccountId() << "]";
return ss.str();
}
@@ -711,13 +712,6 @@ void WorldSession::SendConnectToInstance(WorldPackets::Auth::ConnectToSerial ser
SendPacket(connectTo.Write());
}
-void WorldSession::LoadGlobalAccountData()
-{
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_DATA);
- stmt->setUInt32(0, GetAccountId());
- LoadAccountData(CharacterDatabase.Query(stmt), GLOBAL_CACHE_MASK);
-}
-
void WorldSession::LoadAccountData(PreparedQueryResult result, uint32 mask)
{
for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
@@ -780,13 +774,11 @@ void WorldSession::SetAccountData(AccountDataType type, uint32 time, std::string
_accountData[type].Data = data;
}
-void WorldSession::LoadTutorialsData()
+void WorldSession::LoadTutorialsData(PreparedQueryResult result)
{
memset(_tutorials, 0, sizeof(uint32) * MAX_ACCOUNT_TUTORIAL_VALUES);
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_TUTORIALS);
- stmt->setUInt32(0, GetAccountId());
- if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
+ if (result)
for (uint8 i = 0; i < MAX_ACCOUNT_TUTORIAL_VALUES; ++i)
_tutorials[i] = (*result)[i].GetUInt32();
@@ -959,6 +951,10 @@ void WorldSession::ProcessQueryCallbacks()
{
PreparedQueryResult result;
+ if (_realmAccountLoginCallback.valid() && _realmAccountLoginCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready &&
+ _accountLoginCallback.valid() && _accountLoginCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
+ InitializeSessionCallback(_realmAccountLoginCallback.get(), _accountLoginCallback.get());
+
//! HandleCharEnumOpcode and HandleCharUndeleteEnumOpcode
if (_charEnumCallback.IsReady())
{
@@ -1083,7 +1079,15 @@ void WorldSession::InitWarden(BigNumber* k, std::string const& os)
_warden = new WardenWin();
_warden->Init(this, k);
}
- else if (os == "OSX")
+ else if (os == "Wn64")
+ {
+ // Not implemented
+ }
+ else if (os == "Mc64")
+ {
+ // Not implemented
+ }
+ else if (os == "Mac")
{
// Disabled as it is causing the client to crash
// _warden = new WardenMac();
@@ -1094,15 +1098,118 @@ void WorldSession::InitWarden(BigNumber* k, std::string const& os)
void WorldSession::LoadPermissions()
{
uint32 id = GetAccountId();
- std::string name;
- AccountMgr::GetName(id, name);
uint8 secLevel = GetSecurity();
- _RBACData = new rbac::RBACData(id, name, realmHandle.Index, secLevel);
+ TC_LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: %u, Name: %s, realmId: %d, secLevel: %u]",
+ id, _accountName.c_str(), realmHandle.Index, secLevel);
+
+ _RBACData = new rbac::RBACData(id, _accountName, realmHandle.Index, secLevel);
_RBACData->LoadFromDB();
+}
+
+PreparedQueryResultFuture WorldSession::LoadPermissionsAsync()
+{
+ uint32 id = GetAccountId();
+ uint8 secLevel = GetSecurity();
TC_LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: %u, Name: %s, realmId: %d, secLevel: %u]",
- id, name.c_str(), realmHandle.Index, secLevel);
+ id, _accountName.c_str(), realmHandle.Index, secLevel);
+
+ _RBACData = new rbac::RBACData(id, _accountName, realmHandle.Index, secLevel);
+ return _RBACData->LoadFromDBAsync();
+}
+
+class AccountInfoQueryHolderPerRealm : public SQLQueryHolder
+{
+public:
+ enum
+ {
+ GLOBAL_ACCOUNT_DATA = 0,
+ TUTORIALS,
+
+ MAX_QUERIES
+ };
+
+ AccountInfoQueryHolderPerRealm() { SetSize(MAX_QUERIES); }
+
+ bool Initialize(uint32 accountId, uint32 /*battlenetAccountId*/)
+ {
+ bool ok = true;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_DATA);
+ stmt->setUInt32(0, accountId);
+ ok = SetPreparedQuery(GLOBAL_ACCOUNT_DATA, stmt) && ok;
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_TUTORIALS);
+ stmt->setUInt32(0, accountId);
+ ok = SetPreparedQuery(TUTORIALS, stmt) && ok;
+
+ return ok;
+ }
+};
+
+class AccountInfoQueryHolder : public SQLQueryHolder
+{
+public:
+ enum
+ {
+ MAX_QUERIES
+ };
+
+ AccountInfoQueryHolder() { SetSize(MAX_QUERIES); }
+
+ bool Initialize(uint32 /*accountId*/, uint32 /*battlenetAccountId*/)
+ {
+ bool ok = true;
+
+ return ok;
+ }
+};
+
+void WorldSession::InitializeSession()
+{
+ AccountInfoQueryHolderPerRealm* realmHolder = new AccountInfoQueryHolderPerRealm();
+ if (!realmHolder->Initialize(GetAccountId(), GetBattlenetAccountId()))
+ {
+ delete realmHolder;
+ SendAuthResponse(AUTH_SYSTEM_ERROR, false);
+ return;
+ }
+
+ AccountInfoQueryHolder* holder = new AccountInfoQueryHolder();
+ if (!holder->Initialize(GetAccountId(), GetBattlenetAccountId()))
+ {
+ delete realmHolder;
+ delete holder;
+ SendAuthResponse(AUTH_SYSTEM_ERROR, false);
+ return;
+ }
+
+ _realmAccountLoginCallback = CharacterDatabase.DelayQueryHolder(realmHolder);
+ _accountLoginCallback = LoginDatabase.DelayQueryHolder(holder);
+}
+
+void WorldSession::InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQueryHolder* holder)
+{
+ LoadAccountData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::GLOBAL_ACCOUNT_DATA), GLOBAL_CACHE_MASK);
+ LoadTutorialsData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::TUTORIALS));
+
+ if (!m_inQueue)
+ SendAuthResponse(AUTH_OK, false);
+ else
+ SendAuthWaitQue(0);
+
+ SetInQueue(false);
+ ResetTimeOutTime();
+
+ SendSetTimeZoneInformation();
+ SendFeatureSystemStatusGlueScreen();
+ SendAddonsInfo();
+ SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION));
+ SendTutorialsData();
+
+ delete realmHolder;
+ delete holder;
}
rbac::RBACData* WorldSession::GetRBACData()
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index d0242b86440..4ea97367d4b 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -498,6 +498,7 @@ namespace WorldPackets
class SetActionButton;
class UnlearnSkill;
class SelfRes;
+ class GetMirrorImageData;
}
namespace Talent
@@ -742,7 +743,7 @@ struct PacketCounter
class WorldSession
{
public:
- WorldSession(uint32 id, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter);
+ WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter);
~WorldSession();
bool PlayerLoading() const { return !m_playerLoading.IsEmpty(); }
@@ -767,9 +768,13 @@ class WorldSession
void SendAuthResponse(uint8 code, bool queued, uint32 queuePos = 0);
void SendClientCacheVersion(uint32 version);
+ void InitializeSession();
+ void InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQueryHolder* holder);
+
rbac::RBACData* GetRBACData();
bool HasPermission(uint32 permissionId);
void LoadPermissions();
+ PreparedQueryResultFuture LoadPermissionsAsync();
void InvalidateRBACData(); // Used to force LoadPermissions at next HasPermission check
AccountTypes GetSecurity() const { return _security; }
@@ -852,10 +857,9 @@ class WorldSession
// Account Data
AccountData const* GetAccountData(AccountDataType type) const { return &_accountData[type]; }
void SetAccountData(AccountDataType type, uint32 time, std::string const& data);
- void LoadGlobalAccountData();
void LoadAccountData(PreparedQueryResult result, uint32 mask);
- void LoadTutorialsData();
+ void LoadTutorialsData(PreparedQueryResult result);
void SendTutorialsData();
void SaveTutorialsData(SQLTransaction& trans);
uint32 GetTutorialInt(uint8 index) const { return _tutorials[index]; }
@@ -1496,7 +1500,7 @@ class WorldSession
// Miscellaneous
void HandleSpellClick(WorldPacket& recvData);
- void HandleMirrorImageDataRequest(WorldPacket& recvData);
+ void HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorImageData& packet);
void HandleRemoveGlyph(WorldPacket& recvData);
void HandleGuildSetFocusedAchievement(WorldPackets::Achievement::GuildSetFocusedAchievement& setFocusedAchievement);
void HandleEquipmentSetSave(WorldPackets::EquipmentSet::SaveEquipmentSet& packet);
@@ -1536,6 +1540,8 @@ class WorldSession
void InitializeQueryCallbackParameters();
void ProcessQueryCallbacks();
+ QueryResultHolderFuture _realmAccountLoginCallback;
+ QueryResultHolderFuture _accountLoginCallback;
PreparedQueryResultFuture _addIgnoreCallback;
PreparedQueryResultFuture _stablePetCallback;
QueryCallback<PreparedQueryResult, bool> _charEnumCallback;
@@ -1609,6 +1615,7 @@ class WorldSession
AccountTypes _security;
uint32 _accountId;
+ std::string _accountName;
uint32 _battlenetAccountId;
uint8 m_expansion;
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index bc1306d73e1..027c35dfd7f 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -72,6 +72,43 @@ WorldSocket::~WorldSocket()
void WorldSocket::Start()
{
+ std::string ip_address = GetRemoteIpAddress().to_string();
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);
+ stmt->setString(0, ip_address);
+ stmt->setUInt32(1, inet_addr(ip_address.c_str()));
+
+ {
+ std::lock_guard<std::mutex> guard(_queryLock);
+ _queryCallback = io_service().wrap(std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1));
+ _queryFuture = LoginDatabase.AsyncQuery(stmt);
+ }
+}
+
+void WorldSocket::CheckIpCallback(PreparedQueryResult result)
+{
+ if (result)
+ {
+ bool banned = false;
+ do
+ {
+ Field* fields = result->Fetch();
+ if (fields[0].GetUInt64() != 0)
+ banned = true;
+
+ if (!fields[1].GetString().empty())
+ _ipCountry = fields[1].GetString();
+
+ } while (result->NextRow());
+
+ if (banned)
+ {
+ SendAuthResponseError(AUTH_REJECT);
+ TC_LOG_ERROR("network", "WorldSocket::CheckIpCallback: Sent Auth Response (IP %s banned).", GetRemoteIpAddress().to_string().c_str());
+ DelayedCloseSocket();
+ return;
+ }
+ }
+
AsyncRead();
MessageBuffer initializer;
@@ -84,6 +121,23 @@ void WorldSocket::Start()
QueuePacket(std::move(initializer), guard);
}
+bool WorldSocket::Update()
+{
+ if (!BaseSocket::Update())
+ return false;
+
+ {
+ std::lock_guard<std::mutex> guard(_queryLock);
+ if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
+ {
+ auto callback = std::move(_queryCallback);
+ callback(_queryFuture.get());
+ }
+ }
+
+ return true;
+}
+
void WorldSocket::HandleSendAuthSession()
{
_encryptSeed.SetRand(16 * 8);
@@ -246,8 +300,8 @@ bool WorldSocket::ReadDataHandler()
return false;
}
- WorldPackets::Auth::AuthSession authSession(std::move(packet));
- authSession.Read();
+ std::shared_ptr<WorldPackets::Auth::AuthSession> authSession = std::make_shared<WorldPackets::Auth::AuthSession>(std::move(packet));
+ authSession->Read();
HandleAuthSession(authSession);
break;
}
@@ -262,8 +316,8 @@ bool WorldSocket::ReadDataHandler()
return false;
}
- WorldPackets::Auth::AuthContinuedSession authSession(std::move(packet));
- authSession.Read();
+ std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession = std::make_shared<WorldPackets::Auth::AuthContinuedSession>(std::move(packet));
+ authSession->Read();
HandleAuthContinuedSession(authSession);
break;
}
@@ -470,23 +524,86 @@ uint32 WorldSocket::CompressPacket(uint8* buffer, WorldPacket const& packet)
return bufferSize - _compressionStream->avail_out;
}
-void WorldSocket::HandleAuthSession(WorldPackets::Auth::AuthSession& authSession)
+struct AccountInfo
{
- uint8 security;
- uint32 id;
- LocaleConstant locale;
- SHA1Hash sha;
- BigNumber k;
- bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED);
+ struct
+ {
+ uint32 Id;
+ bool IsLockedToIP;
+ std::string LastIP;
+ LocaleConstant Locale;
+ std::string OS;
+ bool IsBanned;
+
+ std::string LockCountry;
+ } BattleNet;
+
+ struct
+ {
+ uint32 Id;
+ BigNumber SessionKey;
+ uint8 Expansion;
+ int64 MuteTime;
+ uint32 Recruiter;
+ bool IsRectuiter;
+ AccountTypes Security;
+ bool IsBanned;
+ } Game;
+
+ bool IsBanned() const { return BattleNet.IsBanned || Game.IsBanned; }
+
+ explicit AccountInfo(Field* fields)
+ {
+ // 0 1 2 3 4 5 6 7 8 9 10
+ // SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel,
+ // 11 12 13
+ // bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id
+ // FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?)
+ // LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account r ON a.id = r.recruiter
+ // WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1
+ Game.Id = fields[0].GetUInt32();
+ Game.SessionKey.SetHexStr(fields[1].GetCString());
+ BattleNet.LastIP = fields[2].GetString();
+ BattleNet.IsLockedToIP = fields[3].GetBool();
+ Game.Expansion = fields[4].GetUInt8();
+ Game.MuteTime = fields[5].GetInt64();
+ BattleNet.Locale = LocaleConstant(fields[6].GetUInt8());
+ Game.Recruiter = fields[7].GetUInt32();
+ BattleNet.OS = fields[8].GetString();
+ BattleNet.Id = fields[9].GetUInt32();
+ Game.Security = AccountTypes(fields[10].GetUInt8());
+ BattleNet.IsBanned = fields[11].GetUInt64() != 0;
+ Game.IsBanned = fields[12].GetUInt64() != 0;
+ Game.IsRectuiter = fields[13].GetUInt32() != 0;
+
+ uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION);
+ if (Game.Expansion > world_expansion)
+ Game.Expansion = world_expansion;
+
+ if (BattleNet.Locale >= TOTAL_LOCALES)
+ BattleNet.Locale = LOCALE_enUS;
+ }
+};
+
+void WorldSocket::HandleAuthSession(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession)
+{
+ // Client switches packet headers after sending CMSG_AUTH_SESSION
+ _headerBuffer.Resize(SizeOfClientHeader[1][1]);
// Get the account information from the auth database
- // 0 1 2 3 4 5 6 7 8
- // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME);
- stmt->setString(0, authSession.Account);
+ stmt->setInt32(0, int32(realmHandle.Index));
+ stmt->setString(1, authSession->Account);
- PreparedQueryResult result = LoginDatabase.Query(stmt);
+ {
+ std::lock_guard<std::mutex> guard(_queryLock);
+ _queryCallback = io_service().wrap(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1));
+ _queryFuture = LoginDatabase.AsyncQuery(stmt);
+ }
+}
+void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession, PreparedQueryResult result)
+{
// Stop if the account is not found
if (!result)
{
@@ -497,33 +614,20 @@ void WorldSocket::HandleAuthSession(WorldPackets::Auth::AuthSession& authSession
return;
}
- Field* fields = result->Fetch();
-
- uint8 expansion = fields[4].GetUInt8();
- uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION);
- if (expansion > world_expansion)
- expansion = world_expansion;
+ AccountInfo account(result->Fetch());
// For hook purposes, we get Remoteaddress at this point.
std::string address = GetRemoteIpAddress().to_string();
// As we don't know if attempted login process by ip works, we update last_attempt_ip right away
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP);
-
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP);
stmt->setString(0, address);
- stmt->setString(1, authSession.Account);
-
+ stmt->setString(1, authSession->Account);
LoginDatabase.Execute(stmt);
// This also allows to check for possible "hack" attempts on account
- // id has to be fetched at this point, so that first actual account response that fails can be logged
- id = fields[0].GetUInt32();
-
- k.SetHexStr(fields[1].GetCString());
-
// even if auth credentials are bad, try using the session key we have - client cannot read auth response error without it
- _authCrypt.Init(&k);
- _headerBuffer.Resize(SizeOfClientHeader[1][1]);
+ _authCrypt.Init(&account.Game.SessionKey);
// First reject the connection if packet contains invalid data or realm state doesn't allow logging in
if (sWorld->IsClosed())
@@ -534,7 +638,7 @@ void WorldSocket::HandleAuthSession(WorldPackets::Auth::AuthSession& authSession
return;
}
- if (authSession.RealmID != realmHandle.Index)
+ if (authSession->RealmID != realmHandle.Index)
{
SendAuthResponseError(REALM_LIST_REALM_NOT_FOUND);
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (bad realm).");
@@ -542,13 +646,20 @@ void WorldSocket::HandleAuthSession(WorldPackets::Auth::AuthSession& authSession
return;
}
- std::string os = fields[8].GetString();
-
// Must be done before WorldSession is created
- if (wardenActive && os != "Win" && os != "OSX")
+ bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED);
+ if (wardenActive && account.BattleNet.OS != "Win" && account.BattleNet.OS != "Wn64" && account.BattleNet.OS != "Mc64")
+ {
+ SendAuthResponseError(AUTH_REJECT);
+ TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), account.BattleNet.OS.c_str());
+ DelayedCloseSocket();
+ return;
+ }
+
+ if (!account.BattleNet.Id || authSession->LoginServerType != 1)
{
SendAuthResponseError(AUTH_REJECT);
- TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str());
+ TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s (%s) attempted to log in using deprecated login method (GRUNT).", authSession->Account.c_str(), address.c_str());
DelayedCloseSocket();
return;
}
@@ -556,151 +667,141 @@ void WorldSocket::HandleAuthSession(WorldPackets::Auth::AuthSession& authSession
// Check that Key and account name are the same on client and server
uint32 t = 0;
- sha.UpdateData(authSession.Account);
+ SHA1Hash sha;
+ sha.UpdateData(authSession->Account);
sha.UpdateData((uint8*)&t, 4);
- sha.UpdateData((uint8*)&authSession.LocalChallenge, 4);
+ sha.UpdateData((uint8*)&authSession->LocalChallenge, 4);
sha.UpdateData((uint8*)&_authSeed, 4);
- sha.UpdateBigNumbers(&k, NULL);
+ sha.UpdateBigNumbers(&account.Game.SessionKey, NULL);
sha.Finalize();
- if (memcmp(sha.GetDigest(), authSession.Digest, SHA_DIGEST_LENGTH) != 0)
+ if (memcmp(sha.GetDigest(), authSession->Digest, SHA_DIGEST_LENGTH) != 0)
{
SendAuthResponseError(AUTH_FAILED);
- TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, authSession.Account.c_str(), address.c_str());
+ TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", account.Game.Id, authSession->Account.c_str(), address.c_str());
DelayedCloseSocket();
return;
}
///- Re-check ip locking (same check as in auth).
- if (fields[3].GetUInt8() == 1) // if ip is locked
+ if (account.BattleNet.IsLockedToIP)
+ {
+ if (account.BattleNet.LastIP != address)
+ {
+ SendAuthResponseError(AUTH_FAILED);
+ TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", account.BattleNet.LastIP.c_str(), address.c_str());
+ // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well
+ sScriptMgr->OnFailedAccountLogin(account.Game.Id);
+ DelayedCloseSocket();
+ return;
+ }
+ }
+ else if (!account.BattleNet.LockCountry.empty() && !_ipCountry.empty())
{
- if (strcmp(fields[2].GetCString(), address.c_str()) != 0)
+ if (account.BattleNet.LockCountry != _ipCountry)
{
SendAuthResponseError(AUTH_FAILED);
- TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str());
+ TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account country differs. Original country: %s, new country: %s).", account.BattleNet.LockCountry.c_str(), _ipCountry.c_str());
// We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well
- sScriptMgr->OnFailedAccountLogin(id);
+ sScriptMgr->OnFailedAccountLogin(account.Game.Id);
DelayedCloseSocket();
return;
}
}
- int64 mutetime = fields[5].GetInt64();
+ int64 mutetime = account.Game.MuteTime;
//! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now.
if (mutetime < 0)
{
mutetime = time(NULL) + llabs(mutetime);
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN);
-
stmt->setInt64(0, mutetime);
- stmt->setUInt32(1, id);
-
+ stmt->setUInt32(1, account.Game.Id);
LoginDatabase.Execute(stmt);
}
- locale = LocaleConstant(fields[6].GetUInt8());
- if (locale >= TOTAL_LOCALES)
- locale = LOCALE_enUS;
-
- uint32 recruiter = fields[7].GetUInt32();
-
- uint32 battlenetAccountId = 0;
- if (authSession.LoginServerType == 1)
- battlenetAccountId = Battlenet::AccountMgr::GetIdByGameAccount(id);
-
- // Checks gmlevel per Realm
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID);
-
- stmt->setUInt32(0, id);
- stmt->setInt32(1, int32(realmHandle.Index));
-
- result = LoginDatabase.Query(stmt);
-
- if (!result)
- security = 0;
- else
- {
- fields = result->Fetch();
- security = fields[0].GetUInt8();
- }
-
- // Re-check account ban (same check as in auth)
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS);
-
- stmt->setUInt32(0, id);
- stmt->setString(1, address);
-
- PreparedQueryResult banresult = LoginDatabase.Query(stmt);
-
- if (banresult) // if account banned
+ if (account.IsBanned())
{
SendAuthResponseError(AUTH_BANNED);
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
- sScriptMgr->OnFailedAccountLogin(id);
+ sScriptMgr->OnFailedAccountLogin(account.Game.Id);
DelayedCloseSocket();
return;
}
// Check locked state for server
AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit();
- TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security));
- if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType)
+ TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, account.Game.Security);
+ if (allowedAccountType > SEC_PLAYER && account.Game.Security < allowedAccountType)
{
SendAuthResponseError(AUTH_UNAVAILABLE);
TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
- sScriptMgr->OnFailedAccountLogin(id);
+ sScriptMgr->OnFailedAccountLogin(account.Game.Id);
DelayedCloseSocket();
return;
}
- TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.",
- authSession.Account.c_str(), address.c_str());
-
- // Check if this user is by any chance a recruiter
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER);
-
- stmt->setUInt32(0, id);
-
- result = LoginDatabase.Query(stmt);
-
- bool isRecruiter = false;
- if (result)
- isRecruiter = true;
+ TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", authSession->Account.c_str(), address.c_str());
// Update the last_ip in the database as it was successful for login
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP);
stmt->setString(0, address);
- stmt->setString(1, authSession.Account);
+ stmt->setString(1, authSession->Account);
LoginDatabase.Execute(stmt);
// At this point, we can safely hook a successful login
- sScriptMgr->OnAccountLogin(id);
+ sScriptMgr->OnAccountLogin(account.Game.Id);
_authed = true;
- _worldSession = new WorldSession(id, battlenetAccountId, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter);
- _worldSession->LoadGlobalAccountData();
- _worldSession->LoadTutorialsData();
- _worldSession->ReadAddonsInfo(authSession.AddonInfo);
- _worldSession->LoadPermissions();
+ _worldSession = new WorldSession(account.Game.Id, std::move(authSession->Account), account.BattleNet.Id, shared_from_this(), account.Game.Security,
+ account.Game.Expansion, mutetime, account.BattleNet.Locale, account.Game.Recruiter, account.Game.IsRectuiter);
+ _worldSession->ReadAddonsInfo(authSession->AddonInfo);
// Initialize Warden system only if it is enabled by config
if (wardenActive)
- _worldSession->InitWarden(&k, os);
+ _worldSession->InitWarden(&account.Game.SessionKey, account.BattleNet.OS);
+
+ _queryCallback = io_service().wrap(std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1));
+ _queryFuture = _worldSession->LoadPermissionsAsync();
+}
+
+void WorldSocket::LoadSessionPermissionsCallback(PreparedQueryResult result)
+{
+ // RBAC must be loaded before adding session to check for skip queue permission
+ _worldSession->GetRBACData()->LoadFromDBCallback(result);
sWorld->AddSession(_worldSession);
}
-void WorldSocket::HandleAuthContinuedSession(WorldPackets::Auth::AuthContinuedSession& authSession)
+void WorldSocket::HandleAuthContinuedSession(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession)
{
- uint32 accountId = PAIR64_LOPART(authSession.Key);
- _type = ConnectionType(PAIR64_HIPART(authSession.Key));
+ _type = ConnectionType(PAIR64_HIPART(authSession->Key));
+ if (_type != CONNECTION_TYPE_INSTANCE)
+ {
+ SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
+ DelayedCloseSocket();
+ return;
+ }
+ // Client switches packet headers after sending CMSG_AUTH_CONTINUED_SESSION
+ _headerBuffer.Resize(SizeOfClientHeader[1][1]);
+
+ uint32 accountId = PAIR64_LOPART(authSession->Key);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION);
stmt->setUInt32(0, accountId);
- PreparedQueryResult result = LoginDatabase.Query(stmt);
+
+ {
+ std::lock_guard<std::mutex> guard(_queryLock);
+ _queryCallback = io_service().wrap(std::bind(&WorldSocket::HandleAuthContinuedSessionCallback, this, authSession, std::placeholders::_1));
+ _queryFuture = LoginDatabase.AsyncQuery(stmt);
+ }
+}
+
+void WorldSocket::HandleAuthContinuedSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession, PreparedQueryResult result)
+{
if (!result)
{
SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
@@ -708,13 +809,13 @@ void WorldSocket::HandleAuthContinuedSession(WorldPackets::Auth::AuthContinuedSe
return;
}
+ uint32 accountId = PAIR64_LOPART(authSession->Key);
Field* fields = result->Fetch();
std::string login = fields[0].GetString();
BigNumber k;
k.SetHexStr(fields[1].GetCString());
_authCrypt.Init(&k, _encryptSeed.AsByteArray().get(), _decryptSeed.AsByteArray().get());
- _headerBuffer.Resize(SizeOfClientHeader[1][1]);
SHA1Hash sha;
sha.UpdateData(login);
@@ -722,7 +823,7 @@ void WorldSocket::HandleAuthContinuedSession(WorldPackets::Auth::AuthContinuedSe
sha.UpdateData((uint8*)&_authSeed, 4);
sha.Finalize();
- if (memcmp(sha.GetDigest(), authSession.Digest, sha.GetLength()))
+ if (memcmp(sha.GetDigest(), authSession->Digest, sha.GetLength()))
{
SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
TC_LOG_ERROR("network", "WorldSocket::HandleAuthContinuedSession: Authentication failed for account: %u ('%s') address: %s", accountId, login.c_str(), GetRemoteIpAddress().to_string().c_str());
diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h
index b82029f88d5..9b51b564c8e 100644
--- a/src/server/game/Server/WorldSocket.h
+++ b/src/server/game/Server/WorldSocket.h
@@ -72,6 +72,8 @@ class WorldSocket : public Socket<WorldSocket>
static std::string const ClientConnectionInitialize;
static uint32 const MinSizeForCompression;
+ typedef Socket<WorldSocket> BaseSocket;
+
public:
WorldSocket(tcp::socket&& socket);
~WorldSocket();
@@ -80,6 +82,7 @@ public:
WorldSocket& operator=(WorldSocket const& right) = delete;
void Start() override;
+ bool Update() override;
void SendPacket(WorldPacket const& packet);
@@ -94,6 +97,8 @@ protected:
bool ReadHeaderHandler();
bool ReadDataHandler();
private:
+ void CheckIpCallback(PreparedQueryResult result);
+
/// writes network.opcode log
/// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock
void LogOpcodeText(OpcodeClient opcode, std::unique_lock<std::mutex> const& guard) const;
@@ -103,8 +108,11 @@ private:
uint32 CompressPacket(uint8* buffer, WorldPacket const& packet);
void HandleSendAuthSession();
- void HandleAuthSession(WorldPackets::Auth::AuthSession& authSession);
- void HandleAuthContinuedSession(WorldPackets::Auth::AuthContinuedSession& authSession);
+ void HandleAuthSession(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession);
+ void HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession, PreparedQueryResult result);
+ void HandleAuthContinuedSession(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession);
+ void HandleAuthContinuedSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession, PreparedQueryResult result);
+ void LoadSessionPermissionsCallback(PreparedQueryResult result);
void HandleConnectToFailed(WorldPackets::Auth::ConnectToFailed& connectToFailed);
bool HandlePing(WorldPacket& recvPacket);
@@ -131,6 +139,11 @@ private:
z_stream_s* _compressionStream;
bool _initialized;
+
+ std::mutex _queryLock;
+ PreparedQueryResultFuture _queryFuture;
+ std::function<void(PreparedQueryResult&&)> _queryCallback;
+ std::string _ipCountry;
};
#endif
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index b5f154e6fe1..461fc1d0a76 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -4089,10 +4089,11 @@ void AuraEffect::HandleAuraModIncreaseHealth(AuraApplication const* aurApp, uint
}
else
{
- if (int32(target->GetHealth()) > GetAmount())
- target->ModifyHealth(-GetAmount());
- else
- target->SetHealth(1);
+ if (target->GetHealth() > 0)
+ {
+ int32 value = std::min<int32>(target->GetHealth() - 1, GetAmount());
+ target->ModifyHealth(-value);
+ }
target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply);
}
}
@@ -4104,19 +4105,15 @@ void AuraEffect::HandleAuraModIncreaseMaxHealth(AuraApplication const* aurApp, u
Unit* target = aurApp->GetTarget();
- uint32 oldhealth = target->GetHealth();
- double healthPercentage = (double)oldhealth / (double)target->GetMaxHealth();
+ float percent = target->GetHealthPct();
target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply);
// refresh percentage
- if (oldhealth > 0)
+ if (target->GetHealth() > 0)
{
- uint32 newhealth = uint32(ceil((double)target->GetMaxHealth() * healthPercentage));
- if (newhealth == 0)
- newhealth = 1;
-
- target->SetHealth(newhealth);
+ uint32 newHealth = std::max<uint32>(target->CountPctFromMaxHealth(int32(percent)), 1);
+ target->SetHealth(newHealth);
}
}
@@ -4180,8 +4177,12 @@ void AuraEffect::HandleAuraModIncreaseHealthPercent(AuraApplication const* aurAp
// Unit will keep hp% after MaxHealth being modified if unit is alive.
float percent = target->GetHealthPct();
target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(GetAmount()), apply);
- if (target->IsAlive())
- target->SetHealth(target->CountPctFromMaxHealth(int32(percent)));
+
+ if (target->GetHealth() > 0)
+ {
+ uint32 newHealth = std::max<uint32>(target->CountPctFromMaxHealth(int32(percent)), 1);
+ target->SetHealth(newHealth);
+ }
}
void AuraEffect::HandleAuraIncreaseBaseHealthPercent(AuraApplication const* aurApp, uint8 mode, bool apply) const
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index cb0f34c162b..05e2f5d33ff 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -5134,12 +5134,12 @@ SpellCastResult Spell::CheckCast(bool strict)
bool result = m_preGeneratedPath.CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + target->GetObjectSize(), false, true);
if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT)
return SPELL_FAILED_OUT_OF_RANGE;
- else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH)
+ else if (!result || m_preGeneratedPath.GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE))
{
result = m_preGeneratedPath.CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + target->GetObjectSize(), false, false);
if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT)
return SPELL_FAILED_OUT_OF_RANGE;
- else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH)
+ else if (!result || m_preGeneratedPath.GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE))
return SPELL_FAILED_NOPATH;
}
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index ec716a0e03d..77abd3c78c2 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -409,6 +409,7 @@ class Spell
void EffectLearnGarrisonBuilding(SpellEffIndex effIndex);
void EffectCreateGarrison(SpellEffIndex effIndex);
void EffectAddGarrisonFollower(SpellEffIndex effIndex);
+ void EffectActivateGarrisonBuilding(SpellEffIndex effIndex);
typedef std::set<Aura*> UsedSpellMods;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 36e544de3bd..e4bef46b3bb 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -299,7 +299,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //221 SPELL_EFFECT_221
&Spell::EffectNULL, //222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM
&Spell::EffectNULL, //223 SPELL_EFFECT_CHANGE_ITEM_BONUSES
- &Spell::EffectNULL, //224 SPELL_EFFECT_ACTIVATE_GARRISON_BUILDING
+ &Spell::EffectActivateGarrisonBuilding, //224 SPELL_EFFECT_ACTIVATE_GARRISON_BUILDING
&Spell::EffectNULL, //225 SPELL_EFFECT_GRANT_BATTLEPET_LEVEL
&Spell::EffectNULL, //226 SPELL_EFFECT_226
&Spell::EffectNULL, //227 SPELL_EFFECT_227
@@ -5859,3 +5859,15 @@ void Spell::EffectAddGarrisonFollower(SpellEffIndex effIndex)
if (Garrison* garrison = unitTarget->ToPlayer()->GetGarrison())
garrison->AddFollower(GetEffect(effIndex)->MiscValue);
}
+
+void Spell::EffectActivateGarrisonBuilding(SpellEffIndex effIndex)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+ return;
+
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (Garrison* garrison = unitTarget->ToPlayer()->GetGarrison())
+ garrison->ActivateBuilding(GetEffect(effIndex)->MiscValue);
+}
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 6560309527c..b61469a1fa4 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -3239,8 +3239,6 @@ void SpellInfo::_UnloadImplicitTargetConditionLists()
SpellEffectInfoVector SpellInfo::GetEffectsForDifficulty(uint32 difficulty) const
{
- // 6.x todo: add first highest difficulty effect, resize list to max element, add lower diff effects without overwriting any higher diffed ones
-
SpellEffectInfoVector effList;
// DIFFICULTY_NONE effects are the default effects, always active if current difficulty's effects don't overwrite
@@ -3250,7 +3248,8 @@ SpellEffectInfoVector SpellInfo::GetEffectsForDifficulty(uint32 difficulty) cons
// downscale difficulty if original was not found
// DIFFICULTY_NONE is already in our list
- for (; difficulty > DIFFICULTY_NONE; --difficulty)
+ DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty);
+ while (difficultyEntry)
{
SpellEffectInfoMap::const_iterator itr = _effects.find(difficulty);
if (itr != _effects.end())
@@ -3263,23 +3262,35 @@ SpellEffectInfoVector SpellInfo::GetEffectsForDifficulty(uint32 difficulty) cons
if (effect->EffectIndex >= effList.size())
effList.resize(effect->EffectIndex + 1);
- effList[effect->EffectIndex] = effect;
+ if (!effList[effect->EffectIndex])
+ effList[effect->EffectIndex] = effect;
}
}
- // if we found any effect in our difficulty then stop searching
- break;
}
+
+ difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID);
}
- if (effList.empty())
- TC_LOG_ERROR("spells", "GetEffectsForDifficulty did not find any effects for spell %u in difficulty %u", Id, difficulty);
+
return effList;
}
SpellEffectInfo const* SpellInfo::GetEffect(uint32 difficulty, uint32 index) const
{
- SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty);
- if (index >= effects.size())
- return nullptr;
+ DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty);
+ while (difficultyEntry)
+ {
+ SpellEffectInfoMap::const_iterator itr = _effects.find(difficulty);
+ if (itr != _effects.end())
+ if (itr->second.size() > index && itr->second[index])
+ return itr->second[index];
+
+ difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID);
+ }
+
+ SpellEffectInfoMap::const_iterator itr = _effects.find(DIFFICULTY_NONE);
+ if (itr != _effects.end())
+ if (itr->second.size() > index)
+ return itr->second[index];
- return effects[index];
+ return nullptr;
}
diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index 4fd8b5d8bf6..62aa5a52279 100644
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -664,8 +664,8 @@ SpellEffectInfo const* SpellScript::GetEffectInfo(SpellEffIndex effIndex) const
bool AuraScript::_Validate(SpellInfo const* entry)
{
for (std::list<CheckAreaTargetHandler>::iterator itr = DoCheckAreaTarget.begin(); itr != DoCheckAreaTarget.end(); ++itr)
- if (!entry->HasAreaAuraEffect() && !entry->HasEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA))
- TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have area aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+ if (!entry->HasAreaAuraEffect() && !entry->HasEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) && !entry->HasEffect(SPELL_EFFECT_APPLY_AURA))
+ TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
for (std::list<AuraDispelHandler>::iterator itr = OnDispel.begin(); itr != OnDispel.end(); ++itr)
if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect())
diff --git a/src/server/game/Texts/ChatTextBuilder.h b/src/server/game/Texts/ChatTextBuilder.h
index 167680f1cd2..68cd375edb6 100644
--- a/src/server/game/Texts/ChatTextBuilder.h
+++ b/src/server/game/Texts/ChatTextBuilder.h
@@ -30,13 +30,12 @@ namespace Trinity
BroadcastTextBuilder(Unit const* obj, ChatMsg msgType, uint32 textId, WorldObject const* target = nullptr, uint32 achievementId = 0)
: _source(obj), _msgType(msgType), _textId(textId), _target(target), _achievementId(achievementId) { }
- void operator()(WorldPacket& data, LocaleConstant locale)
+ WorldPackets::Chat::Chat* operator()(LocaleConstant locale) const
{
BroadcastTextEntry const* bct = sBroadcastTextStore.LookupEntry(_textId);
- WorldPackets::Chat::Chat packet;
- packet.Initialize(_msgType, bct ? Language(bct->Language) : LANG_UNIVERSAL, _source, _target, bct ? DB2Manager::GetBroadcastTextValue(bct, locale, _source->getGender()) : "", _achievementId, "", locale);
- packet.Write();
- data = packet.Move();
+ WorldPackets::Chat::Chat* chat = new WorldPackets::Chat::Chat();
+ chat->Initialize(_msgType, bct ? Language(bct->Language) : LANG_UNIVERSAL, _source, _target, bct ? DB2Manager::GetBroadcastTextValue(bct, locale, _source->getGender()) : "", _achievementId, "", locale);
+ return chat;
}
private:
@@ -53,12 +52,11 @@ namespace Trinity
CustomChatTextBuilder(WorldObject const* obj, ChatMsg msgType, std::string const& text, Language language = LANG_UNIVERSAL, WorldObject const* target = nullptr)
: _source(obj), _msgType(msgType), _text(text), _language(language), _target(target) { }
- void operator()(WorldPacket& data, LocaleConstant locale)
+ WorldPackets::Chat::Chat* operator()(LocaleConstant locale) const
{
- WorldPackets::Chat::Chat packet;
- packet.Initialize(_msgType, _language, _source, _target, _text, 0, "", locale);
- packet.Write();
- data = packet.Move();
+ WorldPackets::Chat::Chat* chat = new WorldPackets::Chat::Chat();
+ chat->Initialize(_msgType, _language, _source, _target, _text, 0, "", locale);
+ return chat;
}
private:
diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp
index c1ea235a3e2..4d61e2ec5d2 100644
--- a/src/server/game/Texts/CreatureTextMgr.cpp
+++ b/src/server/game/Texts/CreatureTextMgr.cpp
@@ -33,13 +33,12 @@ class CreatureTextBuilder
CreatureTextBuilder(WorldObject const* obj, uint8 gender, ChatMsg msgtype, uint8 textGroup, uint32 id, uint32 language, WorldObject const* target)
: _source(obj), _gender(gender), _msgType(msgtype), _textGroup(textGroup), _textId(id), _language(language), _target(target) { }
- void operator()(WorldPacket& data, LocaleConstant locale) const
+ WorldPackets::Chat::Chat* operator()(LocaleConstant locale) const
{
std::string const& text = sCreatureTextMgr->GetLocalizedChatString(_source->GetEntry(), _gender, _textGroup, _textId, locale);
- WorldPackets::Chat::Chat packet;
- packet.Initialize(_msgType, Language(_language), _source, _target, text, 0, "", locale);
- packet.Write();
- data = packet.Move();
+ WorldPackets::Chat::Chat* chat = new WorldPackets::Chat::Chat();
+ chat->Initialize(_msgType, Language(_language), _source, _target, text, 0, "", locale);
+ return chat;
}
private:
@@ -58,13 +57,12 @@ class PlayerTextBuilder
PlayerTextBuilder(WorldObject const* obj, WorldObject const* speaker, uint8 gender, ChatMsg msgtype, uint8 textGroup, uint32 id, uint32 language, WorldObject const* target)
: _source(obj), _talker(speaker), _gender(gender), _msgType(msgtype), _textGroup(textGroup), _textId(id), _language(language), _target(target) { }
- void operator()(WorldPacket& data, LocaleConstant locale) const
+ WorldPackets::Chat::Chat* operator()(LocaleConstant locale) const
{
std::string const& text = sCreatureTextMgr->GetLocalizedChatString(_source->GetEntry(), _gender, _textGroup, _textId, locale);
- WorldPackets::Chat::Chat packet;
- packet.Initialize(_msgType, Language(_language), _talker, _target, text, 0, "", locale);
- packet.Write();
- data = packet.Move();
+ WorldPackets::Chat::Chat* chat = new WorldPackets::Chat::Chat();
+ chat->Initialize(_msgType, Language(_language), _talker, _target, text, 0, "", locale);
+ return chat;
}
private:
diff --git a/src/server/game/Texts/CreatureTextMgr.h b/src/server/game/Texts/CreatureTextMgr.h
index 307f6ece4f8..f2963006599 100644
--- a/src/server/game/Texts/CreatureTextMgr.h
+++ b/src/server/game/Texts/CreatureTextMgr.h
@@ -145,23 +145,25 @@ class CreatureTextLocalizer
// create if not cached yet
if (!_packetCache[loc_idx])
{
- messageTemplate = new WorldPackets::Chat::Chat();
+ messageTemplate = _builder(loc_idx);
_packetCache[loc_idx] = messageTemplate;
}
else
messageTemplate = _packetCache[loc_idx];
+ WorldPackets::Chat::Chat message(*messageTemplate);
+
switch (_msgType)
{
case CHAT_MSG_MONSTER_WHISPER:
case CHAT_MSG_RAID_BOSS_WHISPER:
- messageTemplate->TargetGUID = player->GetGUID();
+ message.SetReceiver(player, loc_idx);
break;
default:
break;
}
- player->SendDirectMessage(messageTemplate->Write());
+ player->SendDirectMessage(message.Write());
}
private:
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index d68da8fbc63..50fc812c967 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -282,14 +282,7 @@ void World::AddSession_(WorldSession* s)
return;
}
- s->SendAuthResponse(AUTH_OK, false);
-
- s->SendSetTimeZoneInformation();
- s->SendFeatureSystemStatusGlueScreen();
-
- s->SendAddonsInfo();
- s->SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION));
- s->SendTutorialsData();
+ s->InitializeSession();
UpdateMaxSessionCounters();
@@ -394,18 +387,7 @@ bool World::RemoveQueuedPlayer(WorldSession* sess)
if ((!m_playerLimit || sessions < m_playerLimit) && !m_QueuedPlayer.empty())
{
WorldSession* pop_sess = m_QueuedPlayer.front();
- pop_sess->SetInQueue(false);
- pop_sess->ResetTimeOutTime();
- pop_sess->SendAuthWaitQue(0);
-
- pop_sess->SendSetTimeZoneInformation();
- pop_sess->SendFeatureSystemStatusGlueScreen();
-
- pop_sess->SendAddonsInfo();
-
- pop_sess->SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION));
- pop_sess->SendTutorialsData();
-
+ pop_sess->InitializeSession();
m_QueuedPlayer.pop_front();
// update iter to point first queued socket or end() if queue is empty now
@@ -1845,6 +1827,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading faction change spell pairs...");
sObjectMgr->LoadFactionChangeSpells();
+ TC_LOG_INFO("server.loading", "Loading faction change quest pairs...");
+ sObjectMgr->LoadFactionChangeQuests();
+
TC_LOG_INFO("server.loading", "Loading faction change item pairs...");
sObjectMgr->LoadFactionChangeItems();
@@ -2351,7 +2336,7 @@ namespace Trinity
class WorldWorldTextBuilder
{
public:
- typedef std::vector<WorldPacket*> WorldPacketList;
+ typedef std::vector<WorldPackets::Packet*> WorldPacketList;
static size_t const BufferSize = 2048;
explicit WorldWorldTextBuilder(uint32 textId, va_list* args = NULL) : i_textId(textId), i_args(args) { }
@@ -2383,10 +2368,10 @@ namespace Trinity
{
while (char* line = ChatHandler::LineFromMessage(text))
{
- WorldPackets::Chat::Chat packet;
- packet.Initialize(CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
- packet.Write();
- dataList.emplace_back(new WorldPacket(packet.Move()));
+ WorldPackets::Chat::Chat* packet = new WorldPackets::Chat::Chat();
+ packet->Initialize(CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
+ packet->Write();
+ dataList.push_back(packet);
}
}
diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp
index 53522736718..fcfee396627 100644
--- a/src/server/scripts/Commands/cs_go.cpp
+++ b/src/server/scripts/Commands/cs_go.cpp
@@ -123,7 +123,7 @@ public:
whereClause << "WHERE guid = '" << guid << '\'';
}
- QueryResult result = WorldDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map, guid, id FROM creature %s", whereClause.str().c_str());
+ QueryResult result = WorldDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map FROM creature %s", whereClause.str().c_str());
if (!result)
{
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
@@ -139,10 +139,6 @@ public:
float z = fields[2].GetFloat();
float o = fields[3].GetFloat();
uint32 mapId = fields[4].GetUInt16();
- ObjectGuid::LowType guid = fields[5].GetUInt64();
- uint32 id = fields[6].GetUInt32();
-
- Transport* transport = NULL;
if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId))
{
@@ -161,11 +157,8 @@ public:
else
player->SaveRecallPosition();
- if (player->TeleportTo(mapId, x, y, z, o))
- {
- if (transport)
- transport->AddPassenger(player);
- }
+ player->TeleportTo(mapId, x, y, z, o);
+
return true;
}
diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp
index 55562a004ac..09930770f63 100644
--- a/src/server/scripts/Commands/cs_tele.cpp
+++ b/src/server/scripts/Commands/cs_tele.cpp
@@ -319,7 +319,7 @@ public:
}
MapEntry const* map = sMapStore.LookupEntry(tele->mapId);
- if (!map || map->IsBattlegroundOrArena())
+ if (!map || (map->IsBattlegroundOrArena() && (me->GetMapId() != tele->mapId || !me->IsGameMaster())))
{
handler->SendSysMessage(LANG_CANNOT_TELE_TO_BG);
handler->SetSentErrorMessage(true);
diff --git a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp
index 46e831b0f83..7bd197774bc 100644
--- a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp
+++ b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp
@@ -111,6 +111,7 @@ public:
instance_zulfarrak_InstanceMapScript(Map* map) : InstanceScript(map)
{
SetHeaders(DataHeader);
+ GahzRillaEncounter = NOT_STARTED;
PyramidPhase = 0;
major_wave_Timer = 0;
minor_wave_Timer = 0;
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
index 39c9fbe37a6..c56a49cb92c 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
@@ -741,7 +741,7 @@ class boss_dreadscale : public CreatureScript
switch (pointId)
{
case 0:
- instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR));
+ instance->DoCloseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR));
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
diff --git a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
index ea50969ecb8..ef9ad806c89 100644
--- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
+++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
@@ -794,9 +794,11 @@ public:
trigger->CastSpell(trigger, spellInfoLightning, true, 0, 0, trigger->GetGUID());
// Kill all mobs registered with SetGuidData(ADD_TRASH_MOB)
- for (GuidSet::const_iterator itr = trashMobs.begin(); itr != trashMobs.end(); ++itr)
+ for (GuidSet::const_iterator itr = trashMobs.begin(); itr != trashMobs.end();)
{
Creature* creature = instance->GetCreature(*itr);
+ // Increment the iterator before killing the creature because the kill will remove itr from trashMobs
+ ++itr;
if (creature && creature->IsAlive())
trigger->Kill(creature);
}
diff --git a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp
index 662bad46162..263fd8340b9 100644
--- a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp
+++ b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp
@@ -113,7 +113,7 @@ public:
Map::PlayerList const &PlayerList = map->GetPlayers();
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (Player* i_pl = i->GetSource())
- if (i_pl->IsAlive() && (dist = i_pl->IsWithinDist(me, 45)))
+ if (i_pl->IsAlive() && (dist = i_pl->GetDistance(me)) < 45)
{
i_pl->RemoveAurasDueToSpell(SPELL_INHIBITMAGIC);
me->AddAura(SPELL_INHIBITMAGIC, i_pl);
diff --git a/src/server/shared/Database/DatabaseLoader.cpp b/src/server/shared/Database/DatabaseLoader.cpp
index 25c400fdfa8..1d704100d93 100644
--- a/src/server/shared/Database/DatabaseLoader.cpp
+++ b/src/server/shared/Database/DatabaseLoader.cpp
@@ -117,6 +117,9 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
bool DatabaseLoader::Load()
{
+ if (!_updateFlags)
+ TC_LOG_INFO("sql.updates", "Automatic database updates are disabled for all databases!");
+
if (!OpenDatabases())
return false;
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
index 277ccd4569a..53fc0bba831 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
@@ -276,7 +276,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_PLAYER_CURRENCY, "DELETE FROM character_currency WHERE CharacterGuid = ?", CONNECTION_ASYNC);
// Account data
- PrepareStatement(CHAR_SEL_ACCOUNT_DATA, "SELECT type, time, data FROM account_data WHERE accountId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_ACCOUNT_DATA, "SELECT type, time, data FROM account_data WHERE accountId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_ACCOUNT_DATA, "REPLACE INTO account_data (accountId, type, time, data) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ACCOUNT_DATA, "DELETE FROM account_data WHERE accountId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PLAYER_ACCOUNT_DATA, "SELECT type, time, data FROM character_account_data WHERE guid = ?", CONNECTION_ASYNC);
@@ -284,7 +284,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_PLAYER_ACCOUNT_DATA, "DELETE FROM character_account_data WHERE guid = ?", CONNECTION_ASYNC);
// Tutorials
- PrepareStatement(CHAR_SEL_TUTORIALS, "SELECT tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7 FROM account_tutorial WHERE accountId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_TUTORIALS, "SELECT tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7 FROM account_tutorial WHERE accountId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_HAS_TUTORIALS, "SELECT 1 FROM account_tutorial WHERE accountId = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_INS_TUTORIALS, "INSERT INTO account_tutorial(tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7, accountId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_TUTORIALS, "UPDATE account_tutorial SET tut0 = ?, tut1 = ?, tut2 = ?, tut3 = ?, tut4 = ?, tut5 = ?, tut6 = ?, tut7 = ? WHERE accountId = ?", CONNECTION_ASYNC);
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp
index 0b2967f8922..51a0217ac17 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp
@@ -34,12 +34,17 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_ALL, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 AND username LIKE CONCAT('%%', ?, '%%') GROUP BY account.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION, "SELECT username, sessionkey FROM account WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION, "SELECT username, sessionkey FROM account WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, "
+ "bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id "
+ "FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) "
+ "LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account r ON a.id = r.recruiter "
+ "WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC);
+
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?", CONNECTION_SYNCH);
@@ -83,8 +88,6 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_ACCOUNT_INFO, "SELECT a.username, a.last_ip, aa.gmlevel, a.expansion FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST, "SELECT 1 FROM account_access WHERE id = ? AND gmlevel > ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS, "SELECT a.id, aa.gmlevel, aa.RealmID FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_ACCOUNT_RECRUITER, "SELECT 1 FROM account WHERE recruiter = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_BANS, "SELECT 1 FROM account_banned WHERE id = ? AND active = 1 UNION SELECT 1 FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_WHOIS, "SELECT username, email, last_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_LAST_ATTEMPT_IP, "SELECT last_attempt_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_LAST_IP, "SELECT last_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
@@ -103,7 +106,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES ((SELECT id FROM account WHERE username = ?), 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, "SELECT gmlevel, RealmID FROM account_access WHERE id = ? and (RealmID = ? OR RealmID = -1) ORDER BY gmlevel desc", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_BOTH);
PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION, "INSERT INTO rbac_account_permissions (accountId, permissionId, granted, realmId) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE granted = VALUES(granted)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION, "DELETE FROM rbac_account_permissions WHERE accountId = ? AND permissionId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC);
@@ -113,10 +116,14 @@ void LoginDatabaseConnection::DoPrepareStatements()
#define BnetAccountInfo "ba.id, UPPER(ba.email), ba.locked, ba.lock_country, ba.last_ip, ba.failed_logins, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate"
#define BnetGameAccountInfo "a.id, a.username, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, aa.gmlevel"
- PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", ba.sha_pass_hash, ba.v, ba.s, " BnetGameAccountInfo " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", ba.sha_pass_hash, ba.v, ba.s, " BnetGameAccountInfo
+ " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account"
+ " LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_BNET_VS_FIELDS, "UPDATE battlenet_accounts SET v = ?, s = ? WHERE email = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_BNET_SESSION_KEY, "UPDATE battlenet_accounts SET sessionKey = ?, online = ? WHERE id = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_SEL_BNET_RECONNECT_INFO, "SELECT " BnetAccountInfo ", ba.sessionKey, " BnetGameAccountInfo " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? AND a.username = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_BNET_RECONNECT_INFO, "SELECT " BnetAccountInfo ", ba.sessionKey, " BnetGameAccountInfo
+ " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account"
+ " LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? AND a.username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_BNET_FAILED_LOGINS, "UPDATE battlenet_accounts SET failed_logins = failed_logins + 1 WHERE email = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS, "SELECT rc.numchars, r.id, r.Region, r.Battlegroup, r.gamebuild FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_ASYNC);
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h
index c0aa178f199..06d13f29da3 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.h
+++ b/src/server/shared/Database/Implementation/LoginDatabase.h
@@ -101,8 +101,6 @@ enum LoginDatabaseStatements
LOGIN_SEL_ACCOUNT_INFO,
LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST,
LOGIN_SEL_ACCOUNT_ACCESS,
- LOGIN_SEL_ACCOUNT_RECRUITER,
- LOGIN_SEL_BANS,
LOGIN_SEL_ACCOUNT_WHOIS,
LOGIN_SEL_REALMLIST_SECURITY_LEVEL,
LOGIN_DEL_ACCOUNT,
diff --git a/src/server/shared/Debugging/WheatyExceptionReport.cpp b/src/server/shared/Debugging/WheatyExceptionReport.cpp
index e50cf42e439..e9f888f280d 100644
--- a/src/server/shared/Debugging/WheatyExceptionReport.cpp
+++ b/src/server/shared/Debugging/WheatyExceptionReport.cpp
@@ -62,6 +62,8 @@ HANDLE WheatyExceptionReport::m_hProcess;
SymbolPairs WheatyExceptionReport::symbols;
std::stack<SymbolDetail> WheatyExceptionReport::symbolDetails;
bool WheatyExceptionReport::stackOverflowException;
+bool WheatyExceptionReport::alreadyCrashed;
+std::mutex WheatyExceptionReport::alreadyCrashedLock;
// Declare global instance of class
WheatyExceptionReport g_WheatyExceptionReport;
@@ -74,6 +76,7 @@ WheatyExceptionReport::WheatyExceptionReport() // Constructor
m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
m_hProcess = GetCurrentProcess();
stackOverflowException = false;
+ alreadyCrashed = false;
if (!IsDebuggerPresent())
{
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
@@ -99,6 +102,13 @@ WheatyExceptionReport::~WheatyExceptionReport()
LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo)
{
+ std::unique_lock<std::mutex> guard(alreadyCrashedLock);
+ // Handle only 1 exception in the whole process lifetime
+ if (alreadyCrashed)
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ alreadyCrashed = true;
+
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
stackOverflowException = true;
diff --git a/src/server/shared/Debugging/WheatyExceptionReport.h b/src/server/shared/Debugging/WheatyExceptionReport.h
index 101b6187f2b..ef6334add16 100644
--- a/src/server/shared/Debugging/WheatyExceptionReport.h
+++ b/src/server/shared/Debugging/WheatyExceptionReport.h
@@ -194,6 +194,8 @@ class WheatyExceptionReport
static SymbolPairs symbols;
static std::stack<SymbolDetail> symbolDetails;
static bool stackOverflowException;
+ static bool alreadyCrashed;
+ static std::mutex alreadyCrashedLock;
static char* PushSymbolDetail(char* pszCurrBuffer);
static char* PopSymbolDetail(char* pszCurrBuffer);
diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h
index d337e07ff52..23c4f25b742 100644
--- a/src/server/shared/Networking/Socket.h
+++ b/src/server/shared/Networking/Socket.h
@@ -169,6 +169,8 @@ protected:
MessageBuffer _writeBuffer;
#endif
+ boost::asio::io_service& io_service() { return _socket.get_io_service(); }
+
private:
void ReadHandlerInternal(boost::system::error_code error, size_t transferredBytes)
{
diff --git a/src/server/shared/Updater/DBUpdater.cpp b/src/server/shared/Updater/DBUpdater.cpp
index 10c8c163ec4..c74b8ef868e 100644
--- a/src/server/shared/Updater/DBUpdater.cpp
+++ b/src/server/shared/Updater/DBUpdater.cpp
@@ -271,10 +271,10 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
[&](Path const& file) { DBUpdater<T>::ApplyFile(pool, file); },
[&](std::string const& query) -> QueryResult { return DBUpdater<T>::Retrieve(pool, query); });
- uint32 count;
+ UpdateResult result;
try
{
- count = updateFetcher.Update(
+ result = updateFetcher.Update(
sConfigMgr->GetBoolDefault("Updates.Redundancy", true),
sConfigMgr->GetBoolDefault("Updates.AllowRehash", true),
sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false),
@@ -285,10 +285,13 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
return false;
}
- if (!count)
- TC_LOG_INFO("sql.updates", ">> %s database is up-to-date!", DBUpdater<T>::GetTableName().c_str());
+ std::string const info = Trinity::StringFormat("Containing " SZFMTD " new and " SZFMTD " archived updates.",
+ result.recent, result.archived);
+
+ if (!result.updated)
+ TC_LOG_INFO("sql.updates", ">> %s database is up-to-date! %s", DBUpdater<T>::GetTableName().c_str(), info.c_str());
else
- TC_LOG_INFO("sql.updates", ">> Applied %d %s.", count, count == 1 ? "query" : "queries");
+ TC_LOG_INFO("sql.updates", ">> Applied " SZFMTD " %s. %s", result.updated, result.updated == 1 ? "query" : "queries", info.c_str());
return true;
}
diff --git a/src/server/shared/Updater/DBUpdater.h b/src/server/shared/Updater/DBUpdater.h
index 0caf8a438fb..a2b12bed235 100644
--- a/src/server/shared/Updater/DBUpdater.h
+++ b/src/server/shared/Updater/DBUpdater.h
@@ -41,6 +41,19 @@ enum BaseLocation
LOCATION_DOWNLOAD
};
+struct UpdateResult
+{
+ UpdateResult()
+ : updated(0), recent(0), archived(0) { }
+
+ UpdateResult(size_t const updated_, size_t const recent_, size_t const archived_)
+ : updated(updated_), recent(recent_), archived(archived_) { }
+
+ size_t updated;
+ size_t recent;
+ size_t archived;
+};
+
template <class T>
class DBUpdater
{
diff --git a/src/server/shared/Updater/UpdateFetcher.cpp b/src/server/shared/Updater/UpdateFetcher.cpp
index a4bdd193743..ec023928b99 100644
--- a/src/server/shared/Updater/UpdateFetcher.cpp
+++ b/src/server/shared/Updater/UpdateFetcher.cpp
@@ -154,17 +154,27 @@ UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path co
return update;
}
-uint32 UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const
+UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const
{
LocaleFileStorage const available = GetFileList();
AppliedFileStorage applied = ReceiveAppliedFiles();
+ size_t countRecentUpdates = 0;
+ size_t countArchivedUpdates = 0;
+
+ // Count updates
+ for (auto const& entry : applied)
+ if (entry.second.state == RELEASED)
+ ++countRecentUpdates;
+ else
+ ++countArchivedUpdates;
+
// Fill hash to name cache
HashToFileNameStorage hashToName;
for (auto entry : applied)
hashToName.insert(std::make_pair(entry.second.hash, entry.first));
- uint32 importedUpdates = 0;
+ size_t importedUpdates = 0;
for (auto const& availableQuery : available)
{
@@ -314,7 +324,7 @@ uint32 UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash
}
}
- return importedUpdates;
+ return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates);
}
std::string UpdateFetcher::CalculateHash(SQLUpdate const& query) const
@@ -329,7 +339,6 @@ std::string UpdateFetcher::CalculateHash(SQLUpdate const& query) const
uint32 UpdateFetcher::Apply(Path const& path) const
{
using Time = std::chrono::high_resolution_clock;
- using ms = std::chrono::milliseconds;
// Benchmark query speed
auto const begin = Time::now();
@@ -338,7 +347,7 @@ uint32 UpdateFetcher::Apply(Path const& path) const
_applyFile(path);
// Return time the query took to apply
- return std::chrono::duration_cast<ms>(Time::now() - begin).count();
+ return std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count();
}
void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const
diff --git a/src/server/shared/Updater/UpdateFetcher.h b/src/server/shared/Updater/UpdateFetcher.h
index fa142547873..4ff8c93bc76 100644
--- a/src/server/shared/Updater/UpdateFetcher.h
+++ b/src/server/shared/Updater/UpdateFetcher.h
@@ -35,7 +35,7 @@ public:
std::function<void(Path const& path)> const& applyFile,
std::function<QueryResult(std::string const&)> const& retrieve);
- uint32 Update(bool const redundancyChecks, bool const allowRehash,
+ UpdateResult Update(bool const redundancyChecks, bool const allowRehash,
bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const;
private:
diff --git a/src/server/shared/Utilities/TaskScheduler.cpp b/src/server/shared/Utilities/TaskScheduler.cpp
index 4b261413fd9..04a4071d1f5 100644
--- a/src/server/shared/Utilities/TaskScheduler.cpp
+++ b/src/server/shared/Utilities/TaskScheduler.cpp
@@ -17,16 +17,22 @@
#include "TaskScheduler.h"
-TaskScheduler& TaskScheduler::Update()
+TaskScheduler& TaskScheduler::ClearValidator()
+{
+ _predicate = EmptyValidator;
+ return *this;
+}
+
+TaskScheduler& TaskScheduler::Update(success_t const& callback)
{
_now = clock_t::now();
- Dispatch();
+ Dispatch(callback);
return *this;
}
-TaskScheduler& TaskScheduler::Update(size_t const milliseconds)
+TaskScheduler& TaskScheduler::Update(size_t const milliseconds, success_t const& callback)
{
- return Update(std::chrono::milliseconds(milliseconds));
+ return Update(std::chrono::milliseconds(milliseconds), callback);
}
TaskScheduler& TaskScheduler::Async(std::function<void()> const& callable)
@@ -66,15 +72,26 @@ TaskScheduler& TaskScheduler::InsertTask(TaskContainer task)
return *this;
}
-void TaskScheduler::Dispatch()
+void TaskScheduler::Dispatch(success_t const& callback)
{
+ // If the validation failed abort the dispatching here.
+ if (!_predicate())
+ return;
+
// Process all asyncs
while (!_asyncHolder.empty())
{
_asyncHolder.front()();
_asyncHolder.pop();
+
+ // If the validation failed abort the dispatching here.
+ if (!_predicate())
+ return;
}
+ if (_task_holder.IsEmpty())
+ return;
+
while (!_task_holder.IsEmpty())
{
if (_task_holder.First()->_end > _now)
@@ -86,7 +103,14 @@ void TaskScheduler::Dispatch()
// Invoke the context
context.Invoke();
+
+ // If the validation failed abort the dispatching here.
+ if (!_predicate())
+ return;
}
+
+ // On finish call the final callback
+ callback();
}
void TaskScheduler::TaskQueue::Push(TaskContainer&& task)
diff --git a/src/server/shared/Utilities/TaskScheduler.h b/src/server/shared/Utilities/TaskScheduler.h
index 98e210e55b1..d45835b5f17 100644
--- a/src/server/shared/Utilities/TaskScheduler.h
+++ b/src/server/shared/Utilities/TaskScheduler.h
@@ -60,6 +60,10 @@ class TaskScheduler
typedef uint32 repeated_t;
// Task handle type
typedef std::function<void(TaskContext)> task_handler_t;
+ // Predicate type
+ typedef std::function<bool()> predicate_t;
+ // Success handle type
+ typedef std::function<void()> success_t;
class Task
{
@@ -163,27 +167,57 @@ class TaskScheduler
/// the next update tick.
AsyncHolder _asyncHolder;
+ predicate_t _predicate;
+
+ static bool EmptyValidator()
+ {
+ return true;
+ }
+
+ static void EmptyCallback()
+ {
+ }
+
public:
- TaskScheduler() : self_reference(this, [](TaskScheduler const*) { }),
- _now(clock_t::now()) { }
+ TaskScheduler()
+ : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(EmptyValidator) { }
+
+ template<typename P>
+ TaskScheduler(P&& predicate)
+ : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(std::forward<P>(predicate)) { }
TaskScheduler(TaskScheduler const&) = delete;
TaskScheduler(TaskScheduler&&) = delete;
TaskScheduler& operator= (TaskScheduler const&) = delete;
TaskScheduler& operator= (TaskScheduler&&) = delete;
+ /// Sets a validator which is asked if tasks are allowed to be executed.
+ template<typename P>
+ TaskScheduler& SetValidator(P&& predicate)
+ {
+ _predicate = std::forward<P>(predicate);
+ return *this;
+ }
+
+ /// Clears the validator which is asked if tasks are allowed to be executed.
+ TaskScheduler& ClearValidator();
+
/// Update the scheduler to the current time.
- TaskScheduler& Update();
+ /// Calls the optional callback on successfully finish.
+ TaskScheduler& Update(success_t const& callback = EmptyCallback);
/// Update the scheduler with a difftime in ms.
- TaskScheduler& Update(size_t const milliseconds);
+ /// Calls the optional callback on successfully finish.
+ TaskScheduler& Update(size_t const milliseconds, success_t const& callback = EmptyCallback);
/// Update the scheduler with a difftime.
+ /// Calls the optional callback on successfully finish.
template<class _Rep, class _Period>
- TaskScheduler& Update(std::chrono::duration<_Rep, _Period> const& difftime)
+ TaskScheduler& Update(std::chrono::duration<_Rep, _Period> const& difftime,
+ success_t const& callback = EmptyCallback)
{
_now += difftime;
- Dispatch();
+ Dispatch(callback);
return *this;
}
@@ -370,7 +404,7 @@ private:
}
/// Dispatch remaining tasks
- void Dispatch();
+ void Dispatch(success_t const& callback);
};
class TaskContext