diff options
Diffstat (limited to 'src')
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 |