diff options
author | Yehonal <hw.2@hotmail.it> | 2016-06-26 19:22:35 +0200 |
---|---|---|
committer | Yehonal <hw.2@hotmail.it> | 2016-06-26 19:22:35 +0200 |
commit | 52f305111ce2bb876eaea985d75ffcb744f583a5 (patch) | |
tree | 6607d6eb3af13384f3cf5e6aafd362f244da49ea /src | |
parent | 5bb70a86beab67958520b170c5008b82d0e36314 (diff) |
importing changes from callmephil repo
a special thanks to him
Diffstat (limited to 'src')
43 files changed, 4115 insertions, 3183 deletions
diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 0ee49bef76..9a952c7fdf 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) - * Copyright (C) + * Copyright (C) + * Copyright (C) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -27,125 +27,276 @@ namespace AccountMgr { -uint32 GetId(std::string const& username) -{ - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); - stmt->setString(0, username); - PreparedQueryResult result = LoginDatabase.Query(stmt); + AccountOpResult CreateAccount(std::string username, std::string password) + { + if (utf8length(username) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; // username's too long - return (result) ? (*result)[0].GetUInt32() : 0; -} + normalizeString(username); + normalizeString(password); -uint32 GetSecurity(uint32 accountId) -{ - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); - stmt->setUInt32(0, accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); + if (GetId(username)) + return AOR_NAME_ALREDY_EXIST; // username does already exist - return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER); -} + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT); -uint32 GetSecurity(uint32 accountId, int32 realmId) -{ - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); - stmt->setUInt32(0, accountId); - stmt->setInt32(1, realmId); - PreparedQueryResult result = LoginDatabase.Query(stmt); + stmt->setString(0, username); + stmt->setString(1, CalculateShaPassHash(username, password)); - return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER); -} + LoginDatabase.Execute(stmt); -bool GetName(uint32 accountId, std::string& name) -{ - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); - stmt->setUInt32(0, accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS_INIT); - if (result) - { - name = (*result)[0].GetString(); - return true; - } + LoginDatabase.Execute(stmt); - return false; -} + return AOR_OK; // everything's fine + } -bool CheckPassword(uint32 accountId, std::string password) -{ - std::string username; + AccountOpResult DeleteAccount(uint32 accountId) + { + // Check if accounts exists + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); + stmt->setUInt32(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(stmt); - if (!GetName(accountId, username)) - return false; + if (!result) + return AOR_NAME_NOT_EXIST; - normalizeString(username); - normalizeString(password); + // Obtain accounts characters + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); - stmt->setUInt32(0, accountId); - stmt->setString(1, CalculateShaPassHash(username, password)); - PreparedQueryResult result = LoginDatabase.Query(stmt); + stmt->setUInt32(0, accountId); - return (result) ? true : false; -} + result = CharacterDatabase.Query(stmt); -uint32 GetCharactersCount(uint32 accountId) -{ - // check character count - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); - stmt->setUInt32(0, accountId); - PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (result) + { + do + { + uint32 guidLow = (*result)[0].GetUInt32(); + uint64 guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER); - return (result) ? (*result)[0].GetUInt64() : 0; -} + // Kick if player is online + if (Player* p = ObjectAccessor::FindPlayer(guid)) + { + WorldSession* s = p->GetSession(); + s->KickPlayer(); // mark session to remove at next session list update + s->LogoutPlayer(false); // logout player without waiting next session list update + } -bool normalizeString(std::string& utf8String) -{ - wchar_t buffer[MAX_ACCOUNT_STR+1]; + Player::DeleteFromDB(guid, accountId, false, true); // no need to update realm characters + } while (result->NextRow()); + } + + // table realm specific but common for all characters of account for realm + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_TUTORIALS); + stmt->setUInt32(0, accountId); + CharacterDatabase.Execute(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_DATA); + stmt->setUInt32(0, accountId); + CharacterDatabase.Execute(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_BAN); + stmt->setUInt32(0, accountId); + CharacterDatabase.Execute(stmt); + + SQLTransaction trans = LoginDatabase.BeginTransaction(); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT); + stmt->setUInt32(0, accountId); + trans->Append(stmt); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); + stmt->setUInt32(0, accountId); + trans->Append(stmt); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS); + stmt->setUInt32(0, accountId); + trans->Append(stmt); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED); + stmt->setUInt32(0, accountId); + trans->Append(stmt); + + LoginDatabase.CommitTransaction(trans); + + return AOR_OK; + } + + + AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword) + { + // Check if accounts exists + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); + stmt->setUInt32(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (!result) + return AOR_NAME_NOT_EXIST; + + if (utf8length(newUsername) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; + + if (utf8length(newPassword) > MAX_ACCOUNT_STR) + return AOR_PASS_TOO_LONG; + + normalizeString(newUsername); + normalizeString(newPassword); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_USERNAME); + + stmt->setString(0, newUsername); + stmt->setString(1, CalculateShaPassHash(newUsername, newPassword)); + stmt->setUInt32(2, accountId); + + LoginDatabase.Execute(stmt); + + return AOR_OK; + } + + AccountOpResult ChangePassword(uint32 accountId, std::string newPassword) + { + std::string username; + + if (!GetName(accountId, username)) + return AOR_NAME_NOT_EXIST; // account doesn't exist - size_t maxLength = MAX_ACCOUNT_STR; - if (!Utf8toWStr(utf8String, buffer, maxLength)) - return false; + if (utf8length(newPassword) > MAX_ACCOUNT_STR) + return AOR_PASS_TOO_LONG; + + normalizeString(username); + normalizeString(newPassword); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_PASSWORD); + + stmt->setString(0, CalculateShaPassHash(username, newPassword)); + stmt->setUInt32(1, accountId); + + LoginDatabase.Execute(stmt); + + return AOR_OK; + } + + uint32 GetId(std::string const& username) + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); + stmt->setString(0, username); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + return (result) ? (*result)[0].GetUInt32() : 0; + } + + uint32 GetSecurity(uint32 accountId) + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); + stmt->setUInt32(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER); + } + + uint32 GetSecurity(uint32 accountId, int32 realmId) + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); + stmt->setUInt32(0, accountId); + stmt->setInt32(1, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER); + } + + bool GetName(uint32 accountId, std::string& name) + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); + stmt->setUInt32(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (result) + { + name = (*result)[0].GetString(); + return true; + } + + return false; + } + + bool CheckPassword(uint32 accountId, std::string password) + { + std::string username; + + if (!GetName(accountId, username)) + return false; + + normalizeString(username); + normalizeString(password); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); + stmt->setUInt32(0, accountId); + stmt->setString(1, CalculateShaPassHash(username, password)); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + return (result) ? true : false; + } + + uint32 GetCharactersCount(uint32 accountId) + { + // check character count + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); + stmt->setUInt32(0, accountId); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + + return (result) ? (*result)[0].GetUInt64() : 0; + } + + bool normalizeString(std::string& utf8String) + { + wchar_t buffer[MAX_ACCOUNT_STR + 1]; + + size_t maxLength = MAX_ACCOUNT_STR; + if (!Utf8toWStr(utf8String, buffer, maxLength)) + return false; #ifdef _MSC_VER #pragma warning(disable: 4996) #endif - std::transform(&buffer[0], buffer+maxLength, &buffer[0], wcharToUpperOnlyLatin); + std::transform(&buffer[0], buffer + maxLength, &buffer[0], wcharToUpperOnlyLatin); #ifdef _MSC_VER #pragma warning(default: 4996) #endif - return WStrToUtf8(buffer, maxLength, utf8String); -} + return WStrToUtf8(buffer, maxLength, utf8String); + } -std::string CalculateShaPassHash(std::string const& name, std::string const& password) -{ - SHA1Hash sha; - sha.Initialize(); - sha.UpdateData(name); - sha.UpdateData(":"); - sha.UpdateData(password); - sha.Finalize(); + std::string CalculateShaPassHash(std::string const& name, std::string const& password) + { + SHA1Hash sha; + sha.Initialize(); + sha.UpdateData(name); + sha.UpdateData(":"); + sha.UpdateData(password); + sha.Finalize(); - return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength()); -} + return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength()); + } -bool IsPlayerAccount(uint32 gmlevel) -{ - return gmlevel == SEC_PLAYER; -} + bool IsPlayerAccount(uint32 gmlevel) + { + return gmlevel == SEC_PLAYER; + } -bool IsGMAccount(uint32 gmlevel) -{ - return gmlevel >= SEC_GAMEMASTER && gmlevel <= SEC_CONSOLE; -} + bool IsGMAccount(uint32 gmlevel) + { + return gmlevel >= SEC_GAMEMASTER && gmlevel <= SEC_CONSOLE; + } -bool IsAdminAccount(uint32 gmlevel) -{ - return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE; -} + bool IsAdminAccount(uint32 gmlevel) + { + return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE; + } -bool IsConsoleAccount(uint32 gmlevel) -{ - return gmlevel == SEC_CONSOLE; -} + bool IsConsoleAccount(uint32 gmlevel) + { + return gmlevel == SEC_CONSOLE; + } } // Namespace AccountMgr diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h index e4f070121f..0a17e7da01 100644 --- a/src/server/game/Accounts/AccountMgr.h +++ b/src/server/game/Accounts/AccountMgr.h @@ -36,6 +36,10 @@ enum AccountOpResult namespace AccountMgr { + AccountOpResult CreateAccount(std::string username, std::string password); + AccountOpResult DeleteAccount(uint32 accountId); + AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword); + AccountOpResult ChangePassword(uint32 accountId, std::string newPassword); bool CheckPassword(uint32 accountId, std::string password); uint32 GetId(std::string const& username); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index ecd4df7363..52e37d23d2 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -51,6 +51,9 @@ AuctionHouseMgr::~AuctionHouseMgr() AuctionHouseObject* AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId) { + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + return &mNeutralAuctions; + // team have linked auction houses FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); if (!u_entry) @@ -372,7 +375,7 @@ AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTem { uint32 houseid = 7; // goblin auction house - //if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) { //FIXME: found way for proper auctionhouse selection by another way // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races. diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp index a2c31b2fee..8c801afef6 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -594,7 +594,8 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo if (!victim || !IsOn(victim) || // allow make moderator from another team only if both is GMs // at this moment this only way to show channel post for GM from another team - ((!AccountMgr::IsGMAccount(sec) || !AccountMgr::IsGMAccount(newp->GetSession()->GetSecurity())) && player->GetTeamId() != newp->GetTeamId())) + ((!AccountMgr::IsGMAccount(sec) || !AccountMgr::IsGMAccount(newp->GetSession()->GetSecurity())) && player->GetTeamId() != newp->GetTeamId() && + !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))) { WorldPacket data; MakePlayerNotFound(&data, p2n); @@ -659,7 +660,8 @@ void Channel::SetOwner(Player const* player, std::string const& newname) Player* newp = ObjectAccessor::FindPlayerByName(newname, false); uint64 victim = newp ? newp->GetGUID() : 0; - if (!victim || !IsOn(victim) || newp->GetTeamId() != player->GetTeamId()) + if (!victim || !IsOn(victim) || newp->GetTeamId() != player->GetTeamId() && + !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) { WorldPacket data; MakePlayerNotFound(&data, newname); @@ -764,6 +766,9 @@ void Channel::Say(uint64 guid, std::string const& what, uint32 lang) if (what.empty()) return; + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + lang = LANG_UNIVERSAL; + if (!IsOn(guid)) { WorldPacket data; diff --git a/src/server/game/Chat/Channels/ChannelMgr.cpp b/src/server/game/Chat/Channels/ChannelMgr.cpp index 5716d3a2d6..2826aa298a 100644 --- a/src/server/game/Chat/Channels/ChannelMgr.cpp +++ b/src/server/game/Chat/Channels/ChannelMgr.cpp @@ -1,201 +1,205 @@ -/*
- * Copyright (C)
- * Copyright (C)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "ChannelMgr.h"
-#include "Player.h"
-#include "World.h"
-
-ChannelMgr::~ChannelMgr()
-{
- for (ChannelMap::iterator itr = channels.begin(); itr != channels.end(); ++itr)
- delete itr->second;
-
- channels.clear();
-}
-
-ChannelMgr* ChannelMgr::forTeam(TeamId teamId)
-{
- if (teamId == TEAM_ALLIANCE)
- return ACE_Singleton<AllianceChannelMgr, ACE_Null_Mutex>::instance();
-
- if (teamId == TEAM_HORDE)
- return ACE_Singleton<HordeChannelMgr, ACE_Null_Mutex>::instance();
-
- return NULL;
-}
-
-
-void ChannelMgr::LoadChannels()
-{
- uint32 oldMSTime = getMSTime();
- uint32 count = 0;
-
- QueryResult result = CharacterDatabase.PQuery("SELECT channelId, name, team, announce, password FROM channels WHERE team = %u ORDER BY channelId ASC", _teamId);
- if (!result)
- {
- sLog->outString(">> Loaded 0 channels for %s", _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde");
- sLog->outString();
- return;
- }
-
- do
- {
- Field* fields = result->Fetch();
- if (!fields)
- break;
-
- uint32 channelDBId = fields[0].GetUInt32();
- std::string channelName = fields[1].GetString();
- std::string password = fields[4].GetString();
- std::wstring channelWName;
- Utf8toWStr(channelName, channelWName);
-
- Channel* newChannel = new Channel(channelName, 0, channelDBId, TeamId(fields[2].GetUInt32()), fields[3].GetUInt8());
- newChannel->SetPassword(password);
- channels[channelWName] = newChannel;
-
- if (QueryResult banResult = CharacterDatabase.PQuery("SELECT playerGUID, banTime FROM channels_bans WHERE channelId = %u", channelDBId))
- {
- do
- {
- Field* banFields = banResult->Fetch();
- if (!banFields)
- break;
- newChannel->AddBan(banFields[0].GetUInt32(), banFields[1].GetUInt32());
- }
- while (banResult->NextRow());
- }
-
- if (channelDBId > ChannelMgr::_channelIdMax)
ChannelMgr::_channelIdMax = channelDBId;
- ++count;
- }
- while (result->NextRow());
-
- sLog->outString(">> Loaded %u channels for %s in %ums", count, _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde", GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
-}
-
-Channel* ChannelMgr::GetJoinChannel(std::string const& name, uint32 channelId)
-{
- std::wstring wname;
- Utf8toWStr(name, wname);
- wstrToLower(wname);
-
- ChannelMap::const_iterator i = channels.find(wname);
-
- if (i == channels.end())
- {
- std::string chNameLower = name;
- std::transform(chNameLower.begin(), chNameLower.end(), chNameLower.begin(), ::tolower);
- Channel* nchan = new Channel(chNameLower, channelId, 0, _teamId);
- channels[wname] = nchan;
- return nchan;
- }
-
- return i->second;
-}
-
-Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pkt)
-{
- std::wstring wname;
- Utf8toWStr(name, wname);
- wstrToLower(wname);
-
- ChannelMap::const_iterator i = channels.find(wname);
-
- if (i == channels.end())
- {
- if (pkt)
- {
- WorldPacket data;
- MakeNotOnPacket(&data, name);
- player->GetSession()->SendPacket(&data);
- }
-
- return NULL;
- }
-
- return i->second;
-}
-
-
-uint32 ChannelMgr::_channelIdMax = 0;
-ChannelMgr::ChannelRightsMap ChannelMgr::channels_rights;
-ChannelRights ChannelMgr::channelRightsEmpty;
-
-void ChannelMgr::LoadChannelRights()
-{
- uint32 oldMSTime = getMSTime();
- channels_rights.clear();
-
- QueryResult result = CharacterDatabase.Query("SELECT name, flags, speakdelay, joinmessage, delaymessage, moderators FROM channels_rights");
- if (!result)
- {
- sLog->outString();
- sLog->outString(">> Loaded 0 Channel Rights!");
- return;
- }
-
- uint32 count = 0;
- do
- {
- Field* fields = result->Fetch();
- std::set<uint32> moderators;
- const char* moderatorList = fields[5].GetCString();
- if (moderatorList)
- {
- Tokenizer tokens(moderatorList, ' ');
- for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i)
- {
- uint64 moderator_acc = atol(*i);
- if (moderator_acc && ((uint32)moderator_acc) == moderator_acc)
- moderators.insert((uint32)moderator_acc);
- }
- }
-
- SetChannelRightsFor(fields[0].GetString(), fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetString(), fields[4].GetString(), moderators);
-
- ++count;
- } while (result->NextRow());
-
- sLog->outString(">> Loaded %d Channel Rights in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
-}
-
-const ChannelRights& ChannelMgr::GetChannelRightsFor(const std::string& name)
-{
- std::string nameStr = name;
- std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower);
- ChannelRightsMap::const_iterator itr = channels_rights.find(nameStr);
- if (itr != channels_rights.end())
- return itr->second;
- return channelRightsEmpty;
-}
-
-void ChannelMgr::SetChannelRightsFor(const std::string& name, const uint32& flags, const uint32& speakDelay, const std::string& joinmessage, const std::string& speakmessage, const std::set<uint32>& moderators)
-{
- std::string nameStr = name;
- std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower);
- channels_rights[nameStr] = ChannelRights(flags, speakDelay, joinmessage, speakmessage, moderators);
-}
-
-void ChannelMgr::MakeNotOnPacket(WorldPacket* data, std::string const& name)
-{
- data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + name.size());
- (*data) << uint8(5) << name;
-}
+/* + * Copyright (C) + * Copyright (C) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ChannelMgr.h" +#include "Player.h" +#include "World.h" + +ChannelMgr::~ChannelMgr() +{ + for (ChannelMap::iterator itr = channels.begin(); itr != channels.end(); ++itr) + delete itr->second; + + channels.clear(); +} + +ChannelMgr* ChannelMgr::forTeam(TeamId teamId) +{ + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + return ACE_Singleton<AllianceChannelMgr, ACE_Null_Mutex>::instance(); // cross-faction + + if (teamId == TEAM_ALLIANCE) + return ACE_Singleton<AllianceChannelMgr, ACE_Null_Mutex>::instance(); + + if (teamId == TEAM_HORDE) + return ACE_Singleton<HordeChannelMgr, ACE_Null_Mutex>::instance(); + + return NULL; +} + + +void ChannelMgr::LoadChannels() +{ + uint32 oldMSTime = getMSTime(); + uint32 count = 0; + + QueryResult result = CharacterDatabase.PQuery("SELECT channelId, name, team, announce, password FROM channels WHERE team = %u ORDER BY channelId ASC", _teamId); + if (!result) + { + sLog->outString(">> Loaded 0 channels for %s", _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde"); + sLog->outString(); + return; + } + + do + { + Field* fields = result->Fetch(); + if (!fields) + break; + + uint32 channelDBId = fields[0].GetUInt32(); + std::string channelName = fields[1].GetString(); + std::string password = fields[4].GetString(); + std::wstring channelWName; + Utf8toWStr(channelName, channelWName); + + Channel* newChannel = new Channel(channelName, 0, channelDBId, TeamId(fields[2].GetUInt32()), fields[3].GetUInt8()); + newChannel->SetPassword(password); + channels[channelWName] = newChannel; + + if (QueryResult banResult = CharacterDatabase.PQuery("SELECT playerGUID, banTime FROM channels_bans WHERE channelId = %u", channelDBId)) + { + do + { + Field* banFields = banResult->Fetch(); + if (!banFields) + break; + newChannel->AddBan(banFields[0].GetUInt32(), banFields[1].GetUInt32()); + } + while (banResult->NextRow()); + } + + if (channelDBId > ChannelMgr::_channelIdMax) + ChannelMgr::_channelIdMax = channelDBId; + ++count; + } + while (result->NextRow()); + + sLog->outString(">> Loaded %u channels for %s in %ums", count, _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde", GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +Channel* ChannelMgr::GetJoinChannel(std::string const& name, uint32 channelId) +{ + std::wstring wname; + Utf8toWStr(name, wname); + wstrToLower(wname); + + ChannelMap::const_iterator i = channels.find(wname); + + if (i == channels.end()) + { + std::string chNameLower = name; + std::transform(chNameLower.begin(), chNameLower.end(), chNameLower.begin(), ::tolower); + Channel* nchan = new Channel(chNameLower, channelId, 0, _teamId); + channels[wname] = nchan; + return nchan; + } + + return i->second; +} + +Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pkt) +{ + std::wstring wname; + Utf8toWStr(name, wname); + wstrToLower(wname); + + ChannelMap::const_iterator i = channels.find(wname); + + if (i == channels.end()) + { + if (pkt) + { + WorldPacket data; + MakeNotOnPacket(&data, name); + player->GetSession()->SendPacket(&data); + } + + return NULL; + } + + return i->second; +} + + +uint32 ChannelMgr::_channelIdMax = 0; +ChannelMgr::ChannelRightsMap ChannelMgr::channels_rights; +ChannelRights ChannelMgr::channelRightsEmpty; + +void ChannelMgr::LoadChannelRights() +{ + uint32 oldMSTime = getMSTime(); + channels_rights.clear(); + + QueryResult result = CharacterDatabase.Query("SELECT name, flags, speakdelay, joinmessage, delaymessage, moderators FROM channels_rights"); + if (!result) + { + sLog->outString(); + sLog->outString(">> Loaded 0 Channel Rights!"); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + std::set<uint32> moderators; + const char* moderatorList = fields[5].GetCString(); + if (moderatorList) + { + Tokenizer tokens(moderatorList, ' '); + for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i) + { + uint64 moderator_acc = atol(*i); + if (moderator_acc && ((uint32)moderator_acc) == moderator_acc) + moderators.insert((uint32)moderator_acc); + } + } + + SetChannelRightsFor(fields[0].GetString(), fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetString(), fields[4].GetString(), moderators); + + ++count; + } while (result->NextRow()); + + sLog->outString(">> Loaded %d Channel Rights in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +const ChannelRights& ChannelMgr::GetChannelRightsFor(const std::string& name) +{ + std::string nameStr = name; + std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower); + ChannelRightsMap::const_iterator itr = channels_rights.find(nameStr); + if (itr != channels_rights.end()) + return itr->second; + return channelRightsEmpty; +} + +void ChannelMgr::SetChannelRightsFor(const std::string& name, const uint32& flags, const uint32& speakDelay, const std::string& joinmessage, const std::string& speakmessage, const std::set<uint32>& moderators) +{ + std::string nameStr = name; + std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower); + channels_rights[nameStr] = ChannelRights(flags, speakDelay, joinmessage, speakmessage, moderators); +} + +void ChannelMgr::MakeNotOnPacket(WorldPacket* data, std::string const& name) +{ + data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + name.size()); + (*data) << uint8(5) << name; +} diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 2ecf497505..c72eac5699 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -1,2595 +1,2599 @@ -/*
- * Copyright (C)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "Common.h"
-#include "SharedDefines.h"
-#include "DBCStores.h"
-#include "DisableMgr.h"
-#include "ObjectMgr.h"
-#include "SocialMgr.h"
-#include "Language.h"
-#include "LFGMgr.h"
-#include "LFGScripts.h"
-#include "LFGGroupData.h"
-#include "LFGPlayerData.h"
-#include "LFGQueue.h"
-#include "Group.h"
-#include "SpellAuras.h"
-#include "Player.h"
-#include "GroupMgr.h"
-#include "GameEventMgr.h"
-#include "WorldSession.h"
-
-namespace lfg
-{
-
-LFGMgr::LFGMgr(): m_lfgProposalId(1), m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK))
-{
- new LFGPlayerScript();
- new LFGGroupScript();
-
- for (uint8 team=0; team<2; ++team)
- {
- m_raidBrowserUpdateTimer[team] = 10000;
- m_raidBrowserLastUpdatedDungeonId[team] = 0;
- }
-}
-
-LFGMgr::~LFGMgr()
-{
- for (LfgRewardContainer::iterator itr = RewardMapStore.begin(); itr != RewardMapStore.end(); ++itr)
- delete itr->second;
-}
-
-void LFGMgr::_LoadFromDB(Field* fields, uint64 guid)
-{
- if (!fields)
- return;
-
- if (!IS_GROUP_GUID(guid))
- return;
-
- SetLeader(guid, MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER));
-
- uint32 dungeon = fields[17].GetUInt32();
- uint8 state = fields[18].GetUInt8();
-
- if (!dungeon || !state)
- return;
-
- SetDungeon(guid, dungeon);
-
- switch (state)
- {
- case LFG_STATE_DUNGEON:
- case LFG_STATE_FINISHED_DUNGEON:
- SetState(guid, (LfgState)state);
- break;
- default:
- break;
- }
-}
-
-void LFGMgr::_SaveToDB(uint64 guid)
-{
- if (!IS_GROUP_GUID(guid))
- return;
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_LFG_DATA);
- stmt->setUInt32(0, GUID_LOPART(guid));
- stmt->setUInt32(1, GetDungeon(guid));
- stmt->setUInt32(2, GetState(guid));
- CharacterDatabase.Execute(stmt);
-}
-
-/// Load rewards for completing dungeons
-void LFGMgr::LoadRewards()
-{
- uint32 oldMSTime = getMSTime();
-
- for (LfgRewardContainer::iterator itr = RewardMapStore.begin(); itr != RewardMapStore.end(); ++itr)
- delete itr->second;
- RewardMapStore.clear();
-
- // ORDER BY is very important for GetRandomDungeonReward!
- QueryResult result = WorldDatabase.Query("SELECT dungeonId, maxLevel, firstQuestId, otherQuestId FROM lfg_dungeon_rewards ORDER BY dungeonId, maxLevel ASC");
-
- if (!result)
- {
- sLog->outError(">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!");
- return;
- }
-
- uint32 count = 0;
-
- Field* fields = NULL;
- do
- {
- fields = result->Fetch();
- uint32 dungeonId = fields[0].GetUInt32();
- uint32 maxLevel = fields[1].GetUInt8();
- uint32 firstQuestId = fields[2].GetUInt32();
- uint32 otherQuestId = fields[3].GetUInt32();
-
- if (!GetLFGDungeonEntry(dungeonId))
- {
- sLog->outError("Dungeon %u specified in table `lfg_dungeon_rewards` does not exist!", dungeonId);
- continue;
- }
-
- if (!maxLevel || maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
- {
- sLog->outError("Level %u specified for dungeon %u in table `lfg_dungeon_rewards` can never be reached!", maxLevel, dungeonId);
- maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
- }
-
- if (!firstQuestId || !sObjectMgr->GetQuestTemplate(firstQuestId))
- {
- sLog->outError("First quest %u specified for dungeon %u in table `lfg_dungeon_rewards` does not exist!", firstQuestId, dungeonId);
- continue;
- }
-
- if (otherQuestId && !sObjectMgr->GetQuestTemplate(otherQuestId))
- {
- sLog->outError("Other quest %u specified for dungeon %u in table `lfg_dungeon_rewards` does not exist!", otherQuestId, dungeonId);
- otherQuestId = 0;
- }
-
- RewardMapStore.insert(LfgRewardContainer::value_type(dungeonId, new LfgReward(maxLevel, firstQuestId, otherQuestId)));
- ++count;
- }
- while (result->NextRow());
-
- sLog->outString(">> Loaded %u lfg dungeon rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
-}
-
-LFGDungeonData const* LFGMgr::GetLFGDungeon(uint32 id)
-{
- LFGDungeonContainer::const_iterator itr = LfgDungeonStore.find(id);
- if (itr != LfgDungeonStore.end())
- return &(itr->second);
-
- return NULL;
-}
-
-void LFGMgr::LoadLFGDungeons(bool reload /* = false */)
-{
- uint32 oldMSTime = getMSTime();
-
- LfgDungeonStore.clear();
-
- // Initialize Dungeon map with data from dbcs
- for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i)
- {
- LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i);
- if (!dungeon)
- continue;
-
- switch (dungeon->type)
- {
- case LFG_TYPE_DUNGEON:
- case LFG_TYPE_HEROIC:
- case LFG_TYPE_RAID:
- case LFG_TYPE_RANDOM:
- LfgDungeonStore[dungeon->ID] = LFGDungeonData(dungeon);
- break;
- }
- }
-
- // Fill teleport locations from DB
- QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_entrances");
-
- if (!result)
- {
- sLog->outError(">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!");
- return;
- }
-
- uint32 count = 0;
-
- do
- {
- Field* fields = result->Fetch();
- uint32 dungeonId = fields[0].GetUInt32();
- LFGDungeonContainer::iterator dungeonItr = LfgDungeonStore.find(dungeonId);
- if (dungeonItr == LfgDungeonStore.end())
- {
- sLog->outError("table `lfg_entrances` contains coordinates for wrong dungeon %u", dungeonId);
- continue;
- }
-
- LFGDungeonData& data = dungeonItr->second;
- data.x = fields[1].GetFloat();
- data.y = fields[2].GetFloat();
- data.z = fields[3].GetFloat();
- data.o = fields[4].GetFloat();
-
- ++count;
- }
- while (result->NextRow());
-
- sLog->outString(">> Loaded %u lfg entrance positions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
-
- // Fill all other teleport coords from areatriggers
- for (LFGDungeonContainer::iterator itr = LfgDungeonStore.begin(); itr != LfgDungeonStore.end(); ++itr)
- {
- LFGDungeonData& dungeon = itr->second;
-
- // No teleport coords in database, load from areatriggers
- if (dungeon.type != LFG_TYPE_RANDOM && dungeon.x == 0.0f && dungeon.y == 0.0f && dungeon.z == 0.0f)
- {
- AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon.map);
- if (!at)
- {
- sLog->outError("LFGMgr::LoadLFGDungeons: Failed to load dungeon %s, cant find areatrigger for map %u", dungeon.name.c_str(), dungeon.map);
- continue;
- }
-
- dungeon.map = at->target_mapId;
- dungeon.x = at->target_X;
- dungeon.y = at->target_Y;
- dungeon.z = at->target_Z;
- dungeon.o = at->target_Orientation;
- }
-
- if (dungeon.type != LFG_TYPE_RANDOM)
- CachedDungeonMapStore[dungeon.group].insert(dungeon.id);
- CachedDungeonMapStore[0].insert(dungeon.id);
- }
-
- if (reload)
- {
- CachedDungeonMapStore.clear();
- // Recalculate locked dungeons
- for (LfgPlayerDataContainer::const_iterator it = PlayersStore.begin(); it != PlayersStore.end(); ++it)
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(it->first))
- InitializeLockedDungeons(player);
- }
-}
-
-void LFGMgr::Update(uint32 tdiff, uint8 task)
-{
- if (!isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
- return;
-
- if (task == 0)
- {
- time_t currTime = time(NULL);
-
- // Remove obsolete role checks
- for (LfgRoleCheckContainer::iterator it = RoleChecksStore.begin(); it != RoleChecksStore.end();)
- {
- LfgRoleCheckContainer::iterator itRoleCheck = it++;
- LfgRoleCheck& roleCheck = itRoleCheck->second;
- if (currTime < roleCheck.cancelTime)
- continue;
- roleCheck.state = LFG_ROLECHECK_MISSING_ROLE;
-
- for (LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); itRoles != roleCheck.roles.end(); ++itRoles)
- {
- uint64 guid = itRoles->first;
- RestoreState(guid, "Remove Obsolete RoleCheck");
- SendLfgRoleCheckUpdate(guid, roleCheck);
- if (guid == roleCheck.leader)
- SendLfgJoinResult(guid, LfgJoinResultData(LFG_JOIN_FAILED, LFG_ROLECHECK_MISSING_ROLE));
- }
-
- RestoreState(itRoleCheck->first, "Remove Obsolete RoleCheck");
- RoleChecksStore.erase(itRoleCheck);
- }
-
- // Remove obsolete proposals
- for (LfgProposalContainer::iterator it = ProposalsStore.begin(); it != ProposalsStore.end();)
- {
- LfgProposalContainer::iterator itRemove = it++;
- if (itRemove->second.cancelTime < currTime)
- RemoveProposal(itRemove, LFG_UPDATETYPE_PROPOSAL_FAILED);
- }
-
- // Remove obsolete kicks
- for (LfgPlayerBootContainer::iterator it = BootsStore.begin(); it != BootsStore.end();)
- {
- LfgPlayerBootContainer::iterator itBoot = it++;
- LfgPlayerBoot& boot = itBoot->second;
- if (boot.cancelTime < currTime)
- {
- boot.inProgress = false;
- for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes)
- {
- uint64 pguid = itVotes->first;
- if (pguid != boot.victim)
- SendLfgBootProposalUpdate(pguid, boot);
- SetState(pguid, LFG_STATE_DUNGEON);
- }
- SetState(itBoot->first, LFG_STATE_DUNGEON);
- BootsStore.erase(itBoot);
- }
- }
- }
- else if (task == 1)
- {
- this->lastProposalId = m_lfgProposalId; // pussywizard: task 2 is done independantly, store previous value in LFGMgr for future use
- uint8 newGroupsProcessed = 0;
- // Check if a proposal can be formed with the new groups being added
- for (LfgQueueContainer::iterator it = QueuesStore.begin(); it != QueuesStore.end(); ++it)
- {
- newGroupsProcessed += it->second.FindGroups();
- if (newGroupsProcessed)
- break;
- }
-
- // Update all players status queue info
- if (!newGroupsProcessed) // don't do this on updates that precessed groups (performance)
- for (LfgQueueContainer::iterator it = QueuesStore.begin(); it != QueuesStore.end(); ++it)
- it->second.UpdateQueueTimers(tdiff);
- }
- else if (task == 2)
- {
- if (lastProposalId != m_lfgProposalId)
- {
- // pussywizard: only one proposal can be created in World::Update (during maps update), and it has id == m_lfgProposalId, so try to find only that one, dunno why for loop here xD
- for (LfgProposalContainer::const_iterator itProposal = ProposalsStore.find(m_lfgProposalId); itProposal != ProposalsStore.end(); ++itProposal)
- {
- uint32 proposalId = itProposal->first;
- LfgProposal& proposal = ProposalsStore[proposalId];
-
- uint64 guid = 0;
- for (LfgProposalPlayerContainer::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers)
- {
- guid = itPlayers->first;
- SetState(guid, LFG_STATE_PROPOSAL);
- if (uint64 gguid = GetGroup(guid))
- {
- SetState(gguid, LFG_STATE_PROPOSAL);
- SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
- }
- else
- SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
- SendLfgUpdateProposal(guid, proposal);
- }
-
- if (proposal.state == LFG_PROPOSAL_SUCCESS) // pussywizard: no idea what's the purpose of this xD
- UpdateProposal(proposalId, guid, true);
- }
- }
-
- UpdateRaidBrowser(tdiff);
- }
-}
-
-/**
- Generate the dungeon lock map for a given player
-
- @param[in] player Player we need to initialize the lock status map
-*/
-void LFGMgr::InitializeLockedDungeons(Player* player, uint8 level /* = 0 */)
-{
- uint64 guid = player->GetGUID();
- if (!level)
- level = player->getLevel();
- uint8 expansion = player->GetSession()->Expansion();
- LfgDungeonSet const& dungeons = GetDungeonsByRandom(0);
- LfgLockMap lock;
-
- float avgItemLevel = player->GetAverageItemLevelForDF();
-
- for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end(); ++it)
- {
- LFGDungeonData const* dungeon = GetLFGDungeon(*it);
- if (!dungeon) // should never happen - We provide a list from sLFGDungeonStore
- continue;
- MapEntry const* mapEntry = sMapStore.LookupEntry(dungeon->map);
-
- uint32 lockData = 0;
- if (dungeon->expansion > expansion)
- lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION;
- else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player))
- lockData = LFG_LOCKSTATUS_RAID_LOCKED;
- else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && (!mapEntry || !mapEntry->IsRaid()) && sInstanceSaveMgr->PlayerIsPermBoundToInstance(player->GetGUIDLow(), dungeon->map, Difficulty(dungeon->difficulty)))
- lockData = LFG_LOCKSTATUS_RAID_LOCKED;
- else if (dungeon->minlevel > level)
- lockData = LFG_LOCKSTATUS_TOO_LOW_LEVEL;
- else if (dungeon->maxlevel < level)
- lockData = LFG_LOCKSTATUS_TOO_HIGH_LEVEL;
- else if (dungeon->seasonal && !IsSeasonActive(dungeon->id))
- lockData = LFG_LOCKSTATUS_NOT_IN_SEASON;
- else if (AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty)))
- {
- if (ar->achievement && !player->HasAchieved(ar->achievement))
- lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT;
- else if (ar->reqItemLevel && (float)ar->reqItemLevel > avgItemLevel)
- lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE;
- else if (player->GetTeamId() == TEAM_ALLIANCE && ar->quest_A && !player->GetQuestRewardStatus(ar->quest_A))
- lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED;
- else if (player->GetTeamId() == TEAM_HORDE && ar->quest_H && !player->GetQuestRewardStatus(ar->quest_H))
- lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED;
- else
- if (ar->item)
- {
- if (!player->HasItemCount(ar->item) && (!ar->item2 || !player->HasItemCount(ar->item2)))
- lockData = LFG_LOCKSTATUS_MISSING_ITEM;
- }
- else if (ar->item2 && !player->HasItemCount(ar->item2))
- lockData = LFG_LOCKSTATUS_MISSING_ITEM;
- }
-
- /* TODO VoA closed if WG is not under team control (LFG_LOCKSTATUS_RAID_LOCKED)
- lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE;
- lockData = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE;
- lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL;
- lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL;
- */
-
- if (lockData)
- lock[dungeon->Entry()] = lockData;
- }
- SetLockedDungeons(guid, lock);
-}
-
-/**
- Adds the player/group to lfg queue. If player is in a group then it is the leader
- of the group tying to join the group. Join conditions are checked before adding
- to the new queue.
-
- @param[in] player Player trying to join (or leader of group trying to join)
- @param[in] roles Player selected roles
- @param[in] dungeons Dungeons the player/group is applying for
- @param[in] comment Player selected comment
-*/
-void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const std::string& comment)
-{
- if (!player || dungeons.empty())
- return;
-
- Group* grp = player->GetGroup();
- uint64 guid = player->GetGUID();
- uint64 gguid = grp ? grp->GetGUID() : guid;
- LfgJoinResultData joinData;
- LfgGuidSet players;
- uint32 rDungeonId = 0;
- bool isContinue = grp && grp->isLFGGroup() && GetState(gguid) != LFG_STATE_FINISHED_DUNGEON;
-
- if (grp && (grp->isBGGroup() || grp->isBFGroup()))
- return;
-
- // pussywizard: can't join LFG/LFR while using LFR
- if (GetState(player->GetGUID()) == LFG_STATE_RAIDBROWSER)
- {
- LfgDungeonSet tmp;
- SendRaidBrowserJoinedPacket(player, tmp, ""); // the df "eye" can disappear in various case, resend if needed
- return;
- }
-
- // Do not allow to change dungeon in the middle of a current dungeon
- if (isContinue)
- {
- dungeons.clear();
- dungeons.insert(GetDungeon(gguid));
- }
-
- LfgState state = GetState(gguid);
- switch (state)
- {
- case LFG_STATE_ROLECHECK: // if joining again during rolecheck (eg. many players clicked continue inside instance)
- if (IS_GROUP_GUID(gguid))
- UpdateRoleCheck(gguid); // abort role check and remove from RoleChecksStore
- break;
- case LFG_STATE_QUEUED: // joining again while in a queue
- {
- LFGQueue& queue = GetQueue(gguid);
- queue.RemoveFromQueue(gguid);
- }
- break;
- case LFG_STATE_PROPOSAL: // if joining again during proposal
- joinData.result = LFG_JOIN_INTERNAL_ERROR;
- break;
- case LFG_STATE_FINISHED_DUNGEON:
- if (grp && grp->isLFGGroup())
- joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS;
- break;
- default:
- break;
- }
-
- // Check if all dungeons are valid
- bool isRaid = false;
- if (joinData.result == LFG_JOIN_OK)
- {
- bool isDungeon = false;
- for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end() && joinData.result == LFG_JOIN_OK; ++it)
- {
- LfgType type = GetDungeonType(*it);
- switch (type)
- {
- case LFG_TYPE_RANDOM:
- if (dungeons.size() > 1) // Only allow 1 random dungeon
- joinData.result = LFG_JOIN_DUNGEON_INVALID;
- else
- rDungeonId = (*dungeons.begin());
- // No break on purpose (Random can only be dungeon or heroic dungeon)
- case LFG_TYPE_HEROIC:
- case LFG_TYPE_DUNGEON:
- if (isRaid)
- joinData.result = LFG_JOIN_MIXED_RAID_DUNGEON;
- isDungeon = true;
- break;
- case LFG_TYPE_RAID:
- if (isDungeon)
- joinData.result = LFG_JOIN_MIXED_RAID_DUNGEON;
- isRaid = true;
- break;
- default:
- sLog->outError("Wrong dungeon type %u for dungeon %u", type, *it);
- joinData.result = LFG_JOIN_DUNGEON_INVALID;
- break;
- }
- }
- }
-
- if (!isRaid && joinData.result == LFG_JOIN_OK)
- {
- // Check player or group member restrictions
- if (player->InBattleground() || player->InArena() || player->InBattlegroundQueue())
- joinData.result = LFG_JOIN_USING_BG_SYSTEM;
- else if (player->HasAura(LFG_SPELL_DUNGEON_DESERTER))
- joinData.result = LFG_JOIN_DESERTER;
- else if (dungeons.empty())
- joinData.result = LFG_JOIN_NOT_MEET_REQS;
- else if (grp)
- {
- if (grp->GetMembersCount() > MAXGROUPSIZE)
- joinData.result = LFG_JOIN_TOO_MUCH_MEMBERS;
- else
- {
- uint8 memberCount = 0;
- for (GroupReference* itr = grp->GetFirstMember(); itr != NULL && joinData.result == LFG_JOIN_OK; itr = itr->next())
- {
- if (Player* plrg = itr->GetSource())
- {
- if (plrg->HasAura(LFG_SPELL_DUNGEON_DESERTER))
- joinData.result = LFG_JOIN_PARTY_DESERTER;
- else if (plrg->InBattleground() || plrg->InArena() || plrg->InBattlegroundQueue())
- joinData.result = LFG_JOIN_USING_BG_SYSTEM;
- ++memberCount;
- players.insert(plrg->GetGUID());
- }
- }
-
- if (joinData.result == LFG_JOIN_OK && memberCount != grp->GetMembersCount())
- joinData.result = LFG_JOIN_DISCONNECTED;
- }
- }
- else
- players.insert(player->GetGUID());
-
- // Xinef: Check dungeon cooldown only for random dungeons
- // Xinef: Moreover check this only if dungeon is not started, afterwards its obvious that players will have the cooldown
- if (joinData.result == LFG_JOIN_OK && !isContinue && rDungeonId)
- {
- if (player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) // xinef: added !isContinue
- joinData.result = LFG_JOIN_RANDOM_COOLDOWN;
- else if (grp)
- {
- for (GroupReference* itr = grp->GetFirstMember(); itr != NULL && joinData.result == LFG_JOIN_OK; itr = itr->next())
- if (Player* plrg = itr->GetSource())
- if (plrg->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) // xinef: added !isContinue
- joinData.result = LFG_JOIN_PARTY_RANDOM_COOLDOWN;
- }
- }
- }
-
- if (isRaid)
- players.insert(player->GetGUID());
-
- if (joinData.result == LFG_JOIN_OK)
- {
- // Expand random dungeons and check restrictions
- if (rDungeonId)
- dungeons = GetDungeonsByRandom(rDungeonId);
-
- // if we have lockmap then there are no compatible dungeons
- // xinef: dont check compatibile dungeons for already running group (bind problems)
- if (!isContinue)
- {
- GetCompatibleDungeons(dungeons, players, joinData.lockmap);
- if (dungeons.empty())
- joinData.result = grp ? LFG_JOIN_PARTY_NOT_MEET_REQS : LFG_JOIN_NOT_MEET_REQS;
- }
- }
-
- // pussywizard:
- if (isRaid && grp && (grp->isLFGGroup() || guid != grp->GetLeaderGUID()))
- return;
-
- // Can't join. Send result
- if (joinData.result != LFG_JOIN_OK)
- {
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::Join: [" UI64FMTD "] joining with %u members. result: %u", guid, grp ? grp->GetMembersCount() : 1, joinData.result);
- if (!dungeons.empty()) // Only should show lockmap when have no dungeons available
- joinData.lockmap.clear();
- player->GetSession()->SendLfgJoinResult(joinData);
- return;
- }
-
- SetComment(guid, comment);
-
- if (isRaid)
- {
- if (grp)
- roles = PLAYER_ROLE_LEADER;
- else
- roles &= (PLAYER_ROLE_TANK | PLAYER_ROLE_HEALER | PLAYER_ROLE_DAMAGE);
- if (!roles)
- return;
- JoinRaidBrowser(player, roles, dungeons, comment);
- SetState(guid, LFG_STATE_RAIDBROWSER);
- SendRaidBrowserJoinedPacket(player, dungeons, comment);
- return;
- }
-
- std::string debugNames = "";
- if (grp) // Begin rolecheck
- {
- // Create new rolecheck
- LfgRoleCheck& roleCheck = RoleChecksStore[gguid];
- roleCheck.roles.clear(); // pussywizard: NEW rolecheck, not old one with trash data >_>
- roleCheck.cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK;
- roleCheck.state = LFG_ROLECHECK_INITIALITING;
- roleCheck.leader = guid;
- roleCheck.dungeons = dungeons;
- roleCheck.rDungeonId = rDungeonId;
-
- if (rDungeonId)
- {
- dungeons.clear();
- dungeons.insert(rDungeonId);
- }
-
- SetState(gguid, LFG_STATE_ROLECHECK);
- // Send update to player
- LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_JOIN_QUEUE, dungeons, comment);
- for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- if (Player* plrg = itr->GetSource())
- {
- uint64 pguid = plrg->GetGUID();
- plrg->GetSession()->SendLfgUpdateParty(updateData);
- SetState(pguid, LFG_STATE_ROLECHECK);
- if (!isContinue)
- SetSelectedDungeons(pguid, dungeons);
- roleCheck.roles[pguid] = 0;
- if (!debugNames.empty())
- debugNames.append(", ");
- debugNames.append(plrg->GetName());
- }
- }
- // Update leader role
- UpdateRoleCheck(gguid, guid, roles);
- }
- else // Add player to queue
- {
- LfgRolesMap rolesMap;
- rolesMap[guid] = roles;
- LFGQueue& queue = GetQueue(guid);
- queue.AddQueueData(guid, time(NULL), dungeons, rolesMap);
-
- if (!isContinue)
- {
- if (rDungeonId)
- {
- dungeons.clear();
- dungeons.insert(rDungeonId);
- }
- SetSelectedDungeons(guid, dungeons);
- }
- // Send update to player
- player->GetSession()->SendLfgJoinResult(joinData);
- player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_JOIN_QUEUE, dungeons, comment));
- SetState(guid, LFG_STATE_QUEUED);
- SetRoles(guid, roles);
- debugNames.append(player->GetName());
- }
-
- /*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG))
- {
- std::ostringstream o;
- o << "LFGMgr::Join: [" << guid << "] joined (" << (grp ? "group" : "player") << ") Members: " << debugNames.c_str()
- << ". Dungeons (" << uint32(dungeons.size()) << "): " << ConcatenateDungeons(dungeons);
- ;//sLog->outDebug((LOG_FILTER_LFG, "%s", o.str().c_str());
- }*/
-}
-
-/**
- Leaves Dungeon System. Player/Group is removed from queue, rolechecks, proposals
- or votekicks. Player or group needs to be not NULL and using Dungeon System
-
- @param[in] guid Player or group guid
-*/
-void LFGMgr::LeaveLfg(uint64 guid)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::Leave: [" UI64FMTD "]", guid);
-
- uint64 gguid = IS_GROUP_GUID(guid) ? guid : GetGroup(guid);
- LfgState state = GetState(guid);
- switch (state)
- {
- case LFG_STATE_QUEUED:
- if (gguid)
- {
- LFGQueue& queue = GetQueue(gguid);
- queue.RemoveFromQueue(gguid);
- SetState(gguid, LFG_STATE_NONE);
- const LfgGuidSet& players = GetPlayers(gguid);
- for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
- {
- SetState(*it, LFG_STATE_NONE);
- SendLfgUpdateParty(*it, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
- }
- }
- else
- {
- LFGQueue& queue = GetQueue(guid);
- queue.RemoveFromQueue(guid);
- SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
- SetState(guid, LFG_STATE_NONE);
- }
- break;
- case LFG_STATE_ROLECHECK:
- if (gguid)
- UpdateRoleCheck(gguid); // No player to update role = LFG_ROLECHECK_ABORTED
- break;
- case LFG_STATE_PROPOSAL:
- {
- // Remove from Proposals
- LfgProposalContainer::iterator it = ProposalsStore.begin();
- uint64 pguid = gguid == guid ? GetLeader(gguid) : guid;
- while (it != ProposalsStore.end())
- {
- LfgProposalPlayerContainer::iterator itPlayer = it->second.players.find(pguid);
- if (itPlayer != it->second.players.end())
- {
- // Mark the player/leader of group who left as didn't accept the proposal
- itPlayer->second.accept = LFG_ANSWER_DENY;
- break;
- }
- ++it;
- }
-
- // Remove from queue - if proposal is found, RemoveProposal will call RemoveFromQueue
- if (it != ProposalsStore.end())
- RemoveProposal(it, LFG_UPDATETYPE_PROPOSAL_DECLINED);
- break;
- }
- case LFG_STATE_NONE:
- break;
- case LFG_STATE_DUNGEON:
- case LFG_STATE_FINISHED_DUNGEON:
- case LFG_STATE_BOOT:
- if (guid != gguid) // Player
- SetState(guid, LFG_STATE_NONE);
- break;
- case LFG_STATE_RAIDBROWSER:
- LeaveRaidBrowser(guid);
- SetCanOverrideRBState(guid, true);
- SetState(guid, LFG_STATE_NONE);
- SetCanOverrideRBState(guid, false);
- SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_LEAVE_RAIDBROWSER));
- SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_LEAVE_RAIDBROWSER));
- break;
- }
-}
-
-void LFGMgr::JoinRaidBrowser(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string comment)
-{
- // pussywizard: client limit for comment length is 64 @ 3.3.5a
- if (comment.size() > 64)
- comment = comment.substr(0, 64);
-
- RBEntryInfo entry(roles, comment);
- for (LfgDungeonSet::const_iterator itr = dungeons.begin(); itr != dungeons.end(); ++itr)
- if (GetLFGDungeon(*itr)) // ensure dungeon data exists for such dungeon id
- {
- RaidBrowserStore[player->GetTeamId()][*itr][player->GetGUIDLow()] = entry;
- RBUsedDungeonsStore[player->GetTeamId()].insert(*itr);
- }
-}
-
-void LFGMgr::LeaveRaidBrowser(uint64 guid)
-{
- uint32 guidLow = GUID_LOPART(guid);
- for (uint8 team=0; team<2; ++team)
- for (RBStoreMap::iterator itr = RaidBrowserStore[team].begin(); itr != RaidBrowserStore[team].end(); ++itr)
- itr->second.erase(guidLow);
-}
-
-void LFGMgr::SendRaidBrowserJoinedPacket(Player* p, LfgDungeonSet& dungeons, std::string comment)
-{
- if (dungeons.empty())
- {
- RBEntryInfoMap::iterator iter;
- uint8 team = p->GetTeamId();
- bool setComment = true;
- for (RBStoreMap::iterator itr = RaidBrowserStore[team].begin(); itr != RaidBrowserStore[team].end(); ++itr)
- if ((iter = itr->second.find(p->GetGUIDLow())) != itr->second.end())
- {
- dungeons.insert(itr->first);
- if (setComment)
- {
- comment = iter->second.comment;
- setComment = false;
- }
- }
- }
- LfgJoinResultData joinData;
- p->GetSession()->SendLfgJoinResult(joinData);
- LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_JOIN_RAIDBROWSER, dungeons, comment);
- if (p->GetGroup())
- p->GetSession()->SendLfgUpdateParty(updateData);
- else
- p->GetSession()->SendLfgUpdatePlayer(updateData);
-}
-
-void LFGMgr::LfrSearchAdd(Player* p, uint32 dungeonId)
-{
- RBSearchersStore[p->GetTeamId()][p->GetGUIDLow()] = dungeonId;
-}
-
-void LFGMgr::LfrSearchRemove(Player* p)
-{
- RBSearchersStore[p->GetTeamId()].erase(p->GetGUIDLow());
-}
-
-void LFGMgr::SendRaidBrowserCachedList(Player* player, uint32 dungeonId)
-{
- RBCacheMap::iterator itr = RBCacheStore[player->GetTeamId()].find(dungeonId);
- if (itr != RBCacheStore[player->GetTeamId()].end())
- {
- player->GetSession()->SendPacket(&(itr->second));
- return;
- }
- // send empty packet if cache not found
- WorldPacket data(SMSG_UPDATE_LFG_LIST, 1000);
- data << (uint32)LFG_TYPE_RAID;
- data << (uint32)dungeonId;
- data << (uint8)0;
- data << (uint32)0;
- data << (uint32)0;
- data << (uint32)0;
- data << (uint32)0;
- player->GetSession()->SendPacket(&data);
-}
-
-void LFGMgr::UpdateRaidBrowser(uint32 diff)
-{
- for (uint8 team=0; team<2; ++team)
- {
- if (m_raidBrowserUpdateTimer[team] > diff)
- m_raidBrowserUpdateTimer[team] -= diff;
- else
- m_raidBrowserUpdateTimer[team] = 0;
- }
-
- if (getMSTimeDiff(World::GetGameTimeMS(), getMSTime()) > (70*7)/5) // prevent lagging
- return;
-
- uint64 guid, groupGuid, instanceGuid;
- uint8 level, Class, race, talents[3];
- float iLevel, mp5, mp5combat, baseAP, rangedAP;
- int32 spellDamage, spellHeal;
- uint32 dungeonId, encounterMask, maxPower;
- uint32 deletedCounter, groupCounter, playerCounter;
- ByteBuffer buffer_deleted, buffer_groups, buffer_players;
- std::string emptyComment;
- std::set<uint64> deletedGroups, deletedGroupsToErase;
- RBInternalInfoMap copy;
-
- for (uint8 team=0; team<2; ++team)
- {
- if (m_raidBrowserLastUpdatedDungeonId[team] == 0) // new loop
- {
- if (m_raidBrowserUpdateTimer[team] > 0) // allowed only with some time interval
- continue;
- else // reset timer
- m_raidBrowserUpdateTimer[team] = 5000;
- }
-
- RBUsedDungeonsSet::const_iterator neitr, titr;
- for (neitr = RBUsedDungeonsStore[team].begin(); neitr != RBUsedDungeonsStore[team].end(); )
- {
- titr = neitr++;
- dungeonId = (*titr);
-
- // go to next dungeon than previously (one dungeon updated in one LFGMgr::UpdateRaidBrowser)
- if (dungeonId <= m_raidBrowserLastUpdatedDungeonId[team])
- continue;
- m_raidBrowserLastUpdatedDungeonId[team] = dungeonId;
-
- RBEntryInfoMap& entryInfoMap = RaidBrowserStore[team][dungeonId];
- LFGDungeonData const* dungeonData = GetLFGDungeon(dungeonId); // checked if exists before inserting to the container
- RBInternalInfoMap& currInternalInfoMap = RBInternalInfoStoreCurr[team][dungeonId];
- for (RBEntryInfoMap::const_iterator sitr = entryInfoMap.begin(); sitr != entryInfoMap.end(); ++sitr)
- {
- guid = MAKE_NEW_GUID(sitr->first, 0, HIGHGUID_PLAYER);
- groupGuid = 0;
- Player* p = ObjectAccessor::FindPlayerInOrOutOfWorld(guid);
- ASSERT(p);
- if (sitr->second.roles == PLAYER_ROLE_LEADER)
- {
- ASSERT(p->GetGroup());
- groupGuid = p->GetGroup()->GetGUID();
- }
- encounterMask = 0;
- instanceGuid = 0;
- if (InstancePlayerBind* bind = sInstanceSaveMgr->PlayerGetBoundInstance(sitr->first, dungeonData->map, dungeonData->difficulty))
- if (bind->perm)
- {
- instanceGuid = MAKE_NEW_GUID(bind->save->GetInstanceId(), 0, HIGHGUID_INSTANCE);
- encounterMask = bind->save->GetCompletedEncounterMask();
- }
-
- talents[0] = 0;
- talents[1] = 0;
- talents[2] = 0;
- p->GetTalentTreePoints(talents);
- spellDamage = p->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_ALL);
- spellHeal = p->SpellBaseHealingBonusDone(SPELL_SCHOOL_MASK_ALL);
- mp5 = p->GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER);
- mp5combat = p->GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER);
- baseAP = p->GetTotalAttackPowerValue(BASE_ATTACK);
- rangedAP = p->GetTotalAttackPowerValue(RANGED_ATTACK);
- maxPower = 0;
- if (p->getClass() == CLASS_DRUID)
- maxPower = p->GetMaxPower(POWER_MANA);
- else
- maxPower = (p->getPowerType() == POWER_RAGE || p->getPowerType() == POWER_RUNIC_POWER) ? p->GetMaxPower(p->getPowerType())/10 : p->GetMaxPower(p->getPowerType());
-
- currInternalInfoMap[sitr->first] = RBInternalInfo(guid, sitr->second.comment, groupGuid != 0, groupGuid, sitr->second.roles, encounterMask, instanceGuid,
- 1, p->getLevel(), p->getClass(), p->getRace(), p->GetAverageItemLevel(),
- talents, p->m_last_area_id, p->GetArmor(), (uint32)std::max<int32>(0, spellDamage), (uint32)std::max<int32>(0, spellHeal),
- p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_CRIT_MELEE), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_CRIT_RANGED), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_CRIT_SPELL), std::max<float>(0.0f, mp5), std::max<float>(0.0f, mp5combat),
- std::max<uint32>(baseAP, rangedAP), (uint32)p->GetStat(STAT_AGILITY), p->GetMaxHealth(), maxPower, p->GetDefenseSkillValue(),
- p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_DODGE), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_BLOCK), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_PARRY), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_HASTE_SPELL), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_EXPERTISE));
-
- if (!groupGuid)
- continue;
- for (Group::member_citerator mitr = p->GetGroup()->GetMemberSlots().begin(); mitr != p->GetGroup()->GetMemberSlots().end(); ++mitr)
- {
- if (mitr->guid == sitr->first) // leader already added
- continue;
- guid = MAKE_NEW_GUID(mitr->guid, 0, HIGHGUID_PLAYER);
- level = 1;
- Class = 0;
- race = 0;
- iLevel = 0.0f;
- talents[0] = 0;
- talents[1] = 0;
- talents[2] = 0;
- if (const GlobalPlayerData* gpd = sWorld->GetGlobalPlayerData(mitr->guid))
- {
- level = gpd->level;
- Class = gpd->playerClass;
- race = gpd->race;
- }
- Player* mplr = ObjectAccessor::FindPlayerInOrOutOfWorld(guid);
- if (mplr)
- {
- iLevel = mplr->GetAverageItemLevel();
- mplr->GetTalentTreePoints(talents);
- }
- currInternalInfoMap[mitr->guid] = RBInternalInfo(guid, emptyComment, false, groupGuid, 0, 0, 0,
- (mplr ? 1 : 0), level, Class, race, iLevel,
- talents, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0);
- }
- }
-
- copy.clear();
- copy = currInternalInfoMap; // will be saved as prev at the end
-
- // compare prev with curr to build difference packet
- deletedCounter = 0; groupCounter = 0; playerCounter = 0;
- buffer_deleted.clear(); buffer_groups.clear(); buffer_players.clear();
- deletedGroups.clear(); deletedGroupsToErase.clear();
-
- RBInternalInfoMap& prevInternalInfoMap = RBInternalInfoStorePrev[team][dungeonId];
- RBInternalInfoMap::iterator iter, iterTmp;
- for (RBInternalInfoMap::const_iterator sitr = prevInternalInfoMap.begin(); sitr != prevInternalInfoMap.end(); ++sitr)
- {
- iter = currInternalInfoMap.find(sitr->first);
- if (iter == currInternalInfoMap.end()) // was -> isn't
- {
- if (sitr->second.isGroupLeader)
- deletedGroups.insert(sitr->second.groupGuid);
- ++deletedCounter;
- buffer_deleted << (uint64)sitr->second.guid;
- }
- else // was -> is
- {
- if (sitr->second.isGroupLeader) // was a leader
- {
- if (!iter->second.isGroupLeader) // leader -> no longer a leader
- deletedGroups.insert(sitr->second.groupGuid);
- else if (sitr->second.groupGuid != iter->second.groupGuid) // leader -> leader of another group
- {
- deletedGroups.insert(sitr->second.groupGuid);
- deletedGroupsToErase.insert(iter->second.groupGuid);
- ++groupCounter;
- RBPacketAppendGroup(iter->second, buffer_groups);
- }
- else if (sitr->second.comment != iter->second.comment || sitr->second.encounterMask != iter->second.encounterMask || sitr->second.instanceGuid != iter->second.instanceGuid) // leader -> nothing changed
- {
- ++groupCounter;
- RBPacketAppendGroup(iter->second, buffer_groups);
- }
- }
- else if (iter->second.isGroupLeader) // wasn't a leader -> is a leader
- {
- deletedGroupsToErase.insert(iter->second.groupGuid);
- ++groupCounter;
- RBPacketAppendGroup(iter->second, buffer_groups);
- }
-
- if (!iter->second._online) // if offline, copy previous stats (itemLevel, talents, area, etc.)
- {
- iterTmp = copy.find(sitr->first); // copied container is for building a full packet, so modify it there (currInternalInfoMap is erased)
- iterTmp->second.CopyStats(sitr->second);
- if (!sitr->second.PlayerSameAs(iterTmp->second)) // player info changed
- {
- ++playerCounter;
- RBPacketAppendPlayer(iterTmp->second, buffer_players);
- }
- }
- else if (!sitr->second.PlayerSameAs(iter->second)) // player info changed
- {
- ++playerCounter;
- RBPacketAppendPlayer(iter->second, buffer_players);
- }
- currInternalInfoMap.erase(iter);
- }
- }
- // left entries (new)
- for (RBInternalInfoMap::const_iterator sitr = currInternalInfoMap.begin(); sitr != currInternalInfoMap.end(); ++sitr)
- {
- if (sitr->second.isGroupLeader)
- {
- deletedGroupsToErase.insert(sitr->second.groupGuid);
- ++groupCounter;
- RBPacketAppendGroup(sitr->second, buffer_groups);
- }
- ++playerCounter;
- RBPacketAppendPlayer(sitr->second, buffer_players);
- }
-
- if (!deletedGroupsToErase.empty())
- for (std::set<uint64>::const_iterator sitr = deletedGroupsToErase.begin(); sitr != deletedGroupsToErase.end(); ++sitr)
- deletedGroups.erase(*sitr);
-
- if (!deletedGroups.empty())
- for (std::set<uint64>::const_iterator sitr = deletedGroups.begin(); sitr != deletedGroups.end(); ++sitr)
- {
- ++deletedCounter;
- buffer_deleted << (*sitr);
- }
-
- WorldPacket differencePacket(SMSG_UPDATE_LFG_LIST, 1000);
- RBPacketBuildDifference(differencePacket, dungeonId, deletedCounter, buffer_deleted, groupCounter, buffer_groups, playerCounter, buffer_players);
- WorldPacket fullPacket(SMSG_UPDATE_LFG_LIST, 1000);
- RBPacketBuildFull(fullPacket, dungeonId, copy);
-
- RBCacheStore[team][dungeonId] = fullPacket;
- prevInternalInfoMap = copy;
- currInternalInfoMap.clear();
-
- if (entryInfoMap.empty())
- RBUsedDungeonsStore[team].erase(titr);
-
- // send difference packet to browsing players
- for (RBSearchersMap::const_iterator sitr = RBSearchersStore[team].begin(); sitr != RBSearchersStore[team].end(); ++sitr)
- if (sitr->second == dungeonId)
- if (Player* p = ObjectAccessor::FindPlayerInOrOutOfWorld(MAKE_NEW_GUID(sitr->first, 0, HIGHGUID_PLAYER)))
- p->GetSession()->SendPacket(&differencePacket);
-
- break; // one dungeon updated in one LFGMgr::UpdateRaidBrowser
- }
-
- // already updated all in this time interval
- if (neitr == RBUsedDungeonsStore[team].end())
- m_raidBrowserLastUpdatedDungeonId[team] = 0;
- }
-}
-
-void LFGMgr::RBPacketAppendGroup(const RBInternalInfo& info, ByteBuffer& buffer)
-{
- buffer << (uint64)info.groupGuid;
- uint32 flags = LFG_UPDATE_FLAG_COMMENT | LFG_UPDATE_FLAG_ROLES | LFG_UPDATE_FLAG_BINDED;
- buffer << (uint32)flags;
- if (flags & LFG_UPDATE_FLAG_COMMENT)
- buffer << info.comment;
- if (flags & LFG_UPDATE_FLAG_ROLES)
- for (uint8 j=0; j<3; ++j)
- buffer << (uint8)0;
- if (!(flags & LFG_UPDATE_FLAG_BINDED))
- return;
- buffer << (uint64)info.instanceGuid;
- buffer << (uint32)info.encounterMask;
-}
-
-void LFGMgr::RBPacketAppendPlayer(const RBInternalInfo& info, ByteBuffer& buffer)
-{
- buffer << (uint64)info.guid;
- uint32 flags = LFG_UPDATE_FLAG_CHARACTERINFO | LFG_UPDATE_FLAG_ROLES | LFG_UPDATE_FLAG_COMMENT | (info.groupGuid ? LFG_UPDATE_FLAG_GROUPGUID : LFG_UPDATE_FLAG_BINDED) | (info.isGroupLeader ? LFG_UPDATE_FLAG_GROUPLEADER : 0) | (!info.groupGuid || info.isGroupLeader ? LFG_UPDATE_FLAG_AREA : 0);
- buffer << (uint32)flags;
-
- if (flags & LFG_UPDATE_FLAG_CHARACTERINFO)
- {
- buffer << (uint8)info._level;
- buffer << (uint8)info._class;
- buffer << (uint8)info._race;
- buffer << (uint8)info._talents0;
- buffer << (uint8)info._talents1;
- buffer << (uint8)info._talents2;
- buffer << (uint32)info._armor;
- buffer << (uint32)info._spellDamage;
- buffer << (uint32)info._spellHeal;
- buffer << (uint32)info._critRatingMelee;
- buffer << (uint32)info._critRatingRanged;
- buffer << (uint32)info._critRatingSpell;
- buffer << (float)info._mp5;
- buffer << (float)info._mp5combat;
- buffer << (uint32)info._attackPower;
- buffer << (uint32)info._agility;
- buffer << (uint32)info._health;
- buffer << (uint32)info._mana;
- buffer << (uint32)info._online; // talentpoints, used as online/offline marker :D
- buffer << (float)info._avgItemLevel; // avgitemlevel
- buffer << (uint32)info._defenseSkill;
- buffer << (uint32)info._dodgeRating;
- buffer << (uint32)info._blockRating;
- buffer << (uint32)info._parryRating;
- buffer << (uint32)info._hasteRating;
- buffer << (uint32)info._expertiseRating;
- }
-
- if (flags & LFG_UPDATE_FLAG_COMMENT)
- buffer << (info.groupGuid ? std::string("") : info.comment);
- if (flags & LFG_UPDATE_FLAG_GROUPLEADER)
- buffer << (uint8)1; // isLFM
- if (flags & LFG_UPDATE_FLAG_GROUPGUID)
- buffer << (uint64)info.groupGuid;
- if (flags & LFG_UPDATE_FLAG_ROLES)
- buffer << (uint8)(info.groupGuid ? (info.isGroupLeader ? PLAYER_ROLE_LEADER : 0) : info.roles);
- if (flags & LFG_UPDATE_FLAG_AREA)
- buffer << (uint32)info._area;
- if (flags & LFG_UPDATE_FLAG_STATUS)
- buffer << (uint8)0;
- if (!(flags & LFG_UPDATE_FLAG_BINDED))
- return;
- buffer << (uint64)info.instanceGuid;
- buffer << (uint32)info.encounterMask;
-}
-
-void LFGMgr::RBPacketBuildDifference(WorldPacket& differencePacket, uint32 dungeonId, uint32 deletedCounter, ByteBuffer& buffer_deleted, uint32 groupCounter, ByteBuffer& buffer_groups, uint32 playerCounter, ByteBuffer& buffer_players)
-{
- differencePacket << (uint32)LFG_TYPE_RAID;
- differencePacket << (uint32)dungeonId;
- differencePacket << (uint8)1;
- differencePacket << (uint32)deletedCounter;
- differencePacket.append(buffer_deleted);
- differencePacket << (uint32)groupCounter;
- differencePacket << (uint32)0;
- differencePacket.append(buffer_groups);
- differencePacket << (uint32)playerCounter;
- differencePacket << (uint32)0;
- differencePacket.append(buffer_players);
-}
-
-void LFGMgr::RBPacketBuildFull(WorldPacket& fullPacket, uint32 dungeonId, RBInternalInfoMap& infoMap)
-{
- fullPacket << (uint32)LFG_TYPE_RAID;
- fullPacket << (uint32)dungeonId;
- fullPacket << (uint8)0;
- uint32 groupCounter = 0, playerCounter = 0;
- ByteBuffer buffer_groups, buffer_players;
- for (RBInternalInfoMap::const_iterator itr = infoMap.begin(); itr != infoMap.end(); ++itr)
- {
- if (itr->second.isGroupLeader)
- {
- ++groupCounter;
- RBPacketAppendGroup(itr->second, buffer_groups);
- }
- ++playerCounter;
- RBPacketAppendPlayer(itr->second, buffer_players);
- }
- fullPacket << (uint32)groupCounter;
- fullPacket << (uint32)0;
- fullPacket.append(buffer_groups);
- fullPacket << (uint32)playerCounter;
- fullPacket << (uint32)0;
- fullPacket.append(buffer_players);
-}
-
-// pussywizard:
-void LFGMgr::LeaveAllLfgQueues(uint64 guid, bool allowgroup, uint64 groupguid)
-{
- uint64 pguid = 0, gguid = 0;
- if (IS_GROUP_GUID(guid))
- gguid = guid;
- else if (groupguid && IS_GROUP_GUID(groupguid))
- {
- pguid = guid;
- gguid = groupguid;
- }
- else
- {
- pguid = guid;
- gguid = GetGroup(guid);
- }
- if (!allowgroup)
- gguid = 0;
-
- if (pguid)
- for (lfg::LfgQueueContainer::iterator itr = QueuesStore.begin(); itr != QueuesStore.end(); ++itr)
- itr->second.RemoveFromQueue(pguid);
- if (gguid)
- for (lfg::LfgQueueContainer::iterator itr = QueuesStore.begin(); itr != QueuesStore.end(); ++itr)
- itr->second.RemoveFromQueue(gguid);
-
- if (pguid && !gguid)
- {
- if (GetState(pguid) == LFG_STATE_QUEUED)
- {
- SendLfgUpdatePlayer(pguid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
- SetState(pguid, LFG_STATE_NONE);
- }
- }
- if (gguid)
- {
- if (GetState(gguid) == LFG_STATE_QUEUED)
- {
- SetState(gguid, LFG_STATE_NONE);
- const LfgGuidSet& players = GetPlayers(gguid);
- for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
- {
- SetState(*it, LFG_STATE_NONE);
- SendLfgUpdateParty(*it, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
- }
- }
- }
-}
-
-/**
- Update the Role check info with the player selected role.
-
- @param[in] grp Group guid to update rolecheck
- @param[in] guid Player guid (0 = rolecheck failed)
- @param[in] roles Player selected roles
-*/
-void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* = PLAYER_ROLE_NONE */)
-{
- if (!gguid)
- return;
-
- LfgRolesMap check_roles;
- LfgRoleCheckContainer::iterator itRoleCheck = RoleChecksStore.find(gguid);
- if (itRoleCheck == RoleChecksStore.end())
- return;
-
- LfgRoleCheck& roleCheck = itRoleCheck->second;
- bool sendRoleChosen = roleCheck.state != LFG_ROLECHECK_DEFAULT && guid;
-
- if (!guid)
- roleCheck.state = LFG_ROLECHECK_ABORTED;
- else if (roles < PLAYER_ROLE_TANK) // Player selected no role.
- roleCheck.state = LFG_ROLECHECK_NO_ROLE;
- else
- {
- roleCheck.roles[guid] = roles;
-
- // Check if all players have selected a role
- LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin();
- while (itRoles != roleCheck.roles.end() && itRoles->second != PLAYER_ROLE_NONE)
- ++itRoles;
-
- if (itRoles == roleCheck.roles.end())
- {
- // use temporal var to check roles, CheckGroupRoles modifies the roles
- check_roles = roleCheck.roles;
- roleCheck.state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES;
- }
- }
-
- LfgDungeonSet dungeons;
- if (roleCheck.rDungeonId)
- dungeons.insert(roleCheck.rDungeonId);
- else
- dungeons = roleCheck.dungeons;
-
- LfgJoinResultData joinData = LfgJoinResultData(LFG_JOIN_FAILED, roleCheck.state);
- for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it)
- {
- uint64 pguid = it->first;
-
- if (sendRoleChosen)
- SendLfgRoleChosen(pguid, guid, roles);
-
- SendLfgRoleCheckUpdate(pguid, roleCheck);
- switch (roleCheck.state)
- {
- case LFG_ROLECHECK_INITIALITING:
- continue;
- case LFG_ROLECHECK_FINISHED:
- SetState(pguid, LFG_STATE_QUEUED);
- SetRoles(pguid, it->second);
- SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid)));
- break;
- default:
- if (roleCheck.leader == pguid)
- SendLfgJoinResult(pguid, joinData);
- SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ROLECHECK_FAILED));
- RestoreState(pguid, "Rolecheck Failed");
- break;
- }
- }
-
- if (roleCheck.state == LFG_ROLECHECK_FINISHED)
- {
- SetState(gguid, LFG_STATE_QUEUED);
- LFGQueue& queue = GetQueue(gguid);
- queue.AddQueueData(gguid, time_t(time(NULL)), roleCheck.dungeons, roleCheck.roles);
- RoleChecksStore.erase(itRoleCheck);
- }
- else if (roleCheck.state != LFG_ROLECHECK_INITIALITING)
- {
- RestoreState(gguid, "Rolecheck Failed");
- RoleChecksStore.erase(itRoleCheck);
- }
-}
-
-/**
- Given a list of dungeons remove the dungeons players have restrictions.
-
- @param[in, out] dungeons Dungeons to check restrictions
- @param[in] players Set of players to check their dungeon restrictions
- @param[out] lockMap Map of players Lock status info of given dungeons (Empty if dungeons is not empty)
-*/
-void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap)
-{
- lockMap.clear();
- for (LfgGuidSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it)
- {
- uint64 guid = (*it);
- LfgLockMap const& cachedLockMap = GetLockedDungeons(guid);
- for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2)
- {
- uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids
- LfgDungeonSet::iterator itDungeon = dungeons.find(dungeonId);
- if (itDungeon != dungeons.end())
- {
- dungeons.erase(itDungeon);
- lockMap[guid][dungeonId] = it2->second;
- }
- }
- }
- if (!dungeons.empty())
- lockMap.clear();
-}
-
-uint8 LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true*/)
-{
- if (groles.empty())
- return 0;
-
- uint8 damage = 0;
- uint8 tank = 0;
- uint8 healer = 0;
-
- if (removeLeaderFlag)
- for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it)
- it->second &= ~PLAYER_ROLE_LEADER;
-
- for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it)
- {
- if (it->second == PLAYER_ROLE_NONE)
- return 0;
-
- if (it->second & PLAYER_ROLE_DAMAGE)
- {
- if (it->second != PLAYER_ROLE_DAMAGE)
- {
- it->second -= PLAYER_ROLE_DAMAGE;
- if (uint8 x = CheckGroupRoles(groles, false))
- return x;
- it->second += PLAYER_ROLE_DAMAGE;
- }
- else if (damage == LFG_DPS_NEEDED)
- return 0;
- else
- damage++;
- }
-
- if (it->second & PLAYER_ROLE_HEALER)
- {
- if (it->second != PLAYER_ROLE_HEALER)
- {
- it->second -= PLAYER_ROLE_HEALER;
- if (uint8 x = CheckGroupRoles(groles, false))
- return x;
- it->second += PLAYER_ROLE_HEALER;
- }
- else if (healer == LFG_HEALERS_NEEDED)
- return 0;
- else
- healer++;
- }
-
- if (it->second & PLAYER_ROLE_TANK)
- {
- if (it->second != PLAYER_ROLE_TANK)
- {
- it->second -= PLAYER_ROLE_TANK;
- if (uint8 x = CheckGroupRoles(groles, false))
- return x;
- it->second += PLAYER_ROLE_TANK;
- }
- else if (tank == LFG_TANKS_NEEDED)
- return 0;
- else
- tank++;
- }
- }
- if ((tank + healer + damage) == uint8(groles.size()))
- return (8*tank + 4*healer + damage);
- return 0;
-}
-
-/**
- Makes a new group given a proposal
- @param[in] proposal Proposal to get info from
-*/
-void LFGMgr::MakeNewGroup(LfgProposal const& proposal)
-{
- LfgGuidList players;
- LfgGuidList playersToTeleport;
-
- for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
- {
- uint64 guid = it->first;
- if (guid == proposal.leader)
- players.push_front(guid);
- else
- players.push_back(guid);
-
- if (proposal.isNew || GetGroup(guid) != proposal.group)
- playersToTeleport.push_back(guid);
- }
-
- // Set the dungeon difficulty
- LFGDungeonData const* dungeon = GetLFGDungeon(proposal.dungeonId);
- ASSERT(dungeon);
-
- Group* grp = proposal.group ? sGroupMgr->GetGroupByGUID(GUID_LOPART(proposal.group)) : NULL;
- uint64 oldGroupGUID = 0;
- for (LfgGuidList::const_iterator it = players.begin(); it != players.end(); ++it)
- {
- uint64 pguid = (*it);
- Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(pguid);
- if (!player)
- continue;
-
- Group* group = player->GetGroup();
-
- // Xinef: Apply Random Buff
- if (grp && !grp->IsLfgWithBuff())
- {
- if (!group || group->GetGUID() != oldGroupGUID)
- grp->AddLfgBuffFlag();
- else
- oldGroupGUID = group->GetGUID();
- }
-
- // Xinef: Store amount of random players player grouped with
- if (group)
- {
- SetRandomPlayersCount(pguid, group->GetMembersCount() >= MAXGROUPSIZE ? 0 : MAXGROUPSIZE-group->GetMembersCount());
- oldGroupGUID = group->GetGUID();
- if (group != grp)
- group->RemoveMember(player->GetGUID());
- }
- else
- SetRandomPlayersCount(pguid, MAXGROUPSIZE-1);
-
- if (!grp)
- {
- grp = new Group();
- grp->ConvertToLFG();
- grp->Create(player);
- uint64 gguid = grp->GetGUID();
- SetState(gguid, LFG_STATE_PROPOSAL);
- sGroupMgr->AddGroup(grp);
- }
- else if (group != grp)
- {
- // pussywizard:
- if (!grp->IsFull())
- grp->AddMember(player);
- //else // some cleanup? LeaveLFG?
- // ;
- }
-
- grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role);
- }
-
- // pussywizard: crashfix, group wasn't created when iterating players (no player found by guid), proposal is deleted by the calling function
- if (!grp)
- return;
-
- grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty));
- uint64 gguid = grp->GetGUID();
- SetDungeon(gguid, dungeon->Entry());
- SetState(gguid, LFG_STATE_DUNGEON);
-
- _SaveToDB(gguid);
-
- bool randomDungeon = false;
- // Teleport Player
- for (LfgGuidList::const_iterator it = playersToTeleport.begin(); it != playersToTeleport.end(); ++it)
- if (Player* player = ObjectAccessor::FindPlayer(*it))
- {
- if (player->GetGroup() != grp) // pussywizard: could not add because group was full (some shitness happened)
- continue;
- // Add the cooldown spell if queued for a random dungeon
- // xinef: add aura
- if ((randomDungeon || selectedRandomLfgDungeon(player->GetGUID())) && !player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN))
- {
- randomDungeon = true;
- player->AddAura(LFG_SPELL_DUNGEON_COOLDOWN, player);
- }
- TeleportPlayer(player, false);
- }
-
- if (randomDungeon)
- grp->AddLfgRandomInstanceFlag();
- if (Difficulty(dungeon->difficulty) == DUNGEON_DIFFICULTY_HEROIC)
- grp->AddLfgHeroicFlag();
-
- // Update group info
- grp->SendUpdate();
-}
-
-uint32 LFGMgr::AddProposal(LfgProposal& proposal)
-{
- proposal.id = ++m_lfgProposalId;
- ProposalsStore[m_lfgProposalId] = proposal;
- return m_lfgProposalId;
-}
-
-/**
- Update Proposal info with player answer
-
- @param[in] proposalId Proposal id to be updated
- @param[in] guid Player guid to update answer
- @param[in] accept Player answer
-*/
-void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept)
-{
- // Check if the proposal exists
- LfgProposalContainer::iterator itProposal = ProposalsStore.find(proposalId);
- if (itProposal == ProposalsStore.end())
- return;
-
- LfgProposal& proposal = itProposal->second;
-
- // Check if proposal have the current player
- LfgProposalPlayerContainer::iterator itProposalPlayer = proposal.players.find(guid);
- if (itProposalPlayer == proposal.players.end())
- return;
-
- LfgProposalPlayer& player = itProposalPlayer->second;
- player.accept = LfgAnswer(accept);
-
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::UpdateProposal: Player [" UI64FMTD "] of proposal %u selected: %u", guid, proposalId, accept);
- if (!accept)
- {
- RemoveProposal(itProposal, LFG_UPDATETYPE_PROPOSAL_DECLINED);
- return;
- }
-
- // check if all have answered and reorder players (leader first)
- bool allAnswered = true;
- for (LfgProposalPlayerContainer::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers)
- if (itPlayers->second.accept != LFG_ANSWER_AGREE) // No answer (-1) or not accepted (0)
- allAnswered = false;
-
- if (!allAnswered)
- {
- for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
- SendLfgUpdateProposal(it->first, proposal);
-
- return;
- }
-
- bool sendUpdate = proposal.state != LFG_PROPOSAL_SUCCESS;
- proposal.state = LFG_PROPOSAL_SUCCESS;
- time_t joinTime = time(NULL);
-
- LFGQueue& queue = GetQueue(guid);
- LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND);
- for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
- {
- uint64 pguid = it->first;
- uint64 gguid = it->second.group;
- uint32 dungeonId = (*GetSelectedDungeons(pguid).begin());
- int32 waitTime = -1;
- if (sendUpdate)
- SendLfgUpdateProposal(pguid, proposal);
-
- if (gguid)
- {
- waitTime = int32((joinTime - queue.GetJoinTime(gguid)) / IN_MILLISECONDS);
- SendLfgUpdateParty(pguid, updateData);
- }
- else
- {
- waitTime = int32((joinTime - queue.GetJoinTime(pguid)) / IN_MILLISECONDS);
- SendLfgUpdatePlayer(pguid, updateData);
- }
- updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE;
- SendLfgUpdatePlayer(pguid, updateData);
- SendLfgUpdateParty(pguid, updateData);
-
- // Update timers
- uint8 role = GetRoles(pguid);
- role &= ~PLAYER_ROLE_LEADER;
- switch (role)
- {
- case PLAYER_ROLE_DAMAGE:
- queue.UpdateWaitTimeDps(waitTime, dungeonId);
- break;
- case PLAYER_ROLE_HEALER:
- queue.UpdateWaitTimeHealer(waitTime, dungeonId);
- break;
- case PLAYER_ROLE_TANK:
- queue.UpdateWaitTimeTank(waitTime, dungeonId);
- break;
- default:
- queue.UpdateWaitTimeAvg(waitTime, dungeonId);
- break;
- }
-
- SetState(pguid, LFG_STATE_DUNGEON);
- }
-
- // Remove players/groups from Queue
- for (uint8 i=0; i<5 && proposal.queues.guid[i]; ++i)
- queue.RemoveQueueData(proposal.queues.guid[i]);
-
- MakeNewGroup(proposal);
- ProposalsStore.erase(itProposal);
-}
-
-/**
- Remove a proposal from the pool, remove the group that didn't accept (if needed) and readd the other members to the queue
-
- @param[in] itProposal Iterator to the proposal to remove
- @param[in] type Type of removal (LFG_UPDATETYPE_PROPOSAL_FAILED, LFG_UPDATETYPE_PROPOSAL_DECLINED)
-*/
-void LFGMgr::RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdateType type)
-{
- LfgProposal& proposal = itProposal->second;
- proposal.state = LFG_PROPOSAL_FAILED;
-
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Proposal %u, state FAILED, UpdateType %u", itProposal->first, type);
- // Mark all people that didn't answered as no accept
- if (type == LFG_UPDATETYPE_PROPOSAL_FAILED)
- for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
- if (it->second.accept == LFG_ANSWER_PENDING)
- it->second.accept = LFG_ANSWER_DENY;
-
- // pussywizard: add cooldown for not accepting (after 40 secs) or declining
- for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
- if (it->second.accept == LFG_ANSWER_DENY)
- if (Player* plr = sObjectAccessor->FindPlayer(it->first))
- if (Aura* aura = plr->AddAura(LFG_SPELL_DUNGEON_COOLDOWN, plr))
aura->SetDuration(150*IN_MILLISECONDS);
-
- // Mark players/groups to be removed
- LfgGuidSet toRemove;
- for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
- {
- if (it->second.accept == LFG_ANSWER_AGREE)
- continue;
-
- uint64 guid = it->second.group ? it->second.group : it->first;
- // Player didn't accept or still pending when no secs left
- if (it->second.accept == LFG_ANSWER_DENY || type == LFG_UPDATETYPE_PROPOSAL_FAILED)
- {
- it->second.accept = LFG_ANSWER_DENY;
- toRemove.insert(guid);
- }
- }
-
- // Notify players
- for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
- {
- uint64 guid = it->first;
- uint64 gguid = it->second.group ? it->second.group : guid;
-
- SendLfgUpdateProposal(guid, proposal);
-
- if (toRemove.find(gguid) != toRemove.end()) // Didn't accept or in same group that someone that didn't accept
- {
- LfgUpdateData updateData;
- if (it->second.accept == LFG_ANSWER_DENY)
- {
- updateData.updateType = type;
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] didn't accept. Removing from queue and compatible cache", guid);
- }
- else
- {
- updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE;
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] in same group that someone that didn't accept. Removing from queue and compatible cache", guid);
- }
-
- RestoreState(guid, "Proposal Fail (didn't accepted or in group with someone that didn't accept");
- if (gguid != guid)
- {
- RestoreState(it->second.group, "Proposal Fail (someone in group didn't accepted)");
- SendLfgUpdateParty(guid, updateData);
- }
- else
- SendLfgUpdatePlayer(guid, updateData);
- }
- else
- {
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Readding [" UI64FMTD "] to queue.", guid);
- SetState(guid, LFG_STATE_QUEUED);
- if (gguid != guid)
- {
- SetState(gguid, LFG_STATE_QUEUED);
- SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid)));
- }
- else
- SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid)));
- }
- }
-
- LFGQueue& queue = GetQueue(proposal.players.begin()->first);
- // Remove players/groups from queue
- for (LfgGuidSet::const_iterator it = toRemove.begin(); it != toRemove.end(); ++it)
- {
- uint64 guid = *it;
- queue.RemoveFromQueue(guid);
- proposal.queues.remove(guid);
- }
-
- // Readd to queue
- for (uint8 i=0; i<5 && proposal.queues.guid[i]; ++i)
- {
- // xinef: this will work as data is not deleted, only references to this data are cleared
- // xinef: when new proposal is created
- // xinef: successful proposal is also taken into account is similar manner
- queue.AddToQueue(proposal.queues.guid[i], true);
- }
-
- ProposalsStore.erase(itProposal);
-}
-
-/**
- Initialize a boot kick vote
-
- @param[in] gguid Group the vote kicks belongs to
- @param[in] kicker Kicker guid
- @param[in] victim Victim guid
- @param[in] reason Kick reason
-*/
-void LFGMgr::InitBoot(uint64 gguid, uint64 kicker, uint64 victim, std::string const& reason)
-{
- SetState(gguid, LFG_STATE_BOOT);
-
- LfgPlayerBoot& boot = BootsStore[gguid];
- boot.inProgress = true;
- boot.cancelTime = time_t(time(NULL)) + LFG_TIME_BOOT;
- boot.reason = reason;
- boot.victim = victim;
-
- LfgGuidSet const& players = GetPlayers(gguid);
-
- // Set votes
- for (LfgGuidSet::const_iterator itr = players.begin(); itr != players.end(); ++itr)
- {
- uint64 guid = (*itr);
- SetState(guid, LFG_STATE_BOOT);
- boot.votes[guid] = LFG_ANSWER_PENDING;
- }
-
- boot.votes[victim] = LFG_ANSWER_DENY; // Victim auto vote NO
- boot.votes[kicker] = LFG_ANSWER_AGREE; // Kicker auto vote YES
-
- // Notify players
- for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
- SendLfgBootProposalUpdate(*it, boot);
-}
-
-/**
- Update Boot info with player answer
-
- @param[in] guid Player who has answered
- @param[in] player answer
-*/
-void LFGMgr::UpdateBoot(uint64 guid, bool accept)
-{
- uint64 gguid = GetGroup(guid);
- if (!gguid)
- return;
-
- LfgPlayerBootContainer::iterator itBoot = BootsStore.find(gguid);
- if (itBoot == BootsStore.end())
- return;
-
- LfgPlayerBoot& boot = itBoot->second;
-
- if (boot.votes[guid] != LFG_ANSWER_PENDING) // Cheat check: Player can't vote twice
- return;
-
- boot.votes[guid] = LfgAnswer(accept);
-
- uint8 votesNum = 0;
- uint8 agreeNum = 0;
- for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes)
- {
- if (itVotes->second != LFG_ANSWER_PENDING)
- {
- ++votesNum;
- if (itVotes->second == LFG_ANSWER_AGREE)
- ++agreeNum;
- }
- }
-
- // if we don't have enough votes (agree or deny) do nothing
- if (agreeNum < LFG_GROUP_KICK_VOTES_NEEDED && (votesNum - agreeNum) < LFG_GROUP_KICK_VOTES_NEEDED)
- return;
-
- // Send update info to all players
- boot.inProgress = false;
- for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes)
- {
- uint64 pguid = itVotes->first;
- if (pguid != boot.victim)
- {
- SetState(pguid, LFG_STATE_DUNGEON);
- SendLfgBootProposalUpdate(pguid, boot);
- }
- }
-
- SetState(gguid, LFG_STATE_DUNGEON);
- if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player
- {
- if (Group* group = sGroupMgr->GetGroupByGUID(GUID_LOPART(gguid)))
- Player::RemoveFromGroup(group, boot.victim, GROUP_REMOVEMETHOD_KICK_LFG);
- DecreaseKicksLeft(gguid);
- }
- BootsStore.erase(itBoot);
-}
-
-/**
- Teleports the player in or out the dungeon
-
- @param[in] player Player to teleport
- @param[in] out Teleport out (true) or in (false)
- @param[in] fromOpcode Function called from opcode handlers? (Default false)
-*/
-void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false*/)
-{
- LFGDungeonData const* dungeon = NULL;
- Group* group = player->GetGroup();
-
- if (group && group->isLFGGroup())
- dungeon = GetLFGDungeon(GetDungeon(group->GetGUID()));
-
- if (!dungeon)
- {
- player->GetSession()->SendLfgTeleportError(uint8(LFG_TELEPORTERROR_INVALID_LOCATION));
- return;
- }
-
- if (out)
- {
- if (player->GetMapId() == uint32(dungeon->map))
- player->TeleportToEntryPoint();
-
- return;
- }
-
- LfgTeleportError error = LFG_TELEPORTERROR_OK;
-
- if (!player->IsAlive())
- error = LFG_TELEPORTERROR_PLAYER_DEAD;
- else if (player->IsFalling() || player->HasUnitState(UNIT_STATE_JUMPING))
- error = LFG_TELEPORTERROR_FALLING;
- else if (player->IsMirrorTimerActive(FATIGUE_TIMER))
- error = LFG_TELEPORTERROR_FATIGUE;
- else if (player->GetVehicle())
- error = LFG_TELEPORTERROR_IN_VEHICLE;
- else if (player->GetCharmGUID())
- error = LFG_TELEPORTERROR_CHARMING;
- else if (player->GetMapId() != uint32(dungeon->map)) // Do not teleport players in dungeon to the entrance
- {
- uint32 mapid = dungeon->map;
- float x = dungeon->x;
- float y = dungeon->y;
- float z = dungeon->z;
- float orientation = dungeon->o;
-
- if (!fromOpcode)
- {
- // Select a player inside to be teleported to
- for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player* plrg = itr->GetSource();
- if (plrg && plrg != player && plrg->GetMapId() == uint32(dungeon->map))
- {
- mapid = plrg->GetMapId();
- x = plrg->GetPositionX();
- y = plrg->GetPositionY();
- z = plrg->GetPositionZ();
- orientation = plrg->GetOrientation();
- break;
- }
- }
- }
-
- if (!player->GetMap()->IsDungeon())
- player->SetEntryPoint();
-
- if (!player->TeleportTo(mapid, x, y, z, orientation))
- error = LFG_TELEPORTERROR_INVALID_LOCATION;
- }
- else
- error = LFG_TELEPORTERROR_INVALID_LOCATION;
-
- if (error != LFG_TELEPORTERROR_OK)
- player->GetSession()->SendLfgTeleportError(uint8(error));
-
- //sLog->outDebug(LOG_FILTER_LFG, "TeleportPlayer: Player %s is being teleported in to map %u "
- // "(x: %f, y: %f, z: %f) Result: %u", player->GetName().c_str(), dungeon->map,
- // dungeon->x, dungeon->y, dungeon->z, error);
-}
-
-/**
- Finish a dungeon and give reward, if any.
-
- @param[in] guid Group guid
- @param[in] dungeonId Dungeonid
-*/
-void LFGMgr::FinishDungeon(uint64 gguid, const uint32 dungeonId, const Map* currMap)
-{
- uint32 gDungeonId = GetDungeon(gguid);
- if (gDungeonId != dungeonId)
- {
- sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] Finished dungeon %u but group queued for %u. Ignoring", gguid, dungeonId, gDungeonId);
- return;
- }
-
- if (GetState(gguid) == LFG_STATE_FINISHED_DUNGEON) // Shouldn't happen. Do not reward multiple times
- {
- sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] Already rewarded group. Ignoring", gguid);
- return;
- }
-
- SetState(gguid, LFG_STATE_FINISHED_DUNGEON);
- _SaveToDB(gguid); // pussywizard
-
- const LfgGuidSet& players = GetPlayers(gguid);
- for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
- {
- uint64 guid = (*it);
- if (GetState(guid) == LFG_STATE_FINISHED_DUNGEON)
- {
- sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] Already rewarded player. Ignoring", guid);
- continue;
- }
-
- uint32 rDungeonId = 0;
- const LfgDungeonSet& dungeons = GetSelectedDungeons(guid);
- if (!dungeons.empty())
- rDungeonId = (*dungeons.begin());
-
- SetState(guid, LFG_STATE_FINISHED_DUNGEON);
-
- // Give rewards only if its a random dungeon
- LFGDungeonData const* dungeon = GetLFGDungeon(rDungeonId);
-
- if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !dungeon->seasonal))
- {
- sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] dungeon %u is not random or seasonal", guid, rDungeonId);
- continue;
- }
-
- Player* player = ObjectAccessor::FindPlayer(guid);
- if (!player || player->FindMap() != currMap) // pussywizard: currMap - multithreading crash if on other map (map id check is not enough, binding system is not reliable)
- {
- sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] not found in world", guid);
- continue;
- }
-
- LFGDungeonData const* dungeonDone = GetLFGDungeon(dungeonId);
- uint32 mapId = dungeonDone ? uint32(dungeonDone->map) : 0;
-
- if (player->GetMapId() != mapId)
- {
- sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] is in map %u and should be in %u to get reward", guid, player->GetMapId(), mapId);
- continue;
- }
-
- // Xinef: Update achievements, set correct amount of randomly grouped players
- if (dungeon->difficulty == DUNGEON_DIFFICULTY_HEROIC)
- if (uint8 count = GetRandomPlayersCount(player->GetGUID()))
- player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS, count);
-
- LfgReward const* reward = GetRandomDungeonReward(rDungeonId, player->getLevel());
- if (!reward)
- continue;
-
- bool done = false;
- Quest const* quest = sObjectMgr->GetQuestTemplate(reward->firstQuest);
- if (!quest)
- continue;
-
- // if we can take the quest, means that we haven't done this kind of "run", IE: First Heroic Random of Day.
- if (player->CanRewardQuest(quest, false))
- player->RewardQuest(quest, 0, NULL, false);
- else
- {
- done = true;
- quest = sObjectMgr->GetQuestTemplate(reward->otherQuest);
- if (!quest)
- continue;
- // we give reward without informing client (retail does this)
- player->RewardQuest(quest, 0, NULL, false);
- }
-
- // Give rewards
- sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] done dungeon %u, %s previously done.", player->GetGUID(), GetDungeon(gguid), done? " " : " not");
- LfgPlayerRewardData data = LfgPlayerRewardData(dungeon->Entry(), GetDungeon(gguid, false), done, quest);
- player->GetSession()->SendLfgPlayerReward(data);
- }
-}
-
-// --------------------------------------------------------------------------//
-// Auxiliar Functions
-// --------------------------------------------------------------------------//
-
-/**
- Get the dungeon list that can be done given a random dungeon entry.
-
- @param[in] randomdungeon Random dungeon id (if value = 0 will return all dungeons)
- @returns Set of dungeons that can be done.
-*/
-LfgDungeonSet const& LFGMgr::GetDungeonsByRandom(uint32 randomdungeon)
-{
- LFGDungeonData const* dungeon = GetLFGDungeon(randomdungeon);
- uint32 group = dungeon ? dungeon->group : 0;
- return CachedDungeonMapStore[group];
-}
-
-/**
- Get the reward of a given random dungeon at a certain level
-
- @param[in] dungeon dungeon id
- @param[in] level Player level
- @returns Reward
-*/
-LfgReward const* LFGMgr::GetRandomDungeonReward(uint32 dungeon, uint8 level)
-{
- LfgReward const* rew = NULL;
- LfgRewardContainerBounds bounds = RewardMapStore.equal_range(dungeon & 0x00FFFFFF);
- for (LfgRewardContainer::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- rew = itr->second;
- // ordered properly at loading
- if (itr->second->maxLevel >= level)
- break;
- }
-
- return rew;
-}
-
-/**
- Given a Dungeon id returns the dungeon Type
-
- @param[in] dungeon dungeon id
- @returns Dungeon type
-*/
-LfgType LFGMgr::GetDungeonType(uint32 dungeonId)
-{
- LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId);
- if (!dungeon)
- return LFG_TYPE_NONE;
-
- return LfgType(dungeon->type);
-}
-
-LfgState LFGMgr::GetState(uint64 guid)
-{
- LfgState state;
- if (IS_GROUP_GUID(guid))
- state = GroupsStore[guid].GetState();
- else
- state = PlayersStore[guid].GetState();
-
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "] = %u", guid, state);
- return state;
-}
-
-LfgState LFGMgr::GetOldState(uint64 guid)
-{
- LfgState state;
- if (IS_GROUP_GUID(guid))
- state = GroupsStore[guid].GetOldState();
- else
- state = PlayersStore[guid].GetOldState();
-
- ;//sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::GetOldState: [" UI64FMTD "] = %u", guid, state);
- return state;
-}
-
-uint32 LFGMgr::GetDungeon(uint64 guid, bool asId /*= true */)
-{
- uint32 dungeon = GroupsStore[guid].GetDungeon(asId);
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetDungeon: [" UI64FMTD "] asId: %u = %u", guid, asId, dungeon);
- return dungeon;
-}
-
-uint32 LFGMgr::GetDungeonMapId(uint64 guid)
-{
- uint32 dungeonId = GroupsStore[guid].GetDungeon(true);
- uint32 mapId = 0;
- if (dungeonId)
- if (LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId))
- mapId = dungeon->map;
-
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetDungeonMapId: [" UI64FMTD "] = %u (DungeonId = %u)", guid, mapId, dungeonId);
- return mapId;
-}
-
-uint8 LFGMgr::GetRoles(uint64 guid)
-{
- uint8 roles = PlayersStore[guid].GetRoles();
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetRoles: [" UI64FMTD "] = %u", guid, roles);
- return roles;
-}
-
-const std::string& LFGMgr::GetComment(uint64 guid)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetComment: [" UI64FMTD "] = %s", guid, PlayersStore[guid].GetComment().c_str());
- return PlayersStore[guid].GetComment();
-}
-
-LfgDungeonSet const& LFGMgr::GetSelectedDungeons(uint64 guid)
-{
- ;//sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::GetSelectedDungeons: [" UI64FMTD "]", guid);
- return PlayersStore[guid].GetSelectedDungeons();
-}
-
-LfgLockMap const& LFGMgr::GetLockedDungeons(uint64 guid)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetLockedDungeons: [" UI64FMTD "]", guid);
- return PlayersStore[guid].GetLockedDungeons();
-}
-
-uint8 LFGMgr::GetKicksLeft(uint64 guid)
-{
- uint8 kicks = GroupsStore[guid].GetKicksLeft();
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetKicksLeft: [" UI64FMTD "] = %u", guid, kicks);
- return kicks;
-}
-
-void LFGMgr::RestoreState(uint64 guid, char const* debugMsg)
-{
- if (IS_GROUP_GUID(guid))
- {
- LfgGroupData& data = GroupsStore[guid];
- /*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG))
- {
- std::string const& ps = GetStateString(data.GetState());
- std::string const& os = GetStateString(data.GetOldState());
- sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::RestoreState: Group: [" UI64FMTD "] (%s) State: %s, oldState: %s",
- guid, debugMsg, ps.c_str(), os.c_str());
- }*/
-
- data.RestoreState();
- }
- else
- {
- LfgPlayerData& data = PlayersStore[guid];
- /*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG))
- {
- std::string const& ps = GetStateString(data.GetState());
- std::string const& os = GetStateString(data.GetOldState());
- sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::RestoreState: Player: [" UI64FMTD "] (%s) State: %s, oldState: %s",
- guid, debugMsg, ps.c_str(), os.c_str());
- }*/
- data.RestoreState();
- }
-}
-
-void LFGMgr::SetState(uint64 guid, LfgState state)
-{
- if (IS_GROUP_GUID(guid))
- {
- LfgGroupData& data = GroupsStore[guid];
- //char const * const ns = GetStateString(state);
- //char const * const ps = GetStateString(data.GetState());
- //char const * const os = GetStateString(data.GetOldState());
- //sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Group: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os);
- data.SetState(state);
- }
- else
- {
- LfgPlayerData& data = PlayersStore[guid];
- //char const * const ns = GetStateString(state);
- //char const * const ps = GetStateString(data.GetState());
- //char const * const os = GetStateString(data.GetOldState());
- //sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Player: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os);
- data.SetState(state);
- }
-}
-
-void LFGMgr::SetCanOverrideRBState(uint64 guid, bool val)
-{
- PlayersStore[guid].SetCanOverrideRBState(val);
-}
-
-void LFGMgr::SetDungeon(uint64 guid, uint32 dungeon)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetDungeon: [" UI64FMTD "] dungeon %u", guid, dungeon);
- GroupsStore[guid].SetDungeon(dungeon);
-}
-
-void LFGMgr::SetRoles(uint64 guid, uint8 roles)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetRoles: [" UI64FMTD "] roles: %u", guid, roles);
- PlayersStore[guid].SetRoles(roles);
-}
-
-void LFGMgr::SetComment(uint64 guid, std::string const& comment)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetComment: [" UI64FMTD "] comment: %s", guid, comment.c_str());
- PlayersStore[guid].SetComment(comment);
-}
-
-void LFGMgr::LfrSetComment(Player* p, std::string comment)
-{
- // pussywizard: client limit for comment length is 64 @ 3.3.5a
- if (comment.size() > 64)
- comment = comment.substr(0, 64);
-
- uint8 teamId = p->GetTeamId();
- RBEntryInfoMap::iterator iter;
- for (RBStoreMap::iterator itr = RaidBrowserStore[teamId].begin(); itr != RaidBrowserStore[teamId].end(); ++itr)
- if ((iter = itr->second.find(p->GetGUIDLow())) != itr->second.end())
- iter->second.comment = comment;
-}
-
-void LFGMgr::SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetSelectedDungeons: [" UI64FMTD "]", guid);
- PlayersStore[guid].SetSelectedDungeons(dungeons);
-}
-
-void LFGMgr::SetLockedDungeons(uint64 guid, LfgLockMap const& lock)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetLockedDungeons: [" UI64FMTD "]", guid);
- PlayersStore[guid].SetLockedDungeons(lock);
-}
-
-void LFGMgr::DecreaseKicksLeft(uint64 guid)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::DecreaseKicksLeft: [" UI64FMTD "]", guid);
- GroupsStore[guid].DecreaseKicksLeft();
-}
-
-void LFGMgr::RemoveGroupData(uint64 guid)
-{
- ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveGroupData: [" UI64FMTD "]", guid);
- LfgGroupDataContainer::iterator it = GroupsStore.find(guid);
- if (it == GroupsStore.end())
- return;
-
- LfgState state = GetState(guid);
- // If group is being formed after proposal success do nothing more
- LfgGuidSet const& players = it->second.GetPlayers();
- for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
- {
- uint64 guid = (*it);
- SetGroup(*it, 0);
- if (state != LFG_STATE_PROPOSAL)
- {
- SetState(*it, LFG_STATE_NONE);
- SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
- }
- }
- GroupsStore.erase(it);
-}
-
-TeamId LFGMgr::GetTeam(uint64 guid)
-{
- return PlayersStore[guid].GetTeam();
-}
-
-uint8 LFGMgr::RemovePlayerFromGroup(uint64 gguid, uint64 guid)
-{
- return GroupsStore[gguid].RemovePlayer(guid);
-}
-
-void LFGMgr::AddPlayerToGroup(uint64 gguid, uint64 guid)
-{
- GroupsStore[gguid].AddPlayer(guid);
-}
-
-void LFGMgr::SetLeader(uint64 gguid, uint64 leader)
-{
- GroupsStore[gguid].SetLeader(leader);
-}
-
-void LFGMgr::SetTeam(uint64 guid, TeamId teamId)
-{
- PlayersStore[guid].SetTeam(teamId);
-}
-
-uint64 LFGMgr::GetGroup(uint64 guid)
-{
- return PlayersStore[guid].GetGroup();
-}
-
-void LFGMgr::SetGroup(uint64 guid, uint64 group)
-{
- PlayersStore[guid].SetGroup(group);
-}
-
-LfgGuidSet const& LFGMgr::GetPlayers(uint64 guid)
-{
- return GroupsStore[guid].GetPlayers();
-}
-
-uint8 LFGMgr::GetPlayerCount(uint64 guid)
-{
- return GroupsStore[guid].GetPlayerCount();
-}
-
-uint64 LFGMgr::GetLeader(uint64 guid)
-{
- return GroupsStore[guid].GetLeader();
-}
-
-void LFGMgr::SetRandomPlayersCount(uint64 guid, uint8 count)
-{
- PlayersStore[guid].SetRandomPlayersCount(count);
-}
-
-uint8 LFGMgr::GetRandomPlayersCount(uint64 guid)
-{
- return PlayersStore[guid].GetRandomPlayersCount();
-}
-
-bool LFGMgr::HasIgnore(uint64 guid1, uint64 guid2)
-{
- Player* plr1 = ObjectAccessor::FindPlayerInOrOutOfWorld(guid1);
- Player* plr2 = ObjectAccessor::FindPlayerInOrOutOfWorld(guid2);
- uint32 low1 = GUID_LOPART(guid1);
- uint32 low2 = GUID_LOPART(guid2);
- return plr1 && plr2 && (plr1->GetSocial()->HasIgnore(low2) || plr2->GetSocial()->HasIgnore(low1));
-}
-
-void LFGMgr::SendLfgRoleChosen(uint64 guid, uint64 pguid, uint8 roles)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgRoleChosen(pguid, roles);
-}
-
-void LFGMgr::SendLfgRoleCheckUpdate(uint64 guid, LfgRoleCheck const& roleCheck)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgRoleCheckUpdate(roleCheck);
-}
-
-void LFGMgr::SendLfgUpdatePlayer(uint64 guid, LfgUpdateData const& data)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgUpdatePlayer(data);
-}
-
-void LFGMgr::SendLfgUpdateParty(uint64 guid, LfgUpdateData const& data)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgUpdateParty(data);
-}
-
-void LFGMgr::SendLfgJoinResult(uint64 guid, LfgJoinResultData const& data)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgJoinResult(data);
-}
-
-void LFGMgr::SendLfgBootProposalUpdate(uint64 guid, LfgPlayerBoot const& boot)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgBootProposalUpdate(boot);
-}
-
-void LFGMgr::SendLfgUpdateProposal(uint64 guid, LfgProposal const& proposal)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgUpdateProposal(proposal);
-}
-
-void LFGMgr::SendLfgQueueStatus(uint64 guid, LfgQueueStatusData const& data)
-{
- if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
- player->GetSession()->SendLfgQueueStatus(data);
-}
-
-bool LFGMgr::IsLfgGroup(uint64 guid)
-{
- return guid && IS_GROUP_GUID(guid) && GroupsStore[guid].IsLfgGroup();
-}
-
-LFGQueue& LFGMgr::GetQueue(uint64 guid)
-{
- uint8 queueId = 0;
- if (IS_GROUP_GUID(guid))
- {
- LfgGuidSet const& players = GetPlayers(guid);
- uint64 pguid = players.empty() ? 0 : (*players.begin());
- if (pguid)
- queueId = GetTeam(pguid);
- else
- queueId = GetTeam(GetLeader(guid));
- }
- else
- queueId = GetTeam(guid);
- return QueuesStore[queueId];
-}
-
-bool LFGMgr::AllQueued(Lfg5Guids const& check)
-{
- bool ok = true;
-
- if (check.empty())
- return false;
-
- for (uint8 i=0; i<5 && check.guid[i]; ++i)
- {
- uint64 guid = check.guid[i];
- if (GetState(guid) != LFG_STATE_QUEUED)
- {
- LFGQueue& queue = GetQueue(guid);
- queue.RemoveFromQueue(guid);
- ok = false;
- }
- }
-
- return ok;
-}
-
-// Only for debugging purposes
-void LFGMgr::Clean()
-{
- QueuesStore.clear();
-}
-
-bool LFGMgr::isOptionEnabled(uint32 option)
-{
- return m_options & option;
-}
-
-uint32 LFGMgr::GetOptions()
-{
- return m_options;
-}
-
-void LFGMgr::SetOptions(uint32 options)
-{
- m_options = options;
-}
-
-LfgUpdateData LFGMgr::GetLfgStatus(uint64 guid)
-{
- LfgPlayerData& playerData = PlayersStore[guid];
- return LfgUpdateData(LFG_UPDATETYPE_UPDATE_STATUS, playerData.GetState(), playerData.GetSelectedDungeons());
-}
-
-bool LFGMgr::IsSeasonActive(uint32 dungeonId)
-{
- switch (dungeonId)
- {
- case 285: // The Headless Horseman
- return IsHolidayActive(HOLIDAY_HALLOWS_END);
- case 286: // The Frost Lord Ahune
- return IsHolidayActive(HOLIDAY_FIRE_FESTIVAL);
- case 287: // Coren Direbrew
- return IsHolidayActive(HOLIDAY_BREWFEST);
- case 288: // The Crown Chemical Co.
- return IsHolidayActive(HOLIDAY_LOVE_IS_IN_THE_AIR);
- }
- return false;
-}
-
-void LFGMgr::SetupGroupMember(uint64 guid, uint64 gguid)
-{
- LfgDungeonSet dungeons;
- dungeons.insert(GetDungeon(gguid));
- SetSelectedDungeons(guid, dungeons);
- SetState(guid, GetState(gguid));
- SetGroup(guid, gguid);
- AddPlayerToGroup(gguid, guid);
-}
-
-bool LFGMgr::selectedRandomLfgDungeon(uint64 guid)
-{
- if (GetState(guid) != LFG_STATE_NONE)
- {
- LfgDungeonSet const& dungeons = GetSelectedDungeons(guid);
- if (!dungeons.empty())
- {
- LFGDungeonData const* dungeon = GetLFGDungeon(*dungeons.begin());
- if (dungeon && (dungeon->type == LFG_TYPE_RANDOM || dungeon->seasonal))
- return true;
- }
- }
-
- return false;
-}
-
-bool LFGMgr::inLfgDungeonMap(uint64 guid, uint32 map, Difficulty difficulty)
-{
- if (!IS_GROUP_GUID(guid))
- guid = GetGroup(guid);
-
- if (uint32 dungeonId = GetDungeon(guid, true))
- if (LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId))
- if (uint32(dungeon->map) == map && dungeon->difficulty == difficulty)
- return true;
-
- return false;
-}
-
-uint32 LFGMgr::GetLFGDungeonEntry(uint32 id)
-{
- if (id)
- if (LFGDungeonData const* dungeon = GetLFGDungeon(id))
- return dungeon->Entry();
-
- return 0;
-}
-
-LfgDungeonSet LFGMgr::GetRandomAndSeasonalDungeons(uint8 level, uint8 expansion)
-{
- LfgDungeonSet randomDungeons;
- for (lfg::LFGDungeonContainer::const_iterator itr = LfgDungeonStore.begin(); itr != LfgDungeonStore.end(); ++itr)
- {
- lfg::LFGDungeonData const& dungeon = itr->second;
- if ((dungeon.type == lfg::LFG_TYPE_RANDOM || (dungeon.seasonal && sLFGMgr->IsSeasonActive(dungeon.id)))
- && dungeon.expansion <= expansion && dungeon.minlevel <= level && level <= dungeon.maxlevel)
- randomDungeons.insert(dungeon.Entry());
- }
- return randomDungeons;
-}
-
-} // namespace lfg
+/* + * Copyright (C) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Common.h" +#include "SharedDefines.h" +#include "DBCStores.h" +#include "DisableMgr.h" +#include "ObjectMgr.h" +#include "SocialMgr.h" +#include "Language.h" +#include "LFGMgr.h" +#include "LFGScripts.h" +#include "LFGGroupData.h" +#include "LFGPlayerData.h" +#include "LFGQueue.h" +#include "Group.h" +#include "SpellAuras.h" +#include "Player.h" +#include "GroupMgr.h" +#include "GameEventMgr.h" +#include "WorldSession.h" + +namespace lfg +{ + +LFGMgr::LFGMgr(): m_lfgProposalId(1), m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK)) +{ + new LFGPlayerScript(); + new LFGGroupScript(); + + for (uint8 team=0; team<2; ++team) + { + m_raidBrowserUpdateTimer[team] = 10000; + m_raidBrowserLastUpdatedDungeonId[team] = 0; + } +} + +LFGMgr::~LFGMgr() +{ + for (LfgRewardContainer::iterator itr = RewardMapStore.begin(); itr != RewardMapStore.end(); ++itr) + delete itr->second; +} + +void LFGMgr::_LoadFromDB(Field* fields, uint64 guid) +{ + if (!fields) + return; + + if (!IS_GROUP_GUID(guid)) + return; + + SetLeader(guid, MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER)); + + uint32 dungeon = fields[17].GetUInt32(); + uint8 state = fields[18].GetUInt8(); + + if (!dungeon || !state) + return; + + SetDungeon(guid, dungeon); + + switch (state) + { + case LFG_STATE_DUNGEON: + case LFG_STATE_FINISHED_DUNGEON: + SetState(guid, (LfgState)state); + break; + default: + break; + } +} + +void LFGMgr::_SaveToDB(uint64 guid) +{ + if (!IS_GROUP_GUID(guid)) + return; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_LFG_DATA); + stmt->setUInt32(0, GUID_LOPART(guid)); + stmt->setUInt32(1, GetDungeon(guid)); + stmt->setUInt32(2, GetState(guid)); + CharacterDatabase.Execute(stmt); +} + +/// Load rewards for completing dungeons +void LFGMgr::LoadRewards() +{ + uint32 oldMSTime = getMSTime(); + + for (LfgRewardContainer::iterator itr = RewardMapStore.begin(); itr != RewardMapStore.end(); ++itr) + delete itr->second; + RewardMapStore.clear(); + + // ORDER BY is very important for GetRandomDungeonReward! + QueryResult result = WorldDatabase.Query("SELECT dungeonId, maxLevel, firstQuestId, otherQuestId FROM lfg_dungeon_rewards ORDER BY dungeonId, maxLevel ASC"); + + if (!result) + { + sLog->outError(">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!"); + return; + } + + uint32 count = 0; + + Field* fields = NULL; + do + { + fields = result->Fetch(); + uint32 dungeonId = fields[0].GetUInt32(); + uint32 maxLevel = fields[1].GetUInt8(); + uint32 firstQuestId = fields[2].GetUInt32(); + uint32 otherQuestId = fields[3].GetUInt32(); + + if (!GetLFGDungeonEntry(dungeonId)) + { + sLog->outError("Dungeon %u specified in table `lfg_dungeon_rewards` does not exist!", dungeonId); + continue; + } + + if (!maxLevel || maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + sLog->outError("Level %u specified for dungeon %u in table `lfg_dungeon_rewards` can never be reached!", maxLevel, dungeonId); + maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + } + + if (!firstQuestId || !sObjectMgr->GetQuestTemplate(firstQuestId)) + { + sLog->outError("First quest %u specified for dungeon %u in table `lfg_dungeon_rewards` does not exist!", firstQuestId, dungeonId); + continue; + } + + if (otherQuestId && !sObjectMgr->GetQuestTemplate(otherQuestId)) + { + sLog->outError("Other quest %u specified for dungeon %u in table `lfg_dungeon_rewards` does not exist!", otherQuestId, dungeonId); + otherQuestId = 0; + } + + RewardMapStore.insert(LfgRewardContainer::value_type(dungeonId, new LfgReward(maxLevel, firstQuestId, otherQuestId))); + ++count; + } + while (result->NextRow()); + + sLog->outString(">> Loaded %u lfg dungeon rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +LFGDungeonData const* LFGMgr::GetLFGDungeon(uint32 id) +{ + LFGDungeonContainer::const_iterator itr = LfgDungeonStore.find(id); + if (itr != LfgDungeonStore.end()) + return &(itr->second); + + return NULL; +} + +void LFGMgr::LoadLFGDungeons(bool reload /* = false */) +{ + uint32 oldMSTime = getMSTime(); + + LfgDungeonStore.clear(); + + // Initialize Dungeon map with data from dbcs + for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) + { + LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); + if (!dungeon) + continue; + + switch (dungeon->type) + { + case LFG_TYPE_DUNGEON: + case LFG_TYPE_HEROIC: + case LFG_TYPE_RAID: + case LFG_TYPE_RANDOM: + LfgDungeonStore[dungeon->ID] = LFGDungeonData(dungeon); + break; + } + } + + // Fill teleport locations from DB + QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_entrances"); + + if (!result) + { + sLog->outError(">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!"); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 dungeonId = fields[0].GetUInt32(); + LFGDungeonContainer::iterator dungeonItr = LfgDungeonStore.find(dungeonId); + if (dungeonItr == LfgDungeonStore.end()) + { + sLog->outError("table `lfg_entrances` contains coordinates for wrong dungeon %u", dungeonId); + continue; + } + + LFGDungeonData& data = dungeonItr->second; + data.x = fields[1].GetFloat(); + data.y = fields[2].GetFloat(); + data.z = fields[3].GetFloat(); + data.o = fields[4].GetFloat(); + + ++count; + } + while (result->NextRow()); + + sLog->outString(">> Loaded %u lfg entrance positions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + // Fill all other teleport coords from areatriggers + for (LFGDungeonContainer::iterator itr = LfgDungeonStore.begin(); itr != LfgDungeonStore.end(); ++itr) + { + LFGDungeonData& dungeon = itr->second; + + // No teleport coords in database, load from areatriggers + if (dungeon.type != LFG_TYPE_RANDOM && dungeon.x == 0.0f && dungeon.y == 0.0f && dungeon.z == 0.0f) + { + AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon.map); + if (!at) + { + sLog->outError("LFGMgr::LoadLFGDungeons: Failed to load dungeon %s, cant find areatrigger for map %u", dungeon.name.c_str(), dungeon.map); + continue; + } + + dungeon.map = at->target_mapId; + dungeon.x = at->target_X; + dungeon.y = at->target_Y; + dungeon.z = at->target_Z; + dungeon.o = at->target_Orientation; + } + + if (dungeon.type != LFG_TYPE_RANDOM) + CachedDungeonMapStore[dungeon.group].insert(dungeon.id); + CachedDungeonMapStore[0].insert(dungeon.id); + } + + if (reload) + { + CachedDungeonMapStore.clear(); + // Recalculate locked dungeons + for (LfgPlayerDataContainer::const_iterator it = PlayersStore.begin(); it != PlayersStore.end(); ++it) + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(it->first)) + InitializeLockedDungeons(player); + } +} + +void LFGMgr::Update(uint32 tdiff, uint8 task) +{ + if (!isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) + return; + + if (task == 0) + { + time_t currTime = time(NULL); + + // Remove obsolete role checks + for (LfgRoleCheckContainer::iterator it = RoleChecksStore.begin(); it != RoleChecksStore.end();) + { + LfgRoleCheckContainer::iterator itRoleCheck = it++; + LfgRoleCheck& roleCheck = itRoleCheck->second; + if (currTime < roleCheck.cancelTime) + continue; + roleCheck.state = LFG_ROLECHECK_MISSING_ROLE; + + for (LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); itRoles != roleCheck.roles.end(); ++itRoles) + { + uint64 guid = itRoles->first; + RestoreState(guid, "Remove Obsolete RoleCheck"); + SendLfgRoleCheckUpdate(guid, roleCheck); + if (guid == roleCheck.leader) + SendLfgJoinResult(guid, LfgJoinResultData(LFG_JOIN_FAILED, LFG_ROLECHECK_MISSING_ROLE)); + } + + RestoreState(itRoleCheck->first, "Remove Obsolete RoleCheck"); + RoleChecksStore.erase(itRoleCheck); + } + + // Remove obsolete proposals + for (LfgProposalContainer::iterator it = ProposalsStore.begin(); it != ProposalsStore.end();) + { + LfgProposalContainer::iterator itRemove = it++; + if (itRemove->second.cancelTime < currTime) + RemoveProposal(itRemove, LFG_UPDATETYPE_PROPOSAL_FAILED); + } + + // Remove obsolete kicks + for (LfgPlayerBootContainer::iterator it = BootsStore.begin(); it != BootsStore.end();) + { + LfgPlayerBootContainer::iterator itBoot = it++; + LfgPlayerBoot& boot = itBoot->second; + if (boot.cancelTime < currTime) + { + boot.inProgress = false; + for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) + { + uint64 pguid = itVotes->first; + if (pguid != boot.victim) + SendLfgBootProposalUpdate(pguid, boot); + SetState(pguid, LFG_STATE_DUNGEON); + } + SetState(itBoot->first, LFG_STATE_DUNGEON); + BootsStore.erase(itBoot); + } + } + } + else if (task == 1) + { + this->lastProposalId = m_lfgProposalId; // pussywizard: task 2 is done independantly, store previous value in LFGMgr for future use + uint8 newGroupsProcessed = 0; + // Check if a proposal can be formed with the new groups being added + for (LfgQueueContainer::iterator it = QueuesStore.begin(); it != QueuesStore.end(); ++it) + { + newGroupsProcessed += it->second.FindGroups(); + if (newGroupsProcessed) + break; + } + + // Update all players status queue info + if (!newGroupsProcessed) // don't do this on updates that precessed groups (performance) + for (LfgQueueContainer::iterator it = QueuesStore.begin(); it != QueuesStore.end(); ++it) + it->second.UpdateQueueTimers(tdiff); + } + else if (task == 2) + { + if (lastProposalId != m_lfgProposalId) + { + // pussywizard: only one proposal can be created in World::Update (during maps update), and it has id == m_lfgProposalId, so try to find only that one, dunno why for loop here xD + for (LfgProposalContainer::const_iterator itProposal = ProposalsStore.find(m_lfgProposalId); itProposal != ProposalsStore.end(); ++itProposal) + { + uint32 proposalId = itProposal->first; + LfgProposal& proposal = ProposalsStore[proposalId]; + + uint64 guid = 0; + for (LfgProposalPlayerContainer::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers) + { + guid = itPlayers->first; + SetState(guid, LFG_STATE_PROPOSAL); + if (uint64 gguid = GetGroup(guid)) + { + SetState(gguid, LFG_STATE_PROPOSAL); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid))); + } + else + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid))); + SendLfgUpdateProposal(guid, proposal); + } + + if (proposal.state == LFG_PROPOSAL_SUCCESS) // pussywizard: no idea what's the purpose of this xD + UpdateProposal(proposalId, guid, true); + } + } + + UpdateRaidBrowser(tdiff); + } +} + +/** + Generate the dungeon lock map for a given player + + @param[in] player Player we need to initialize the lock status map +*/ +void LFGMgr::InitializeLockedDungeons(Player* player, uint8 level /* = 0 */) +{ + uint64 guid = player->GetGUID(); + if (!level) + level = player->getLevel(); + uint8 expansion = player->GetSession()->Expansion(); + LfgDungeonSet const& dungeons = GetDungeonsByRandom(0); + LfgLockMap lock; + + float avgItemLevel = player->GetAverageItemLevelForDF(); + + for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end(); ++it) + { + LFGDungeonData const* dungeon = GetLFGDungeon(*it); + if (!dungeon) // should never happen - We provide a list from sLFGDungeonStore + continue; + MapEntry const* mapEntry = sMapStore.LookupEntry(dungeon->map); + + uint32 lockData = 0; + if (dungeon->expansion > expansion) + lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; + else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player)) + lockData = LFG_LOCKSTATUS_RAID_LOCKED; + else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && (!mapEntry || !mapEntry->IsRaid()) && sInstanceSaveMgr->PlayerIsPermBoundToInstance(player->GetGUIDLow(), dungeon->map, Difficulty(dungeon->difficulty))) + lockData = LFG_LOCKSTATUS_RAID_LOCKED; + else if (dungeon->minlevel > level) + lockData = LFG_LOCKSTATUS_TOO_LOW_LEVEL; + else if (dungeon->maxlevel < level) + lockData = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; + else if (dungeon->seasonal && !IsSeasonActive(dungeon->id)) + lockData = LFG_LOCKSTATUS_NOT_IN_SEASON; + else if (AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty))) + { + if (ar->achievement && !player->HasAchieved(ar->achievement)) + lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT; + else if (ar->reqItemLevel && (float)ar->reqItemLevel > avgItemLevel) + lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; + else if (player->GetTeamId() == TEAM_ALLIANCE && ar->quest_A && !player->GetQuestRewardStatus(ar->quest_A)) + lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; + else if (player->GetTeamId() == TEAM_HORDE && ar->quest_H && !player->GetQuestRewardStatus(ar->quest_H)) + lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; + else + if (ar->item) + { + if (!player->HasItemCount(ar->item) && (!ar->item2 || !player->HasItemCount(ar->item2))) + lockData = LFG_LOCKSTATUS_MISSING_ITEM; + } + else if (ar->item2 && !player->HasItemCount(ar->item2)) + lockData = LFG_LOCKSTATUS_MISSING_ITEM; + } + + /* TODO VoA closed if WG is not under team control (LFG_LOCKSTATUS_RAID_LOCKED) + lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; + lockData = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; + lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; + lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; + */ + + if (lockData) + lock[dungeon->Entry()] = lockData; + } + SetLockedDungeons(guid, lock); +} + +/** + Adds the player/group to lfg queue. If player is in a group then it is the leader + of the group tying to join the group. Join conditions are checked before adding + to the new queue. + + @param[in] player Player trying to join (or leader of group trying to join) + @param[in] roles Player selected roles + @param[in] dungeons Dungeons the player/group is applying for + @param[in] comment Player selected comment +*/ +void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const std::string& comment) +{ + if (!player || dungeons.empty()) + return; + + Group* grp = player->GetGroup(); + uint64 guid = player->GetGUID(); + uint64 gguid = grp ? grp->GetGUID() : guid; + LfgJoinResultData joinData; + LfgGuidSet players; + uint32 rDungeonId = 0; + bool isContinue = grp && grp->isLFGGroup() && GetState(gguid) != LFG_STATE_FINISHED_DUNGEON; + + if (grp && (grp->isBGGroup() || grp->isBFGroup())) + return; + + // pussywizard: can't join LFG/LFR while using LFR + if (GetState(player->GetGUID()) == LFG_STATE_RAIDBROWSER) + { + LfgDungeonSet tmp; + SendRaidBrowserJoinedPacket(player, tmp, ""); // the df "eye" can disappear in various case, resend if needed + return; + } + + // Do not allow to change dungeon in the middle of a current dungeon + if (isContinue) + { + dungeons.clear(); + dungeons.insert(GetDungeon(gguid)); + } + + LfgState state = GetState(gguid); + switch (state) + { + case LFG_STATE_ROLECHECK: // if joining again during rolecheck (eg. many players clicked continue inside instance) + if (IS_GROUP_GUID(gguid)) + UpdateRoleCheck(gguid); // abort role check and remove from RoleChecksStore + break; + case LFG_STATE_QUEUED: // joining again while in a queue + { + LFGQueue& queue = GetQueue(gguid); + queue.RemoveFromQueue(gguid); + } + break; + case LFG_STATE_PROPOSAL: // if joining again during proposal + joinData.result = LFG_JOIN_INTERNAL_ERROR; + break; + case LFG_STATE_FINISHED_DUNGEON: + if (grp && grp->isLFGGroup()) + joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS; + break; + default: + break; + } + + // Check if all dungeons are valid + bool isRaid = false; + if (joinData.result == LFG_JOIN_OK) + { + bool isDungeon = false; + for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end() && joinData.result == LFG_JOIN_OK; ++it) + { + LfgType type = GetDungeonType(*it); + switch (type) + { + case LFG_TYPE_RANDOM: + if (dungeons.size() > 1) // Only allow 1 random dungeon + joinData.result = LFG_JOIN_DUNGEON_INVALID; + else + rDungeonId = (*dungeons.begin()); + // No break on purpose (Random can only be dungeon or heroic dungeon) + case LFG_TYPE_HEROIC: + case LFG_TYPE_DUNGEON: + if (isRaid) + joinData.result = LFG_JOIN_MIXED_RAID_DUNGEON; + isDungeon = true; + break; + case LFG_TYPE_RAID: + if (isDungeon) + joinData.result = LFG_JOIN_MIXED_RAID_DUNGEON; + isRaid = true; + break; + default: + sLog->outError("Wrong dungeon type %u for dungeon %u", type, *it); + joinData.result = LFG_JOIN_DUNGEON_INVALID; + break; + } + } + } + + if (!isRaid && joinData.result == LFG_JOIN_OK) + { + // Check player or group member restrictions + if (player->InBattleground() || player->InArena() || player->InBattlegroundQueue()) + joinData.result = LFG_JOIN_USING_BG_SYSTEM; + else if (player->HasAura(LFG_SPELL_DUNGEON_DESERTER)) + joinData.result = LFG_JOIN_DESERTER; + else if (dungeons.empty()) + joinData.result = LFG_JOIN_NOT_MEET_REQS; + else if (grp) + { + if (grp->GetMembersCount() > MAXGROUPSIZE) + joinData.result = LFG_JOIN_TOO_MUCH_MEMBERS; + else + { + uint8 memberCount = 0; + for (GroupReference* itr = grp->GetFirstMember(); itr != NULL && joinData.result == LFG_JOIN_OK; itr = itr->next()) + { + if (Player* plrg = itr->GetSource()) + { + if (plrg->HasAura(LFG_SPELL_DUNGEON_DESERTER)) + joinData.result = LFG_JOIN_PARTY_DESERTER; + else if (plrg->InBattleground() || plrg->InArena() || plrg->InBattlegroundQueue()) + joinData.result = LFG_JOIN_USING_BG_SYSTEM; + ++memberCount; + players.insert(plrg->GetGUID()); + } + } + + if (joinData.result == LFG_JOIN_OK && memberCount != grp->GetMembersCount()) + joinData.result = LFG_JOIN_DISCONNECTED; + } + } + else + players.insert(player->GetGUID()); + + // Xinef: Check dungeon cooldown only for random dungeons + // Xinef: Moreover check this only if dungeon is not started, afterwards its obvious that players will have the cooldown + if (joinData.result == LFG_JOIN_OK && !isContinue && rDungeonId) + { + if (player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) // xinef: added !isContinue + joinData.result = LFG_JOIN_RANDOM_COOLDOWN; + else if (grp) + { + for (GroupReference* itr = grp->GetFirstMember(); itr != NULL && joinData.result == LFG_JOIN_OK; itr = itr->next()) + if (Player* plrg = itr->GetSource()) + if (plrg->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) // xinef: added !isContinue + joinData.result = LFG_JOIN_PARTY_RANDOM_COOLDOWN; + } + } + } + + if (isRaid) + players.insert(player->GetGUID()); + + if (joinData.result == LFG_JOIN_OK) + { + // Expand random dungeons and check restrictions + if (rDungeonId) + dungeons = GetDungeonsByRandom(rDungeonId); + + // if we have lockmap then there are no compatible dungeons + // xinef: dont check compatibile dungeons for already running group (bind problems) + if (!isContinue) + { + GetCompatibleDungeons(dungeons, players, joinData.lockmap); + if (dungeons.empty()) + joinData.result = grp ? LFG_JOIN_PARTY_NOT_MEET_REQS : LFG_JOIN_NOT_MEET_REQS; + } + } + + // pussywizard: + if (isRaid && grp && (grp->isLFGGroup() || guid != grp->GetLeaderGUID())) + return; + + // Can't join. Send result + if (joinData.result != LFG_JOIN_OK) + { + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::Join: [" UI64FMTD "] joining with %u members. result: %u", guid, grp ? grp->GetMembersCount() : 1, joinData.result); + if (!dungeons.empty()) // Only should show lockmap when have no dungeons available + joinData.lockmap.clear(); + player->GetSession()->SendLfgJoinResult(joinData); + return; + } + + SetComment(guid, comment); + + if (isRaid) + { + if (grp) + roles = PLAYER_ROLE_LEADER; + else + roles &= (PLAYER_ROLE_TANK | PLAYER_ROLE_HEALER | PLAYER_ROLE_DAMAGE); + if (!roles) + return; + JoinRaidBrowser(player, roles, dungeons, comment); + SetState(guid, LFG_STATE_RAIDBROWSER); + SendRaidBrowserJoinedPacket(player, dungeons, comment); + return; + } + + std::string debugNames = ""; + if (grp) // Begin rolecheck + { + // Create new rolecheck + LfgRoleCheck& roleCheck = RoleChecksStore[gguid]; + roleCheck.roles.clear(); // pussywizard: NEW rolecheck, not old one with trash data >_> + roleCheck.cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK; + roleCheck.state = LFG_ROLECHECK_INITIALITING; + roleCheck.leader = guid; + roleCheck.dungeons = dungeons; + roleCheck.rDungeonId = rDungeonId; + + if (rDungeonId) + { + dungeons.clear(); + dungeons.insert(rDungeonId); + } + + SetState(gguid, LFG_STATE_ROLECHECK); + // Send update to player + LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_JOIN_QUEUE, dungeons, comment); + for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + if (Player* plrg = itr->GetSource()) + { + uint64 pguid = plrg->GetGUID(); + plrg->GetSession()->SendLfgUpdateParty(updateData); + SetState(pguid, LFG_STATE_ROLECHECK); + if (!isContinue) + SetSelectedDungeons(pguid, dungeons); + roleCheck.roles[pguid] = 0; + if (!debugNames.empty()) + debugNames.append(", "); + debugNames.append(plrg->GetName()); + } + } + // Update leader role + UpdateRoleCheck(gguid, guid, roles); + } + else // Add player to queue + { + LfgRolesMap rolesMap; + rolesMap[guid] = roles; + LFGQueue& queue = GetQueue(guid); + queue.AddQueueData(guid, time(NULL), dungeons, rolesMap); + + if (!isContinue) + { + if (rDungeonId) + { + dungeons.clear(); + dungeons.insert(rDungeonId); + } + SetSelectedDungeons(guid, dungeons); + } + // Send update to player + player->GetSession()->SendLfgJoinResult(joinData); + player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_JOIN_QUEUE, dungeons, comment)); + SetState(guid, LFG_STATE_QUEUED); + SetRoles(guid, roles); + debugNames.append(player->GetName()); + } + + /*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG)) + { + std::ostringstream o; + o << "LFGMgr::Join: [" << guid << "] joined (" << (grp ? "group" : "player") << ") Members: " << debugNames.c_str() + << ". Dungeons (" << uint32(dungeons.size()) << "): " << ConcatenateDungeons(dungeons); + ;//sLog->outDebug((LOG_FILTER_LFG, "%s", o.str().c_str()); + }*/ +} + +/** + Leaves Dungeon System. Player/Group is removed from queue, rolechecks, proposals + or votekicks. Player or group needs to be not NULL and using Dungeon System + + @param[in] guid Player or group guid +*/ +void LFGMgr::LeaveLfg(uint64 guid) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::Leave: [" UI64FMTD "]", guid); + + uint64 gguid = IS_GROUP_GUID(guid) ? guid : GetGroup(guid); + LfgState state = GetState(guid); + switch (state) + { + case LFG_STATE_QUEUED: + if (gguid) + { + LFGQueue& queue = GetQueue(gguid); + queue.RemoveFromQueue(gguid); + SetState(gguid, LFG_STATE_NONE); + const LfgGuidSet& players = GetPlayers(gguid); + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + { + SetState(*it, LFG_STATE_NONE); + SendLfgUpdateParty(*it, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + } + } + else + { + LFGQueue& queue = GetQueue(guid); + queue.RemoveFromQueue(guid); + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + SetState(guid, LFG_STATE_NONE); + } + break; + case LFG_STATE_ROLECHECK: + if (gguid) + UpdateRoleCheck(gguid); // No player to update role = LFG_ROLECHECK_ABORTED + break; + case LFG_STATE_PROPOSAL: + { + // Remove from Proposals + LfgProposalContainer::iterator it = ProposalsStore.begin(); + uint64 pguid = gguid == guid ? GetLeader(gguid) : guid; + while (it != ProposalsStore.end()) + { + LfgProposalPlayerContainer::iterator itPlayer = it->second.players.find(pguid); + if (itPlayer != it->second.players.end()) + { + // Mark the player/leader of group who left as didn't accept the proposal + itPlayer->second.accept = LFG_ANSWER_DENY; + break; + } + ++it; + } + + // Remove from queue - if proposal is found, RemoveProposal will call RemoveFromQueue + if (it != ProposalsStore.end()) + RemoveProposal(it, LFG_UPDATETYPE_PROPOSAL_DECLINED); + break; + } + case LFG_STATE_NONE: + break; + case LFG_STATE_DUNGEON: + case LFG_STATE_FINISHED_DUNGEON: + case LFG_STATE_BOOT: + if (guid != gguid) // Player + SetState(guid, LFG_STATE_NONE); + break; + case LFG_STATE_RAIDBROWSER: + LeaveRaidBrowser(guid); + SetCanOverrideRBState(guid, true); + SetState(guid, LFG_STATE_NONE); + SetCanOverrideRBState(guid, false); + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_LEAVE_RAIDBROWSER)); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_LEAVE_RAIDBROWSER)); + break; + } +} + +void LFGMgr::JoinRaidBrowser(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string comment) +{ + // pussywizard: client limit for comment length is 64 @ 3.3.5a + if (comment.size() > 64) + comment = comment.substr(0, 64); + + RBEntryInfo entry(roles, comment); + for (LfgDungeonSet::const_iterator itr = dungeons.begin(); itr != dungeons.end(); ++itr) + if (GetLFGDungeon(*itr)) // ensure dungeon data exists for such dungeon id + { + RaidBrowserStore[player->GetTeamId()][*itr][player->GetGUIDLow()] = entry; + RBUsedDungeonsStore[player->GetTeamId()].insert(*itr); + } +} + +void LFGMgr::LeaveRaidBrowser(uint64 guid) +{ + uint32 guidLow = GUID_LOPART(guid); + for (uint8 team=0; team<2; ++team) + for (RBStoreMap::iterator itr = RaidBrowserStore[team].begin(); itr != RaidBrowserStore[team].end(); ++itr) + itr->second.erase(guidLow); +} + +void LFGMgr::SendRaidBrowserJoinedPacket(Player* p, LfgDungeonSet& dungeons, std::string comment) +{ + if (dungeons.empty()) + { + RBEntryInfoMap::iterator iter; + uint8 team = p->GetTeamId(); + bool setComment = true; + for (RBStoreMap::iterator itr = RaidBrowserStore[team].begin(); itr != RaidBrowserStore[team].end(); ++itr) + if ((iter = itr->second.find(p->GetGUIDLow())) != itr->second.end()) + { + dungeons.insert(itr->first); + if (setComment) + { + comment = iter->second.comment; + setComment = false; + } + } + } + LfgJoinResultData joinData; + p->GetSession()->SendLfgJoinResult(joinData); + LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_JOIN_RAIDBROWSER, dungeons, comment); + if (p->GetGroup()) + p->GetSession()->SendLfgUpdateParty(updateData); + else + p->GetSession()->SendLfgUpdatePlayer(updateData); +} + +void LFGMgr::LfrSearchAdd(Player* p, uint32 dungeonId) +{ + RBSearchersStore[p->GetTeamId()][p->GetGUIDLow()] = dungeonId; +} + +void LFGMgr::LfrSearchRemove(Player* p) +{ + RBSearchersStore[p->GetTeamId()].erase(p->GetGUIDLow()); +} + +void LFGMgr::SendRaidBrowserCachedList(Player* player, uint32 dungeonId) +{ + RBCacheMap::iterator itr = RBCacheStore[player->GetTeamId()].find(dungeonId); + if (itr != RBCacheStore[player->GetTeamId()].end()) + { + player->GetSession()->SendPacket(&(itr->second)); + return; + } + // send empty packet if cache not found + WorldPacket data(SMSG_UPDATE_LFG_LIST, 1000); + data << (uint32)LFG_TYPE_RAID; + data << (uint32)dungeonId; + data << (uint8)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + player->GetSession()->SendPacket(&data); +} + +void LFGMgr::UpdateRaidBrowser(uint32 diff) +{ + for (uint8 team=0; team<2; ++team) + { + if (m_raidBrowserUpdateTimer[team] > diff) + m_raidBrowserUpdateTimer[team] -= diff; + else + m_raidBrowserUpdateTimer[team] = 0; + } + + if (getMSTimeDiff(World::GetGameTimeMS(), getMSTime()) > (70*7)/5) // prevent lagging + return; + + uint64 guid, groupGuid, instanceGuid; + uint8 level, Class, race, talents[3]; + float iLevel, mp5, mp5combat, baseAP, rangedAP; + int32 spellDamage, spellHeal; + uint32 dungeonId, encounterMask, maxPower; + uint32 deletedCounter, groupCounter, playerCounter; + ByteBuffer buffer_deleted, buffer_groups, buffer_players; + std::string emptyComment; + std::set<uint64> deletedGroups, deletedGroupsToErase; + RBInternalInfoMap copy; + + for (uint8 team=0; team<2; ++team) + { + if (m_raidBrowserLastUpdatedDungeonId[team] == 0) // new loop + { + if (m_raidBrowserUpdateTimer[team] > 0) // allowed only with some time interval + continue; + else // reset timer + m_raidBrowserUpdateTimer[team] = 5000; + } + + RBUsedDungeonsSet::const_iterator neitr, titr; + for (neitr = RBUsedDungeonsStore[team].begin(); neitr != RBUsedDungeonsStore[team].end(); ) + { + titr = neitr++; + dungeonId = (*titr); + + // go to next dungeon than previously (one dungeon updated in one LFGMgr::UpdateRaidBrowser) + if (dungeonId <= m_raidBrowserLastUpdatedDungeonId[team]) + continue; + m_raidBrowserLastUpdatedDungeonId[team] = dungeonId; + + RBEntryInfoMap& entryInfoMap = RaidBrowserStore[team][dungeonId]; + LFGDungeonData const* dungeonData = GetLFGDungeon(dungeonId); // checked if exists before inserting to the container + RBInternalInfoMap& currInternalInfoMap = RBInternalInfoStoreCurr[team][dungeonId]; + for (RBEntryInfoMap::const_iterator sitr = entryInfoMap.begin(); sitr != entryInfoMap.end(); ++sitr) + { + guid = MAKE_NEW_GUID(sitr->first, 0, HIGHGUID_PLAYER); + groupGuid = 0; + Player* p = ObjectAccessor::FindPlayerInOrOutOfWorld(guid); + ASSERT(p); + if (sitr->second.roles == PLAYER_ROLE_LEADER) + { + ASSERT(p->GetGroup()); + groupGuid = p->GetGroup()->GetGUID(); + } + encounterMask = 0; + instanceGuid = 0; + if (InstancePlayerBind* bind = sInstanceSaveMgr->PlayerGetBoundInstance(sitr->first, dungeonData->map, dungeonData->difficulty)) + if (bind->perm) + { + instanceGuid = MAKE_NEW_GUID(bind->save->GetInstanceId(), 0, HIGHGUID_INSTANCE); + encounterMask = bind->save->GetCompletedEncounterMask(); + } + + talents[0] = 0; + talents[1] = 0; + talents[2] = 0; + p->GetTalentTreePoints(talents); + spellDamage = p->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_ALL); + spellHeal = p->SpellBaseHealingBonusDone(SPELL_SCHOOL_MASK_ALL); + mp5 = p->GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER); + mp5combat = p->GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER); + baseAP = p->GetTotalAttackPowerValue(BASE_ATTACK); + rangedAP = p->GetTotalAttackPowerValue(RANGED_ATTACK); + maxPower = 0; + if (p->getClass() == CLASS_DRUID) + maxPower = p->GetMaxPower(POWER_MANA); + else + maxPower = (p->getPowerType() == POWER_RAGE || p->getPowerType() == POWER_RUNIC_POWER) ? p->GetMaxPower(p->getPowerType())/10 : p->GetMaxPower(p->getPowerType()); + + currInternalInfoMap[sitr->first] = RBInternalInfo(guid, sitr->second.comment, groupGuid != 0, groupGuid, sitr->second.roles, encounterMask, instanceGuid, + 1, p->getLevel(), p->getClass(), p->getRace(), p->GetAverageItemLevel(), + talents, p->m_last_area_id, p->GetArmor(), (uint32)std::max<int32>(0, spellDamage), (uint32)std::max<int32>(0, spellHeal), + p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_CRIT_MELEE), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_CRIT_RANGED), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_CRIT_SPELL), std::max<float>(0.0f, mp5), std::max<float>(0.0f, mp5combat), + std::max<uint32>(baseAP, rangedAP), (uint32)p->GetStat(STAT_AGILITY), p->GetMaxHealth(), maxPower, p->GetDefenseSkillValue(), + p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_DODGE), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_BLOCK), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_PARRY), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_HASTE_SPELL), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_EXPERTISE)); + + if (!groupGuid) + continue; + for (Group::member_citerator mitr = p->GetGroup()->GetMemberSlots().begin(); mitr != p->GetGroup()->GetMemberSlots().end(); ++mitr) + { + if (mitr->guid == sitr->first) // leader already added + continue; + guid = MAKE_NEW_GUID(mitr->guid, 0, HIGHGUID_PLAYER); + level = 1; + Class = 0; + race = 0; + iLevel = 0.0f; + talents[0] = 0; + talents[1] = 0; + talents[2] = 0; + if (const GlobalPlayerData* gpd = sWorld->GetGlobalPlayerData(mitr->guid)) + { + level = gpd->level; + Class = gpd->playerClass; + race = gpd->race; + } + Player* mplr = ObjectAccessor::FindPlayerInOrOutOfWorld(guid); + if (mplr) + { + iLevel = mplr->GetAverageItemLevel(); + mplr->GetTalentTreePoints(talents); + } + currInternalInfoMap[mitr->guid] = RBInternalInfo(guid, emptyComment, false, groupGuid, 0, 0, 0, + (mplr ? 1 : 0), level, Class, race, iLevel, + talents, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0); + } + } + + copy.clear(); + copy = currInternalInfoMap; // will be saved as prev at the end + + // compare prev with curr to build difference packet + deletedCounter = 0; groupCounter = 0; playerCounter = 0; + buffer_deleted.clear(); buffer_groups.clear(); buffer_players.clear(); + deletedGroups.clear(); deletedGroupsToErase.clear(); + + RBInternalInfoMap& prevInternalInfoMap = RBInternalInfoStorePrev[team][dungeonId]; + RBInternalInfoMap::iterator iter, iterTmp; + for (RBInternalInfoMap::const_iterator sitr = prevInternalInfoMap.begin(); sitr != prevInternalInfoMap.end(); ++sitr) + { + iter = currInternalInfoMap.find(sitr->first); + if (iter == currInternalInfoMap.end()) // was -> isn't + { + if (sitr->second.isGroupLeader) + deletedGroups.insert(sitr->second.groupGuid); + ++deletedCounter; + buffer_deleted << (uint64)sitr->second.guid; + } + else // was -> is + { + if (sitr->second.isGroupLeader) // was a leader + { + if (!iter->second.isGroupLeader) // leader -> no longer a leader + deletedGroups.insert(sitr->second.groupGuid); + else if (sitr->second.groupGuid != iter->second.groupGuid) // leader -> leader of another group + { + deletedGroups.insert(sitr->second.groupGuid); + deletedGroupsToErase.insert(iter->second.groupGuid); + ++groupCounter; + RBPacketAppendGroup(iter->second, buffer_groups); + } + else if (sitr->second.comment != iter->second.comment || sitr->second.encounterMask != iter->second.encounterMask || sitr->second.instanceGuid != iter->second.instanceGuid) // leader -> nothing changed + { + ++groupCounter; + RBPacketAppendGroup(iter->second, buffer_groups); + } + } + else if (iter->second.isGroupLeader) // wasn't a leader -> is a leader + { + deletedGroupsToErase.insert(iter->second.groupGuid); + ++groupCounter; + RBPacketAppendGroup(iter->second, buffer_groups); + } + + if (!iter->second._online) // if offline, copy previous stats (itemLevel, talents, area, etc.) + { + iterTmp = copy.find(sitr->first); // copied container is for building a full packet, so modify it there (currInternalInfoMap is erased) + iterTmp->second.CopyStats(sitr->second); + if (!sitr->second.PlayerSameAs(iterTmp->second)) // player info changed + { + ++playerCounter; + RBPacketAppendPlayer(iterTmp->second, buffer_players); + } + } + else if (!sitr->second.PlayerSameAs(iter->second)) // player info changed + { + ++playerCounter; + RBPacketAppendPlayer(iter->second, buffer_players); + } + currInternalInfoMap.erase(iter); + } + } + // left entries (new) + for (RBInternalInfoMap::const_iterator sitr = currInternalInfoMap.begin(); sitr != currInternalInfoMap.end(); ++sitr) + { + if (sitr->second.isGroupLeader) + { + deletedGroupsToErase.insert(sitr->second.groupGuid); + ++groupCounter; + RBPacketAppendGroup(sitr->second, buffer_groups); + } + ++playerCounter; + RBPacketAppendPlayer(sitr->second, buffer_players); + } + + if (!deletedGroupsToErase.empty()) + for (std::set<uint64>::const_iterator sitr = deletedGroupsToErase.begin(); sitr != deletedGroupsToErase.end(); ++sitr) + deletedGroups.erase(*sitr); + + if (!deletedGroups.empty()) + for (std::set<uint64>::const_iterator sitr = deletedGroups.begin(); sitr != deletedGroups.end(); ++sitr) + { + ++deletedCounter; + buffer_deleted << (*sitr); + } + + WorldPacket differencePacket(SMSG_UPDATE_LFG_LIST, 1000); + RBPacketBuildDifference(differencePacket, dungeonId, deletedCounter, buffer_deleted, groupCounter, buffer_groups, playerCounter, buffer_players); + WorldPacket fullPacket(SMSG_UPDATE_LFG_LIST, 1000); + RBPacketBuildFull(fullPacket, dungeonId, copy); + + RBCacheStore[team][dungeonId] = fullPacket; + prevInternalInfoMap = copy; + currInternalInfoMap.clear(); + + if (entryInfoMap.empty()) + RBUsedDungeonsStore[team].erase(titr); + + // send difference packet to browsing players + for (RBSearchersMap::const_iterator sitr = RBSearchersStore[team].begin(); sitr != RBSearchersStore[team].end(); ++sitr) + if (sitr->second == dungeonId) + if (Player* p = ObjectAccessor::FindPlayerInOrOutOfWorld(MAKE_NEW_GUID(sitr->first, 0, HIGHGUID_PLAYER))) + p->GetSession()->SendPacket(&differencePacket); + + break; // one dungeon updated in one LFGMgr::UpdateRaidBrowser + } + + // already updated all in this time interval + if (neitr == RBUsedDungeonsStore[team].end()) + m_raidBrowserLastUpdatedDungeonId[team] = 0; + } +} + +void LFGMgr::RBPacketAppendGroup(const RBInternalInfo& info, ByteBuffer& buffer) +{ + buffer << (uint64)info.groupGuid; + uint32 flags = LFG_UPDATE_FLAG_COMMENT | LFG_UPDATE_FLAG_ROLES | LFG_UPDATE_FLAG_BINDED; + buffer << (uint32)flags; + if (flags & LFG_UPDATE_FLAG_COMMENT) + buffer << info.comment; + if (flags & LFG_UPDATE_FLAG_ROLES) + for (uint8 j=0; j<3; ++j) + buffer << (uint8)0; + if (!(flags & LFG_UPDATE_FLAG_BINDED)) + return; + buffer << (uint64)info.instanceGuid; + buffer << (uint32)info.encounterMask; +} + +void LFGMgr::RBPacketAppendPlayer(const RBInternalInfo& info, ByteBuffer& buffer) +{ + buffer << (uint64)info.guid; + uint32 flags = LFG_UPDATE_FLAG_CHARACTERINFO | LFG_UPDATE_FLAG_ROLES | LFG_UPDATE_FLAG_COMMENT | (info.groupGuid ? LFG_UPDATE_FLAG_GROUPGUID : LFG_UPDATE_FLAG_BINDED) | (info.isGroupLeader ? LFG_UPDATE_FLAG_GROUPLEADER : 0) | (!info.groupGuid || info.isGroupLeader ? LFG_UPDATE_FLAG_AREA : 0); + buffer << (uint32)flags; + + if (flags & LFG_UPDATE_FLAG_CHARACTERINFO) + { + buffer << (uint8)info._level; + buffer << (uint8)info._class; + buffer << (uint8)info._race; + buffer << (uint8)info._talents0; + buffer << (uint8)info._talents1; + buffer << (uint8)info._talents2; + buffer << (uint32)info._armor; + buffer << (uint32)info._spellDamage; + buffer << (uint32)info._spellHeal; + buffer << (uint32)info._critRatingMelee; + buffer << (uint32)info._critRatingRanged; + buffer << (uint32)info._critRatingSpell; + buffer << (float)info._mp5; + buffer << (float)info._mp5combat; + buffer << (uint32)info._attackPower; + buffer << (uint32)info._agility; + buffer << (uint32)info._health; + buffer << (uint32)info._mana; + buffer << (uint32)info._online; // talentpoints, used as online/offline marker :D + buffer << (float)info._avgItemLevel; // avgitemlevel + buffer << (uint32)info._defenseSkill; + buffer << (uint32)info._dodgeRating; + buffer << (uint32)info._blockRating; + buffer << (uint32)info._parryRating; + buffer << (uint32)info._hasteRating; + buffer << (uint32)info._expertiseRating; + } + + if (flags & LFG_UPDATE_FLAG_COMMENT) + buffer << (info.groupGuid ? std::string("") : info.comment); + if (flags & LFG_UPDATE_FLAG_GROUPLEADER) + buffer << (uint8)1; // isLFM + if (flags & LFG_UPDATE_FLAG_GROUPGUID) + buffer << (uint64)info.groupGuid; + if (flags & LFG_UPDATE_FLAG_ROLES) + buffer << (uint8)(info.groupGuid ? (info.isGroupLeader ? PLAYER_ROLE_LEADER : 0) : info.roles); + if (flags & LFG_UPDATE_FLAG_AREA) + buffer << (uint32)info._area; + if (flags & LFG_UPDATE_FLAG_STATUS) + buffer << (uint8)0; + if (!(flags & LFG_UPDATE_FLAG_BINDED)) + return; + buffer << (uint64)info.instanceGuid; + buffer << (uint32)info.encounterMask; +} + +void LFGMgr::RBPacketBuildDifference(WorldPacket& differencePacket, uint32 dungeonId, uint32 deletedCounter, ByteBuffer& buffer_deleted, uint32 groupCounter, ByteBuffer& buffer_groups, uint32 playerCounter, ByteBuffer& buffer_players) +{ + differencePacket << (uint32)LFG_TYPE_RAID; + differencePacket << (uint32)dungeonId; + differencePacket << (uint8)1; + differencePacket << (uint32)deletedCounter; + differencePacket.append(buffer_deleted); + differencePacket << (uint32)groupCounter; + differencePacket << (uint32)0; + differencePacket.append(buffer_groups); + differencePacket << (uint32)playerCounter; + differencePacket << (uint32)0; + differencePacket.append(buffer_players); +} + +void LFGMgr::RBPacketBuildFull(WorldPacket& fullPacket, uint32 dungeonId, RBInternalInfoMap& infoMap) +{ + fullPacket << (uint32)LFG_TYPE_RAID; + fullPacket << (uint32)dungeonId; + fullPacket << (uint8)0; + uint32 groupCounter = 0, playerCounter = 0; + ByteBuffer buffer_groups, buffer_players; + for (RBInternalInfoMap::const_iterator itr = infoMap.begin(); itr != infoMap.end(); ++itr) + { + if (itr->second.isGroupLeader) + { + ++groupCounter; + RBPacketAppendGroup(itr->second, buffer_groups); + } + ++playerCounter; + RBPacketAppendPlayer(itr->second, buffer_players); + } + fullPacket << (uint32)groupCounter; + fullPacket << (uint32)0; + fullPacket.append(buffer_groups); + fullPacket << (uint32)playerCounter; + fullPacket << (uint32)0; + fullPacket.append(buffer_players); +} + +// pussywizard: +void LFGMgr::LeaveAllLfgQueues(uint64 guid, bool allowgroup, uint64 groupguid) +{ + uint64 pguid = 0, gguid = 0; + if (IS_GROUP_GUID(guid)) + gguid = guid; + else if (groupguid && IS_GROUP_GUID(groupguid)) + { + pguid = guid; + gguid = groupguid; + } + else + { + pguid = guid; + gguid = GetGroup(guid); + } + if (!allowgroup) + gguid = 0; + + if (pguid) + for (lfg::LfgQueueContainer::iterator itr = QueuesStore.begin(); itr != QueuesStore.end(); ++itr) + itr->second.RemoveFromQueue(pguid); + if (gguid) + for (lfg::LfgQueueContainer::iterator itr = QueuesStore.begin(); itr != QueuesStore.end(); ++itr) + itr->second.RemoveFromQueue(gguid); + + if (pguid && !gguid) + { + if (GetState(pguid) == LFG_STATE_QUEUED) + { + SendLfgUpdatePlayer(pguid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + SetState(pguid, LFG_STATE_NONE); + } + } + if (gguid) + { + if (GetState(gguid) == LFG_STATE_QUEUED) + { + SetState(gguid, LFG_STATE_NONE); + const LfgGuidSet& players = GetPlayers(gguid); + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + { + SetState(*it, LFG_STATE_NONE); + SendLfgUpdateParty(*it, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + } + } + } +} + +/** + Update the Role check info with the player selected role. + + @param[in] grp Group guid to update rolecheck + @param[in] guid Player guid (0 = rolecheck failed) + @param[in] roles Player selected roles +*/ +void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* = PLAYER_ROLE_NONE */) +{ + if (!gguid) + return; + + LfgRolesMap check_roles; + LfgRoleCheckContainer::iterator itRoleCheck = RoleChecksStore.find(gguid); + if (itRoleCheck == RoleChecksStore.end()) + return; + + LfgRoleCheck& roleCheck = itRoleCheck->second; + bool sendRoleChosen = roleCheck.state != LFG_ROLECHECK_DEFAULT && guid; + + if (!guid) + roleCheck.state = LFG_ROLECHECK_ABORTED; + else if (roles < PLAYER_ROLE_TANK) // Player selected no role. + roleCheck.state = LFG_ROLECHECK_NO_ROLE; + else + { + roleCheck.roles[guid] = roles; + + // Check if all players have selected a role + LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); + while (itRoles != roleCheck.roles.end() && itRoles->second != PLAYER_ROLE_NONE) + ++itRoles; + + if (itRoles == roleCheck.roles.end()) + { + // use temporal var to check roles, CheckGroupRoles modifies the roles + check_roles = roleCheck.roles; + roleCheck.state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES; + } + } + + LfgDungeonSet dungeons; + if (roleCheck.rDungeonId) + dungeons.insert(roleCheck.rDungeonId); + else + dungeons = roleCheck.dungeons; + + LfgJoinResultData joinData = LfgJoinResultData(LFG_JOIN_FAILED, roleCheck.state); + for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it) + { + uint64 pguid = it->first; + + if (sendRoleChosen) + SendLfgRoleChosen(pguid, guid, roles); + + SendLfgRoleCheckUpdate(pguid, roleCheck); + switch (roleCheck.state) + { + case LFG_ROLECHECK_INITIALITING: + continue; + case LFG_ROLECHECK_FINISHED: + SetState(pguid, LFG_STATE_QUEUED); + SetRoles(pguid, it->second); + SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid))); + break; + default: + if (roleCheck.leader == pguid) + SendLfgJoinResult(pguid, joinData); + SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ROLECHECK_FAILED)); + RestoreState(pguid, "Rolecheck Failed"); + break; + } + } + + if (roleCheck.state == LFG_ROLECHECK_FINISHED) + { + SetState(gguid, LFG_STATE_QUEUED); + LFGQueue& queue = GetQueue(gguid); + queue.AddQueueData(gguid, time_t(time(NULL)), roleCheck.dungeons, roleCheck.roles); + RoleChecksStore.erase(itRoleCheck); + } + else if (roleCheck.state != LFG_ROLECHECK_INITIALITING) + { + RestoreState(gguid, "Rolecheck Failed"); + RoleChecksStore.erase(itRoleCheck); + } +} + +/** + Given a list of dungeons remove the dungeons players have restrictions. + + @param[in, out] dungeons Dungeons to check restrictions + @param[in] players Set of players to check their dungeon restrictions + @param[out] lockMap Map of players Lock status info of given dungeons (Empty if dungeons is not empty) +*/ +void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap) +{ + lockMap.clear(); + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it) + { + uint64 guid = (*it); + LfgLockMap const& cachedLockMap = GetLockedDungeons(guid); + for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2) + { + uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids + LfgDungeonSet::iterator itDungeon = dungeons.find(dungeonId); + if (itDungeon != dungeons.end()) + { + dungeons.erase(itDungeon); + lockMap[guid][dungeonId] = it2->second; + } + } + } + if (!dungeons.empty()) + lockMap.clear(); +} + +uint8 LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true*/) +{ + if (groles.empty()) + return 0; + + uint8 damage = 0; + uint8 tank = 0; + uint8 healer = 0; + + if (removeLeaderFlag) + for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) + it->second &= ~PLAYER_ROLE_LEADER; + + for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) + { + if (it->second == PLAYER_ROLE_NONE) + return 0; + + if (it->second & PLAYER_ROLE_DAMAGE) + { + if (it->second != PLAYER_ROLE_DAMAGE) + { + it->second -= PLAYER_ROLE_DAMAGE; + if (uint8 x = CheckGroupRoles(groles, false)) + return x; + it->second += PLAYER_ROLE_DAMAGE; + } + else if (damage == LFG_DPS_NEEDED) + return 0; + else + damage++; + } + + if (it->second & PLAYER_ROLE_HEALER) + { + if (it->second != PLAYER_ROLE_HEALER) + { + it->second -= PLAYER_ROLE_HEALER; + if (uint8 x = CheckGroupRoles(groles, false)) + return x; + it->second += PLAYER_ROLE_HEALER; + } + else if (healer == LFG_HEALERS_NEEDED) + return 0; + else + healer++; + } + + if (it->second & PLAYER_ROLE_TANK) + { + if (it->second != PLAYER_ROLE_TANK) + { + it->second -= PLAYER_ROLE_TANK; + if (uint8 x = CheckGroupRoles(groles, false)) + return x; + it->second += PLAYER_ROLE_TANK; + } + else if (tank == LFG_TANKS_NEEDED) + return 0; + else + tank++; + } + } + if ((tank + healer + damage) == uint8(groles.size())) + return (8*tank + 4*healer + damage); + return 0; +} + +/** + Makes a new group given a proposal + @param[in] proposal Proposal to get info from +*/ +void LFGMgr::MakeNewGroup(LfgProposal const& proposal) +{ + LfgGuidList players; + LfgGuidList playersToTeleport; + + for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + uint64 guid = it->first; + if (guid == proposal.leader) + players.push_front(guid); + else + players.push_back(guid); + + if (proposal.isNew || GetGroup(guid) != proposal.group) + playersToTeleport.push_back(guid); + } + + // Set the dungeon difficulty + LFGDungeonData const* dungeon = GetLFGDungeon(proposal.dungeonId); + ASSERT(dungeon); + + Group* grp = proposal.group ? sGroupMgr->GetGroupByGUID(GUID_LOPART(proposal.group)) : NULL; + uint64 oldGroupGUID = 0; + for (LfgGuidList::const_iterator it = players.begin(); it != players.end(); ++it) + { + uint64 pguid = (*it); + Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(pguid); + if (!player) + continue; + + Group* group = player->GetGroup(); + + // Xinef: Apply Random Buff + if (grp && !grp->IsLfgWithBuff()) + { + if (!group || group->GetGUID() != oldGroupGUID) + grp->AddLfgBuffFlag(); + else + oldGroupGUID = group->GetGUID(); + } + + // Xinef: Store amount of random players player grouped with + if (group) + { + SetRandomPlayersCount(pguid, group->GetMembersCount() >= MAXGROUPSIZE ? 0 : MAXGROUPSIZE-group->GetMembersCount()); + oldGroupGUID = group->GetGUID(); + if (group != grp) + group->RemoveMember(player->GetGUID()); + } + else + SetRandomPlayersCount(pguid, MAXGROUPSIZE-1); + + if (!grp) + { + grp = new Group(); + grp->ConvertToLFG(); + grp->Create(player); + uint64 gguid = grp->GetGUID(); + SetState(gguid, LFG_STATE_PROPOSAL); + sGroupMgr->AddGroup(grp); + } + else if (group != grp) + { + // pussywizard: + if (!grp->IsFull()) + grp->AddMember(player); + //else // some cleanup? LeaveLFG? + // ; + } + + grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role); + } + + // pussywizard: crashfix, group wasn't created when iterating players (no player found by guid), proposal is deleted by the calling function + if (!grp) + return; + + grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty)); + uint64 gguid = grp->GetGUID(); + SetDungeon(gguid, dungeon->Entry()); + SetState(gguid, LFG_STATE_DUNGEON); + + _SaveToDB(gguid); + + bool randomDungeon = false; + // Teleport Player + for (LfgGuidList::const_iterator it = playersToTeleport.begin(); it != playersToTeleport.end(); ++it) + if (Player* player = ObjectAccessor::FindPlayer(*it)) + { + if (player->GetGroup() != grp) // pussywizard: could not add because group was full (some shitness happened) + continue; + // Add the cooldown spell if queued for a random dungeon + // xinef: add aura + if ((randomDungeon || selectedRandomLfgDungeon(player->GetGUID())) && !player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) + { + randomDungeon = true; + player->AddAura(LFG_SPELL_DUNGEON_COOLDOWN, player); + } + TeleportPlayer(player, false); + } + + if (randomDungeon) + grp->AddLfgRandomInstanceFlag(); + if (Difficulty(dungeon->difficulty) == DUNGEON_DIFFICULTY_HEROIC) + grp->AddLfgHeroicFlag(); + + // Update group info + grp->SendUpdate(); +} + +uint32 LFGMgr::AddProposal(LfgProposal& proposal) +{ + proposal.id = ++m_lfgProposalId; + ProposalsStore[m_lfgProposalId] = proposal; + return m_lfgProposalId; +} + +/** + Update Proposal info with player answer + + @param[in] proposalId Proposal id to be updated + @param[in] guid Player guid to update answer + @param[in] accept Player answer +*/ +void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) +{ + // Check if the proposal exists + LfgProposalContainer::iterator itProposal = ProposalsStore.find(proposalId); + if (itProposal == ProposalsStore.end()) + return; + + LfgProposal& proposal = itProposal->second; + + // Check if proposal have the current player + LfgProposalPlayerContainer::iterator itProposalPlayer = proposal.players.find(guid); + if (itProposalPlayer == proposal.players.end()) + return; + + LfgProposalPlayer& player = itProposalPlayer->second; + player.accept = LfgAnswer(accept); + + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::UpdateProposal: Player [" UI64FMTD "] of proposal %u selected: %u", guid, proposalId, accept); + if (!accept) + { + RemoveProposal(itProposal, LFG_UPDATETYPE_PROPOSAL_DECLINED); + return; + } + + // check if all have answered and reorder players (leader first) + bool allAnswered = true; + for (LfgProposalPlayerContainer::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers) + if (itPlayers->second.accept != LFG_ANSWER_AGREE) // No answer (-1) or not accepted (0) + allAnswered = false; + + if (!allAnswered) + { + for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + SendLfgUpdateProposal(it->first, proposal); + + return; + } + + bool sendUpdate = proposal.state != LFG_PROPOSAL_SUCCESS; + proposal.state = LFG_PROPOSAL_SUCCESS; + time_t joinTime = time(NULL); + + LFGQueue& queue = GetQueue(guid); + LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND); + for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + uint64 pguid = it->first; + uint64 gguid = it->second.group; + uint32 dungeonId = (*GetSelectedDungeons(pguid).begin()); + int32 waitTime = -1; + if (sendUpdate) + SendLfgUpdateProposal(pguid, proposal); + + if (gguid) + { + waitTime = int32((joinTime - queue.GetJoinTime(gguid)) / IN_MILLISECONDS); + SendLfgUpdateParty(pguid, updateData); + } + else + { + waitTime = int32((joinTime - queue.GetJoinTime(pguid)) / IN_MILLISECONDS); + SendLfgUpdatePlayer(pguid, updateData); + } + updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE; + SendLfgUpdatePlayer(pguid, updateData); + SendLfgUpdateParty(pguid, updateData); + + // Update timers + uint8 role = GetRoles(pguid); + role &= ~PLAYER_ROLE_LEADER; + switch (role) + { + case PLAYER_ROLE_DAMAGE: + queue.UpdateWaitTimeDps(waitTime, dungeonId); + break; + case PLAYER_ROLE_HEALER: + queue.UpdateWaitTimeHealer(waitTime, dungeonId); + break; + case PLAYER_ROLE_TANK: + queue.UpdateWaitTimeTank(waitTime, dungeonId); + break; + default: + queue.UpdateWaitTimeAvg(waitTime, dungeonId); + break; + } + + SetState(pguid, LFG_STATE_DUNGEON); + } + + // Remove players/groups from Queue + for (uint8 i=0; i<5 && proposal.queues.guid[i]; ++i) + queue.RemoveQueueData(proposal.queues.guid[i]); + + MakeNewGroup(proposal); + ProposalsStore.erase(itProposal); +} + +/** + Remove a proposal from the pool, remove the group that didn't accept (if needed) and readd the other members to the queue + + @param[in] itProposal Iterator to the proposal to remove + @param[in] type Type of removal (LFG_UPDATETYPE_PROPOSAL_FAILED, LFG_UPDATETYPE_PROPOSAL_DECLINED) +*/ +void LFGMgr::RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdateType type) +{ + LfgProposal& proposal = itProposal->second; + proposal.state = LFG_PROPOSAL_FAILED; + + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Proposal %u, state FAILED, UpdateType %u", itProposal->first, type); + // Mark all people that didn't answered as no accept + if (type == LFG_UPDATETYPE_PROPOSAL_FAILED) + for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + if (it->second.accept == LFG_ANSWER_PENDING) + it->second.accept = LFG_ANSWER_DENY; + + // pussywizard: add cooldown for not accepting (after 40 secs) or declining + for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + if (it->second.accept == LFG_ANSWER_DENY) + if (Player* plr = sObjectAccessor->FindPlayer(it->first)) + if (Aura* aura = plr->AddAura(LFG_SPELL_DUNGEON_COOLDOWN, plr)) + aura->SetDuration(150*IN_MILLISECONDS); + + // Mark players/groups to be removed + LfgGuidSet toRemove; + for (LfgProposalPlayerContainer::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + if (it->second.accept == LFG_ANSWER_AGREE) + continue; + + uint64 guid = it->second.group ? it->second.group : it->first; + // Player didn't accept or still pending when no secs left + if (it->second.accept == LFG_ANSWER_DENY || type == LFG_UPDATETYPE_PROPOSAL_FAILED) + { + it->second.accept = LFG_ANSWER_DENY; + toRemove.insert(guid); + } + } + + // Notify players + for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + uint64 guid = it->first; + uint64 gguid = it->second.group ? it->second.group : guid; + + SendLfgUpdateProposal(guid, proposal); + + if (toRemove.find(gguid) != toRemove.end()) // Didn't accept or in same group that someone that didn't accept + { + LfgUpdateData updateData; + if (it->second.accept == LFG_ANSWER_DENY) + { + updateData.updateType = type; + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] didn't accept. Removing from queue and compatible cache", guid); + } + else + { + updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE; + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] in same group that someone that didn't accept. Removing from queue and compatible cache", guid); + } + + RestoreState(guid, "Proposal Fail (didn't accepted or in group with someone that didn't accept"); + if (gguid != guid) + { + RestoreState(it->second.group, "Proposal Fail (someone in group didn't accepted)"); + SendLfgUpdateParty(guid, updateData); + } + else + SendLfgUpdatePlayer(guid, updateData); + } + else + { + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Readding [" UI64FMTD "] to queue.", guid); + SetState(guid, LFG_STATE_QUEUED); + if (gguid != guid) + { + SetState(gguid, LFG_STATE_QUEUED); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); + } + else + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); + } + } + + LFGQueue& queue = GetQueue(proposal.players.begin()->first); + // Remove players/groups from queue + for (LfgGuidSet::const_iterator it = toRemove.begin(); it != toRemove.end(); ++it) + { + uint64 guid = *it; + queue.RemoveFromQueue(guid); + proposal.queues.remove(guid); + } + + // Readd to queue + for (uint8 i=0; i<5 && proposal.queues.guid[i]; ++i) + { + // xinef: this will work as data is not deleted, only references to this data are cleared + // xinef: when new proposal is created + // xinef: successful proposal is also taken into account is similar manner + queue.AddToQueue(proposal.queues.guid[i], true); + } + + ProposalsStore.erase(itProposal); +} + +/** + Initialize a boot kick vote + + @param[in] gguid Group the vote kicks belongs to + @param[in] kicker Kicker guid + @param[in] victim Victim guid + @param[in] reason Kick reason +*/ +void LFGMgr::InitBoot(uint64 gguid, uint64 kicker, uint64 victim, std::string const& reason) +{ + SetState(gguid, LFG_STATE_BOOT); + + LfgPlayerBoot& boot = BootsStore[gguid]; + boot.inProgress = true; + boot.cancelTime = time_t(time(NULL)) + LFG_TIME_BOOT; + boot.reason = reason; + boot.victim = victim; + + LfgGuidSet const& players = GetPlayers(gguid); + + // Set votes + for (LfgGuidSet::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + uint64 guid = (*itr); + SetState(guid, LFG_STATE_BOOT); + boot.votes[guid] = LFG_ANSWER_PENDING; + } + + boot.votes[victim] = LFG_ANSWER_DENY; // Victim auto vote NO + boot.votes[kicker] = LFG_ANSWER_AGREE; // Kicker auto vote YES + + // Notify players + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + SendLfgBootProposalUpdate(*it, boot); +} + +/** + Update Boot info with player answer + + @param[in] guid Player who has answered + @param[in] player answer +*/ +void LFGMgr::UpdateBoot(uint64 guid, bool accept) +{ + uint64 gguid = GetGroup(guid); + if (!gguid) + return; + + LfgPlayerBootContainer::iterator itBoot = BootsStore.find(gguid); + if (itBoot == BootsStore.end()) + return; + + LfgPlayerBoot& boot = itBoot->second; + + if (boot.votes[guid] != LFG_ANSWER_PENDING) // Cheat check: Player can't vote twice + return; + + boot.votes[guid] = LfgAnswer(accept); + + uint8 votesNum = 0; + uint8 agreeNum = 0; + for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) + { + if (itVotes->second != LFG_ANSWER_PENDING) + { + ++votesNum; + if (itVotes->second == LFG_ANSWER_AGREE) + ++agreeNum; + } + } + + // if we don't have enough votes (agree or deny) do nothing + if (agreeNum < LFG_GROUP_KICK_VOTES_NEEDED && (votesNum - agreeNum) < LFG_GROUP_KICK_VOTES_NEEDED) + return; + + // Send update info to all players + boot.inProgress = false; + for (LfgAnswerContainer::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) + { + uint64 pguid = itVotes->first; + if (pguid != boot.victim) + { + SetState(pguid, LFG_STATE_DUNGEON); + SendLfgBootProposalUpdate(pguid, boot); + } + } + + SetState(gguid, LFG_STATE_DUNGEON); + if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player + { + if (Group* group = sGroupMgr->GetGroupByGUID(GUID_LOPART(gguid))) + Player::RemoveFromGroup(group, boot.victim, GROUP_REMOVEMETHOD_KICK_LFG); + DecreaseKicksLeft(gguid); + } + BootsStore.erase(itBoot); +} + +/** + Teleports the player in or out the dungeon + + @param[in] player Player to teleport + @param[in] out Teleport out (true) or in (false) + @param[in] fromOpcode Function called from opcode handlers? (Default false) +*/ +void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false*/) +{ + LFGDungeonData const* dungeon = NULL; + Group* group = player->GetGroup(); + + if (group && group->isLFGGroup()) + dungeon = GetLFGDungeon(GetDungeon(group->GetGUID())); + + if (!dungeon) + { + player->GetSession()->SendLfgTeleportError(uint8(LFG_TELEPORTERROR_INVALID_LOCATION)); + return; + } + + if (out) + { + if (player->GetMapId() == uint32(dungeon->map)) + player->TeleportToEntryPoint(); + + return; + } + + LfgTeleportError error = LFG_TELEPORTERROR_OK; + + if (!player->IsAlive()) + error = LFG_TELEPORTERROR_PLAYER_DEAD; + else if (player->IsFalling() || player->HasUnitState(UNIT_STATE_JUMPING)) + error = LFG_TELEPORTERROR_FALLING; + else if (player->IsMirrorTimerActive(FATIGUE_TIMER)) + error = LFG_TELEPORTERROR_FATIGUE; + else if (player->GetVehicle()) + error = LFG_TELEPORTERROR_IN_VEHICLE; + else if (player->GetCharmGUID()) + error = LFG_TELEPORTERROR_CHARMING; + else if (player->GetMapId() != uint32(dungeon->map)) // Do not teleport players in dungeon to the entrance + { + uint32 mapid = dungeon->map; + float x = dungeon->x; + float y = dungeon->y; + float z = dungeon->z; + float orientation = dungeon->o; + + if (!fromOpcode) + { + // Select a player inside to be teleported to + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* plrg = itr->GetSource(); + if (plrg && plrg != player && plrg->GetMapId() == uint32(dungeon->map)) + { + mapid = plrg->GetMapId(); + x = plrg->GetPositionX(); + y = plrg->GetPositionY(); + z = plrg->GetPositionZ(); + orientation = plrg->GetOrientation(); + break; + } + } + } + + if (!player->GetMap()->IsDungeon()) + player->SetEntryPoint(); + + if (!player->TeleportTo(mapid, x, y, z, orientation)) + error = LFG_TELEPORTERROR_INVALID_LOCATION; + } + else + error = LFG_TELEPORTERROR_INVALID_LOCATION; + + if (error != LFG_TELEPORTERROR_OK) + player->GetSession()->SendLfgTeleportError(uint8(error)); + + //sLog->outDebug(LOG_FILTER_LFG, "TeleportPlayer: Player %s is being teleported in to map %u " + // "(x: %f, y: %f, z: %f) Result: %u", player->GetName().c_str(), dungeon->map, + // dungeon->x, dungeon->y, dungeon->z, error); +} + +/** + Finish a dungeon and give reward, if any. + + @param[in] guid Group guid + @param[in] dungeonId Dungeonid +*/ +void LFGMgr::FinishDungeon(uint64 gguid, const uint32 dungeonId, const Map* currMap) +{ + uint32 gDungeonId = GetDungeon(gguid); + if (gDungeonId != dungeonId) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] Finished dungeon %u but group queued for %u. Ignoring", gguid, dungeonId, gDungeonId); + return; + } + + if (GetState(gguid) == LFG_STATE_FINISHED_DUNGEON) // Shouldn't happen. Do not reward multiple times + { + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] Already rewarded group. Ignoring", gguid); + return; + } + + SetState(gguid, LFG_STATE_FINISHED_DUNGEON); + _SaveToDB(gguid); // pussywizard + + const LfgGuidSet& players = GetPlayers(gguid); + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + { + uint64 guid = (*it); + if (GetState(guid) == LFG_STATE_FINISHED_DUNGEON) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] Already rewarded player. Ignoring", guid); + continue; + } + + uint32 rDungeonId = 0; + const LfgDungeonSet& dungeons = GetSelectedDungeons(guid); + if (!dungeons.empty()) + rDungeonId = (*dungeons.begin()); + + SetState(guid, LFG_STATE_FINISHED_DUNGEON); + + // Give rewards only if its a random dungeon + LFGDungeonData const* dungeon = GetLFGDungeon(rDungeonId); + + if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !dungeon->seasonal)) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] dungeon %u is not random or seasonal", guid, rDungeonId); + continue; + } + + Player* player = ObjectAccessor::FindPlayer(guid); + if (!player || player->FindMap() != currMap) // pussywizard: currMap - multithreading crash if on other map (map id check is not enough, binding system is not reliable) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] not found in world", guid); + continue; + } + + LFGDungeonData const* dungeonDone = GetLFGDungeon(dungeonId); + uint32 mapId = dungeonDone ? uint32(dungeonDone->map) : 0; + + if (player->GetMapId() != mapId) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] is in map %u and should be in %u to get reward", guid, player->GetMapId(), mapId); + continue; + } + + // Xinef: Update achievements, set correct amount of randomly grouped players + if (dungeon->difficulty == DUNGEON_DIFFICULTY_HEROIC) + if (uint8 count = GetRandomPlayersCount(player->GetGUID())) + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS, count); + + LfgReward const* reward = GetRandomDungeonReward(rDungeonId, player->getLevel()); + if (!reward) + continue; + + bool done = false; + Quest const* quest = sObjectMgr->GetQuestTemplate(reward->firstQuest); + if (!quest) + continue; + + // if we can take the quest, means that we haven't done this kind of "run", IE: First Heroic Random of Day. + if (player->CanRewardQuest(quest, false)) + player->RewardQuest(quest, 0, NULL, false); + else + { + done = true; + quest = sObjectMgr->GetQuestTemplate(reward->otherQuest); + if (!quest) + continue; + // we give reward without informing client (retail does this) + player->RewardQuest(quest, 0, NULL, false); + } + + // Give rewards + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FinishDungeon: [" UI64FMTD "] done dungeon %u, %s previously done.", player->GetGUID(), GetDungeon(gguid), done? " " : " not"); + LfgPlayerRewardData data = LfgPlayerRewardData(dungeon->Entry(), GetDungeon(gguid, false), done, quest); + player->GetSession()->SendLfgPlayerReward(data); + } +} + +// --------------------------------------------------------------------------// +// Auxiliar Functions +// --------------------------------------------------------------------------// + +/** + Get the dungeon list that can be done given a random dungeon entry. + + @param[in] randomdungeon Random dungeon id (if value = 0 will return all dungeons) + @returns Set of dungeons that can be done. +*/ +LfgDungeonSet const& LFGMgr::GetDungeonsByRandom(uint32 randomdungeon) +{ + LFGDungeonData const* dungeon = GetLFGDungeon(randomdungeon); + uint32 group = dungeon ? dungeon->group : 0; + return CachedDungeonMapStore[group]; +} + +/** + Get the reward of a given random dungeon at a certain level + + @param[in] dungeon dungeon id + @param[in] level Player level + @returns Reward +*/ +LfgReward const* LFGMgr::GetRandomDungeonReward(uint32 dungeon, uint8 level) +{ + LfgReward const* rew = NULL; + LfgRewardContainerBounds bounds = RewardMapStore.equal_range(dungeon & 0x00FFFFFF); + for (LfgRewardContainer::const_iterator itr = bounds.first; itr != bounds.second; ++itr) + { + rew = itr->second; + // ordered properly at loading + if (itr->second->maxLevel >= level) + break; + } + + return rew; +} + +/** + Given a Dungeon id returns the dungeon Type + + @param[in] dungeon dungeon id + @returns Dungeon type +*/ +LfgType LFGMgr::GetDungeonType(uint32 dungeonId) +{ + LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId); + if (!dungeon) + return LFG_TYPE_NONE; + + return LfgType(dungeon->type); +} + +LfgState LFGMgr::GetState(uint64 guid) +{ + LfgState state; + if (IS_GROUP_GUID(guid)) + state = GroupsStore[guid].GetState(); + else + state = PlayersStore[guid].GetState(); + + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "] = %u", guid, state); + return state; +} + +LfgState LFGMgr::GetOldState(uint64 guid) +{ + LfgState state; + if (IS_GROUP_GUID(guid)) + state = GroupsStore[guid].GetOldState(); + else + state = PlayersStore[guid].GetOldState(); + + ;//sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::GetOldState: [" UI64FMTD "] = %u", guid, state); + return state; +} + +uint32 LFGMgr::GetDungeon(uint64 guid, bool asId /*= true */) +{ + uint32 dungeon = GroupsStore[guid].GetDungeon(asId); + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetDungeon: [" UI64FMTD "] asId: %u = %u", guid, asId, dungeon); + return dungeon; +} + +uint32 LFGMgr::GetDungeonMapId(uint64 guid) +{ + uint32 dungeonId = GroupsStore[guid].GetDungeon(true); + uint32 mapId = 0; + if (dungeonId) + if (LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId)) + mapId = dungeon->map; + + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetDungeonMapId: [" UI64FMTD "] = %u (DungeonId = %u)", guid, mapId, dungeonId); + return mapId; +} + +uint8 LFGMgr::GetRoles(uint64 guid) +{ + uint8 roles = PlayersStore[guid].GetRoles(); + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetRoles: [" UI64FMTD "] = %u", guid, roles); + return roles; +} + +const std::string& LFGMgr::GetComment(uint64 guid) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetComment: [" UI64FMTD "] = %s", guid, PlayersStore[guid].GetComment().c_str()); + return PlayersStore[guid].GetComment(); +} + +LfgDungeonSet const& LFGMgr::GetSelectedDungeons(uint64 guid) +{ + ;//sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::GetSelectedDungeons: [" UI64FMTD "]", guid); + return PlayersStore[guid].GetSelectedDungeons(); +} + +LfgLockMap const& LFGMgr::GetLockedDungeons(uint64 guid) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetLockedDungeons: [" UI64FMTD "]", guid); + return PlayersStore[guid].GetLockedDungeons(); +} + +uint8 LFGMgr::GetKicksLeft(uint64 guid) +{ + uint8 kicks = GroupsStore[guid].GetKicksLeft(); + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::GetKicksLeft: [" UI64FMTD "] = %u", guid, kicks); + return kicks; +} + +void LFGMgr::RestoreState(uint64 guid, char const* debugMsg) +{ + if (IS_GROUP_GUID(guid)) + { + LfgGroupData& data = GroupsStore[guid]; + /*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG)) + { + std::string const& ps = GetStateString(data.GetState()); + std::string const& os = GetStateString(data.GetOldState()); + sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::RestoreState: Group: [" UI64FMTD "] (%s) State: %s, oldState: %s", + guid, debugMsg, ps.c_str(), os.c_str()); + }*/ + + data.RestoreState(); + } + else + { + LfgPlayerData& data = PlayersStore[guid]; + /*if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG)) + { + std::string const& ps = GetStateString(data.GetState()); + std::string const& os = GetStateString(data.GetOldState()); + sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::RestoreState: Player: [" UI64FMTD "] (%s) State: %s, oldState: %s", + guid, debugMsg, ps.c_str(), os.c_str()); + }*/ + data.RestoreState(); + } +} + +void LFGMgr::SetState(uint64 guid, LfgState state) +{ + if (IS_GROUP_GUID(guid)) + { + LfgGroupData& data = GroupsStore[guid]; + //char const * const ns = GetStateString(state); + //char const * const ps = GetStateString(data.GetState()); + //char const * const os = GetStateString(data.GetOldState()); + //sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Group: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os); + data.SetState(state); + } + else + { + LfgPlayerData& data = PlayersStore[guid]; + //char const * const ns = GetStateString(state); + //char const * const ps = GetStateString(data.GetState()); + //char const * const os = GetStateString(data.GetOldState()); + //sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Player: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os); + data.SetState(state); + } +} + +void LFGMgr::SetCanOverrideRBState(uint64 guid, bool val) +{ + PlayersStore[guid].SetCanOverrideRBState(val); +} + +void LFGMgr::SetDungeon(uint64 guid, uint32 dungeon) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetDungeon: [" UI64FMTD "] dungeon %u", guid, dungeon); + GroupsStore[guid].SetDungeon(dungeon); +} + +void LFGMgr::SetRoles(uint64 guid, uint8 roles) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetRoles: [" UI64FMTD "] roles: %u", guid, roles); + PlayersStore[guid].SetRoles(roles); +} + +void LFGMgr::SetComment(uint64 guid, std::string const& comment) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetComment: [" UI64FMTD "] comment: %s", guid, comment.c_str()); + PlayersStore[guid].SetComment(comment); +} + +void LFGMgr::LfrSetComment(Player* p, std::string comment) +{ + // pussywizard: client limit for comment length is 64 @ 3.3.5a + if (comment.size() > 64) + comment = comment.substr(0, 64); + + uint8 teamId = p->GetTeamId(); + RBEntryInfoMap::iterator iter; + for (RBStoreMap::iterator itr = RaidBrowserStore[teamId].begin(); itr != RaidBrowserStore[teamId].end(); ++itr) + if ((iter = itr->second.find(p->GetGUIDLow())) != itr->second.end()) + iter->second.comment = comment; +} + +void LFGMgr::SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetSelectedDungeons: [" UI64FMTD "]", guid); + PlayersStore[guid].SetSelectedDungeons(dungeons); +} + +void LFGMgr::SetLockedDungeons(uint64 guid, LfgLockMap const& lock) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::SetLockedDungeons: [" UI64FMTD "]", guid); + PlayersStore[guid].SetLockedDungeons(lock); +} + +void LFGMgr::DecreaseKicksLeft(uint64 guid) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::DecreaseKicksLeft: [" UI64FMTD "]", guid); + GroupsStore[guid].DecreaseKicksLeft(); +} + +void LFGMgr::RemoveGroupData(uint64 guid) +{ + ;//sLog->outDebug((LOG_FILTER_LFG, "LFGMgr::RemoveGroupData: [" UI64FMTD "]", guid); + LfgGroupDataContainer::iterator it = GroupsStore.find(guid); + if (it == GroupsStore.end()) + return; + + LfgState state = GetState(guid); + // If group is being formed after proposal success do nothing more + LfgGuidSet const& players = it->second.GetPlayers(); + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + { + uint64 guid = (*it); + SetGroup(*it, 0); + if (state != LFG_STATE_PROPOSAL) + { + SetState(*it, LFG_STATE_NONE); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + } + } + GroupsStore.erase(it); +} + +TeamId LFGMgr::GetTeam(uint64 guid) +{ + return PlayersStore[guid].GetTeam(); +} + +uint8 LFGMgr::RemovePlayerFromGroup(uint64 gguid, uint64 guid) +{ + return GroupsStore[gguid].RemovePlayer(guid); +} + +void LFGMgr::AddPlayerToGroup(uint64 gguid, uint64 guid) +{ + GroupsStore[gguid].AddPlayer(guid); +} + +void LFGMgr::SetLeader(uint64 gguid, uint64 leader) +{ + GroupsStore[gguid].SetLeader(leader); +} + +void LFGMgr::SetTeam(uint64 guid, TeamId teamId) +{ + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) + teamId = TEAM_ALLIANCE; // @Not Sure About That TeamId is supposed to be uint8 Team = 0(@TrinityCore) + + PlayersStore[guid].SetTeam(teamId); +} + +uint64 LFGMgr::GetGroup(uint64 guid) +{ + return PlayersStore[guid].GetGroup(); +} + +void LFGMgr::SetGroup(uint64 guid, uint64 group) +{ + PlayersStore[guid].SetGroup(group); +} + +LfgGuidSet const& LFGMgr::GetPlayers(uint64 guid) +{ + return GroupsStore[guid].GetPlayers(); +} + +uint8 LFGMgr::GetPlayerCount(uint64 guid) +{ + return GroupsStore[guid].GetPlayerCount(); +} + +uint64 LFGMgr::GetLeader(uint64 guid) +{ + return GroupsStore[guid].GetLeader(); +} + +void LFGMgr::SetRandomPlayersCount(uint64 guid, uint8 count) +{ + PlayersStore[guid].SetRandomPlayersCount(count); +} + +uint8 LFGMgr::GetRandomPlayersCount(uint64 guid) +{ + return PlayersStore[guid].GetRandomPlayersCount(); +} + +bool LFGMgr::HasIgnore(uint64 guid1, uint64 guid2) +{ + Player* plr1 = ObjectAccessor::FindPlayerInOrOutOfWorld(guid1); + Player* plr2 = ObjectAccessor::FindPlayerInOrOutOfWorld(guid2); + uint32 low1 = GUID_LOPART(guid1); + uint32 low2 = GUID_LOPART(guid2); + return plr1 && plr2 && (plr1->GetSocial()->HasIgnore(low2) || plr2->GetSocial()->HasIgnore(low1)); +} + +void LFGMgr::SendLfgRoleChosen(uint64 guid, uint64 pguid, uint8 roles) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgRoleChosen(pguid, roles); +} + +void LFGMgr::SendLfgRoleCheckUpdate(uint64 guid, LfgRoleCheck const& roleCheck) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgRoleCheckUpdate(roleCheck); +} + +void LFGMgr::SendLfgUpdatePlayer(uint64 guid, LfgUpdateData const& data) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgUpdatePlayer(data); +} + +void LFGMgr::SendLfgUpdateParty(uint64 guid, LfgUpdateData const& data) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgUpdateParty(data); +} + +void LFGMgr::SendLfgJoinResult(uint64 guid, LfgJoinResultData const& data) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgJoinResult(data); +} + +void LFGMgr::SendLfgBootProposalUpdate(uint64 guid, LfgPlayerBoot const& boot) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgBootProposalUpdate(boot); +} + +void LFGMgr::SendLfgUpdateProposal(uint64 guid, LfgProposal const& proposal) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgUpdateProposal(proposal); +} + +void LFGMgr::SendLfgQueueStatus(uint64 guid, LfgQueueStatusData const& data) +{ + if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid)) + player->GetSession()->SendLfgQueueStatus(data); +} + +bool LFGMgr::IsLfgGroup(uint64 guid) +{ + return guid && IS_GROUP_GUID(guid) && GroupsStore[guid].IsLfgGroup(); +} + +LFGQueue& LFGMgr::GetQueue(uint64 guid) +{ + uint8 queueId = 0; + if (IS_GROUP_GUID(guid)) + { + LfgGuidSet const& players = GetPlayers(guid); + uint64 pguid = players.empty() ? 0 : (*players.begin()); + if (pguid) + queueId = GetTeam(pguid); + else + queueId = GetTeam(GetLeader(guid)); + } + else + queueId = GetTeam(guid); + return QueuesStore[queueId]; +} + +bool LFGMgr::AllQueued(Lfg5Guids const& check) +{ + bool ok = true; + + if (check.empty()) + return false; + + for (uint8 i=0; i<5 && check.guid[i]; ++i) + { + uint64 guid = check.guid[i]; + if (GetState(guid) != LFG_STATE_QUEUED) + { + LFGQueue& queue = GetQueue(guid); + queue.RemoveFromQueue(guid); + ok = false; + } + } + + return ok; +} + +// Only for debugging purposes +void LFGMgr::Clean() +{ + QueuesStore.clear(); +} + +bool LFGMgr::isOptionEnabled(uint32 option) +{ + return m_options & option; +} + +uint32 LFGMgr::GetOptions() +{ + return m_options; +} + +void LFGMgr::SetOptions(uint32 options) +{ + m_options = options; +} + +LfgUpdateData LFGMgr::GetLfgStatus(uint64 guid) +{ + LfgPlayerData& playerData = PlayersStore[guid]; + return LfgUpdateData(LFG_UPDATETYPE_UPDATE_STATUS, playerData.GetState(), playerData.GetSelectedDungeons()); +} + +bool LFGMgr::IsSeasonActive(uint32 dungeonId) +{ + switch (dungeonId) + { + case 285: // The Headless Horseman + return IsHolidayActive(HOLIDAY_HALLOWS_END); + case 286: // The Frost Lord Ahune + return IsHolidayActive(HOLIDAY_FIRE_FESTIVAL); + case 287: // Coren Direbrew + return IsHolidayActive(HOLIDAY_BREWFEST); + case 288: // The Crown Chemical Co. + return IsHolidayActive(HOLIDAY_LOVE_IS_IN_THE_AIR); + } + return false; +} + +void LFGMgr::SetupGroupMember(uint64 guid, uint64 gguid) +{ + LfgDungeonSet dungeons; + dungeons.insert(GetDungeon(gguid)); + SetSelectedDungeons(guid, dungeons); + SetState(guid, GetState(gguid)); + SetGroup(guid, gguid); + AddPlayerToGroup(gguid, guid); +} + +bool LFGMgr::selectedRandomLfgDungeon(uint64 guid) +{ + if (GetState(guid) != LFG_STATE_NONE) + { + LfgDungeonSet const& dungeons = GetSelectedDungeons(guid); + if (!dungeons.empty()) + { + LFGDungeonData const* dungeon = GetLFGDungeon(*dungeons.begin()); + if (dungeon && (dungeon->type == LFG_TYPE_RANDOM || dungeon->seasonal)) + return true; + } + } + + return false; +} + +bool LFGMgr::inLfgDungeonMap(uint64 guid, uint32 map, Difficulty difficulty) +{ + if (!IS_GROUP_GUID(guid)) + guid = GetGroup(guid); + + if (uint32 dungeonId = GetDungeon(guid, true)) + if (LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId)) + if (uint32(dungeon->map) == map && dungeon->difficulty == difficulty) + return true; + + return false; +} + +uint32 LFGMgr::GetLFGDungeonEntry(uint32 id) +{ + if (id) + if (LFGDungeonData const* dungeon = GetLFGDungeon(id)) + return dungeon->Entry(); + + return 0; +} + +LfgDungeonSet LFGMgr::GetRandomAndSeasonalDungeons(uint8 level, uint8 expansion) +{ + LfgDungeonSet randomDungeons; + for (lfg::LFGDungeonContainer::const_iterator itr = LfgDungeonStore.begin(); itr != LfgDungeonStore.end(); ++itr) + { + lfg::LFGDungeonData const& dungeon = itr->second; + if ((dungeon.type == lfg::LFG_TYPE_RANDOM || (dungeon.seasonal && sLFGMgr->IsSeasonActive(dungeon.id))) + && dungeon.expansion <= expansion && dungeon.minlevel <= level && level <= dungeon.maxlevel) + randomDungeons.insert(dungeon.Entry()); + } + return randomDungeons; +} + +} // namespace lfg diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 3270dc0bf3..8104a840c4 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1015,8 +1015,6 @@ void Creature::SaveToDB() void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) { - return; - // update in loaded data if (!m_DBTableGuid) m_DBTableGuid = GetGUIDLow(); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 96483bab7f..4fec5c0d90 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -813,8 +813,6 @@ void GameObject::SaveToDB() void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) { - return; - const GameObjectTemplate* goI = GetGOInfo(); if (!goI) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index dbdd264a7c..9661b0eef2 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -93,7 +93,7 @@ WorldObject::~WorldObject() { if (GetTypeId() == TYPEID_CORPSE) { - sLog->outCrash("Object::~Object Corpse guid=" UI64FMTD", type=%d, entry=%u deleted but still in map!!", GetGUID(), ((Corpse*)this)->GetType(), GetEntry()); + sLog->outCrash("Object::~Object Corpse guid="UI64FMTD", type=%d, entry=%u deleted but still in map!!", GetGUID(), ((Corpse*)this)->GetType(), GetEntry()); ASSERT(false); } ResetMap(); @@ -104,7 +104,7 @@ Object::~Object() { if (IsInWorld()) { - sLog->outCrash("Object::~Object - guid=" UI64FMTD", typeid=%d, entry=%u deleted but still in world!!", GetGUID(), GetTypeId(), GetEntry()); + sLog->outCrash("Object::~Object - guid="UI64FMTD", typeid=%d, entry=%u deleted but still in world!!", GetGUID(), GetTypeId(), GetEntry()); if (isType(TYPEMASK_ITEM)) sLog->outCrash("Item slot %u", ((Item*)this)->GetSlot()); ASSERT(false); @@ -113,7 +113,7 @@ Object::~Object() if (m_objectUpdated) { - sLog->outCrash("Object::~Object - guid=" UI64FMTD", typeid=%d, entry=%u deleted but still in update list!!", GetGUID(), GetTypeId(), GetEntry()); + sLog->outCrash("Object::~Object - guid="UI64FMTD", typeid=%d, entry=%u deleted but still in update list!!", GetGUID(), GetTypeId(), GetEntry()); ASSERT(false); sObjectAccessor->RemoveUpdateObject(this); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 097e9345e0..a1c9d50172 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4204,7 +4204,7 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar // not reset skills for professions and racial abilities if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) && (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0)) continue; - + // pussywizard: this is needed for weapon/armor/language skills to remove them when loosing spell SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0); } @@ -14584,8 +14584,8 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool break; case GOSSIP_OPTION_VENDOR: { - VendorItemData const* vendorItems = creature->GetVendorItems(); - if (!vendorItems || vendorItems->Empty()) + VendorItemData const* vendorItems = itr->second.ActionMenuId ? nullptr : creature->GetVendorItems(); + if (!itr->second.ActionMenuId && (!vendorItems || vendorItems->Empty())) { sLog->outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", creature->GetGUIDLow(), creature->GetEntry()); canTalk = false; @@ -14759,7 +14759,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men break; case GOSSIP_OPTION_VENDOR: case GOSSIP_OPTION_ARMORER: - GetSession()->SendListInventory(guid); + GetSession()->SendListInventory(guid, menuItemData->GossipActionMenuId); break; case GOSSIP_OPTION_STABLEPET: GetSession()->SendStablePet(guid); @@ -20407,7 +20407,7 @@ void Player::TextEmote(const std::string& text) { WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, text); - SendMessageToSetInRange_OwnTeam(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true); + SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT)); } void Player::Whisper(const std::string& text, uint32 language, uint64 receiver) @@ -20514,7 +20514,7 @@ void Player::PossessSpellInitialize() if (!charmInfo) { - sLog->outError("Player::PossessSpellInitialize(): charm (" UI64FMTD") has no charminfo!", charm->GetGUID()); + sLog->outError("Player::PossessSpellInitialize(): charm ("UI64FMTD") has no charminfo!", charm->GetGUID()); return; } @@ -20604,7 +20604,7 @@ void Player::CharmSpellInitialize() CharmInfo* charmInfo = charm->GetCharmInfo(); if (!charmInfo) { - sLog->outError("Player::CharmSpellInitialize(): the player's charm (" UI64FMTD") has no charminfo!", charm->GetGUID()); + sLog->outError("Player::CharmSpellInitialize(): the player's charm ("UI64FMTD") has no charminfo!", charm->GetGUID()); return; } @@ -21533,8 +21533,9 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 return false; } - VendorItemData const* vItems = creature->GetVendorItems(); - if (!vItems || vItems->Empty()) + + VendorItemData const* vItems = GetSession()->GetCurrentVendor() ? sObjectMgr->GetNpcVendorItemList(GetSession()->GetCurrentVendor()) : creature->GetVendorItems(); + if (!vItems || vItems->Empty()) { SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0); return false; @@ -25555,7 +25556,7 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset) if (!found) // something wrong... { - sLog->outError("Player %s tried to save equipment set " UI64FMTD" (index %u), but that equipment set not found!", GetName().c_str(), eqset.Guid, index); + sLog->outError("Player %s tried to save equipment set "UI64FMTD" (index %u), but that equipment set not found!", GetName().c_str(), eqset.Guid, index); return; } } diff --git a/src/server/game/Entities/Player/SocialMgr.cpp b/src/server/game/Entities/Player/SocialMgr.cpp index ad7e145aa4..1616c70e07 100644 --- a/src/server/game/Entities/Player/SocialMgr.cpp +++ b/src/server/game/Entities/Player/SocialMgr.cpp @@ -222,6 +222,7 @@ void SocialMgr::GetFriendInfo(Player* player, uint32 friendGUID, FriendInfo &fri TeamId teamId = player->GetTeamId(); AccountTypes security = player->GetSession()->GetSecurity(); + bool allowTwoSideWhoList = sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); AccountTypes gmLevelInWhoList = AccountTypes(sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST)); PlayerSocialMap::iterator itr = player->GetSocial()->m_playerSocialMap.find(friendGUID); @@ -230,7 +231,7 @@ void SocialMgr::GetFriendInfo(Player* player, uint32 friendGUID, FriendInfo &fri // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if (pFriend && (!AccountMgr::IsPlayerAccount(security) || (pFriend->GetTeamId() == teamId && pFriend->GetSession()->GetSecurity() <= gmLevelInWhoList)) && pFriend->IsVisibleGloballyFor(player)) + if (pFriend && (!AccountMgr::IsPlayerAccount(security) || ((pFriend->GetTeamId() == teamId || allowTwoSideWhoList) && pFriend->GetSession()->GetSecurity() <= gmLevelInWhoList)) && pFriend->IsVisibleGloballyFor(player)) { friendInfo.Status = FRIEND_STATUS_ONLINE; if (pFriend->isAFK()) @@ -293,7 +294,8 @@ void SocialMgr::BroadcastToFriendListers(Player* player, WorldPacket* packet) TeamId teamId = player->GetTeamId(); AccountTypes security = player->GetSession()->GetSecurity(); - uint32 guid = player->GetGUIDLow(); + uint32 guid = player->GetGUIDLow(); + bool allowTwoSideWhoList = sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); AccountTypes gmLevelInWhoList = AccountTypes(sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST)); for (SocialMap::const_iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr) @@ -305,7 +307,7 @@ void SocialMgr::BroadcastToFriendListers(Player* player, WorldPacket* packet) // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if (pFriend && (!AccountMgr::IsPlayerAccount(pFriend->GetSession()->GetSecurity()) || (pFriend->GetTeamId() == teamId && security <= gmLevelInWhoList)) && player->IsVisibleGloballyFor(pFriend)) + if (pFriend && (!AccountMgr::IsPlayerAccount(pFriend->GetSession()->GetSecurity()) || ((pFriend->GetTeamId() == teamId || allowTwoSideWhoList) && security <= gmLevelInWhoList)) && player->IsVisibleGloballyFor(pFriend)) pFriend->GetSession()->SendPacket(packet); } } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e445cd4c5f..c20ddcd3b9 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -10587,6 +10587,10 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin float ApCoeffMod = 1.0f; int32 DoneTotal = 0; float DoneTotalMod = TotalMod ? TotalMod : SpellPctDamageModsDone(victim, spellProto, damagetype); + + // Config : RATE_CREATURE_X_SPELLDAMAGE & Do Not Modify Pet/Guardian/Mind Controled Damage + if (GetTypeId() == TYPEID_UNIT && (!ToCreature()->IsPet() || !ToCreature()->IsGuardian() || !ToCreature()->IsControlledByPlayer())) + DoneTotalMod *= ToCreature()->GetSpellDamageMod(ToCreature()->GetCreatureTemplate()->rank); // Some spells don't benefit from pct done mods if (!spellProto->HasAttribute(SPELL_ATTR6_LIMIT_PCT_DAMAGE_MODS)) @@ -17062,7 +17066,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) if (GetCharmInfo()) GetCharmInfo()->SetPetNumber(0, true); else - sLog->outError("Aura::HandleModCharm: target=" UI64FMTD" with typeid=%d has a charm aura but no charm info!", GetGUID(), GetTypeId()); + sLog->outError("Aura::HandleModCharm: target="UI64FMTD" with typeid=%d has a charm aura but no charm info!", GetGUID(), GetTypeId()); } } break; @@ -18472,8 +18476,8 @@ void Unit::StopAttackFaction(uint32 faction_id) void Unit::OutDebugInfo() const { sLog->outError("Unit::OutDebugInfo"); - sLog->outString("GUID " UI64FMTD", entry %u, type %u, name %s", GetGUID(), GetEntry(), (uint32)GetTypeId(), GetName().c_str()); - sLog->outString("OwnerGUID " UI64FMTD", MinionGUID " UI64FMTD", CharmerGUID " UI64FMTD", CharmedGUID " UI64FMTD, GetOwnerGUID(), GetMinionGUID(), GetCharmerGUID(), GetCharmGUID()); + sLog->outString("GUID "UI64FMTD", entry %u, type %u, name %s", GetGUID(), GetEntry(), (uint32)GetTypeId(), GetName().c_str()); + sLog->outString("OwnerGUID "UI64FMTD", MinionGUID "UI64FMTD", CharmerGUID "UI64FMTD", CharmedGUID "UI64FMTD, GetOwnerGUID(), GetMinionGUID(), GetCharmerGUID(), GetCharmGUID()); sLog->outString("In world %u, unit type mask %u", (uint32)(IsInWorld() ? 1 : 0), m_unitTypeMask); if (IsInWorld()) sLog->outString("Mapid %u", GetMapId()); @@ -19190,21 +19194,36 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) // FG: pretend that OTHER players in own group are friendly ("blue") else if (index == UNIT_FIELD_BYTES_2 || index == UNIT_FIELD_FACTIONTEMPLATE) { - if (target->IsSpectator() && target->FindMap() && target->FindMap()->IsBattleArena() && (this->GetTypeId() == TYPEID_PLAYER || this->GetTypeId() == TYPEID_UNIT || this->GetTypeId() == TYPEID_DYNAMICOBJECT)) // pussywizard + if (IsControlledByPlayer() && target != this && sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && IsInRaidWith(target)) + { + FactionTemplateEntry const* ft1 = GetFactionTemplateEntry(); + FactionTemplateEntry const* ft2 = target->GetFactionTemplateEntry(); + if (ft1 && ft2 && !ft1->IsFriendlyTo(*ft2)) + { + if (index == UNIT_FIELD_BYTES_2) + // Allow targetting opposite faction in party when enabled in config + fieldBuffer << (m_uint32Values[UNIT_FIELD_BYTES_2] & ((UNIT_BYTE2_FLAG_SANCTUARY /*| UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5*/) << 8)); // this flag is at uint8 offset 1 !! + else + // pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work) + fieldBuffer << uint32(target->getFaction()); + } + else + fieldBuffer << m_uint32Values[index]; + }// pussywizard / Callmephil + else if (target->IsSpectator() && target->FindMap() && target->FindMap()->IsBattleArena() && + (this->GetTypeId() == TYPEID_PLAYER || this->GetTypeId() == TYPEID_UNIT || this->GetTypeId() == TYPEID_DYNAMICOBJECT)) { if (index == UNIT_FIELD_BYTES_2) fieldBuffer << (m_uint32Values[index] & 0xFFFFF2FF); // clear UNIT_BYTE2_FLAG_PVP, UNIT_BYTE2_FLAG_FFA_PVP, UNIT_BYTE2_FLAG_SANCTUARY else fieldBuffer << (uint32)target->getFaction(); } - else - fieldBuffer << m_uint32Values[index]; + else + fieldBuffer << m_uint32Values[index]; } else - { // send in current format (float as float, uint32 as uint32) fieldBuffer << m_uint32Values[index]; - } } } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index fe466c7f28..4104bf80b1 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8540,8 +8540,9 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru return true; } -bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const +bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set<uint32>* /*skip_vendors*/, uint32 /*ORnpcflag*/) const { + /* CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry); if (!cInfo) { @@ -8566,6 +8567,7 @@ bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 max } return false; } + */ if (!sObjectMgr->GetItemTemplate(item_id)) { diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index ef498ba3e0..1848f92453 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -1504,11 +1504,12 @@ void Guild::HandleInviteMember(WorldSession* session, std::string const& name) if (pInvitee->GetSocial()->HasIgnore(player->GetGUIDLow())) return; - if (pInvitee->GetTeamId() != player->GetTeamId()) + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && pInvitee->GetTeamId() != player->GetTeamId()) { SendCommandResult(session, GUILD_COMMAND_INVITE, ERR_GUILD_NOT_ALLIED, name); return; } + // Invited player cannot be in another guild if (pInvitee->GetGuildId()) { @@ -1545,7 +1546,8 @@ void Guild::HandleInviteMember(WorldSession* session, std::string const& name) void Guild::HandleAcceptMember(WorldSession* session) { Player* player = session->GetPlayer(); - if (player->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(GetLeaderGUID())) + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && + player->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(GetLeaderGUID())) return; AddMember(player->GetGUID()); @@ -1729,7 +1731,7 @@ void Guild::HandleMemberDepositMoney(WorldSession* session, uint32 amount) _BroadcastEvent(GE_BANK_MONEY_SET, 0, aux.c_str()); if (amount > 10*GOLD) - CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<GB DEPOSIT> %s (guild id: %u, members: %u, new amount: " UI64FMTD", leader guid low: %u, char level: %u)\", NOW())", session->GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), session->GetRemoteAddress().c_str(), 0, "", amount, GetName().c_str(), GetId(), GetMemberCount(), GetTotalBankMoney(), (uint32)(GetLeaderGUID()&0xFFFFFFFF), player->getLevel()); + CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<GB DEPOSIT> %s (guild id: %u, members: %u, new amount: "UI64FMTD", leader guid low: %u, char level: %u)\", NOW())", session->GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), session->GetRemoteAddress().c_str(), 0, "", amount, GetName().c_str(), GetId(), GetMemberCount(), GetTotalBankMoney(), (uint32)(GetLeaderGUID()&0xFFFFFFFF), player->getLevel()); } bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool repair) @@ -1772,7 +1774,7 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool CharacterDatabase.CommitTransaction(trans); if (amount > 10*GOLD) - CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<GB WITHDRAW> %s (guild id: %u, members: %u, new amount: " UI64FMTD", leader guid low: %u, char level: %u)\", NOW())", session->GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), session->GetRemoteAddress().c_str(), 0, "", amount, GetName().c_str(), GetId(), GetMemberCount(), GetTotalBankMoney(), (uint32)(GetLeaderGUID()&0xFFFFFFFF), player->getLevel()); + CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<GB WITHDRAW> %s (guild id: %u, members: %u, new amount: "UI64FMTD", leader guid low: %u, char level: %u)\", NOW())", session->GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), session->GetRemoteAddress().c_str(), 0, "", amount, GetName().c_str(), GetId(), GetMemberCount(), GetTotalBankMoney(), (uint32)(GetLeaderGUID()&0xFFFFFFFF), player->getLevel()); std::string aux = ByteArrayToHexStr(reinterpret_cast<uint8*>(&m_bankMoney), 8, true); _BroadcastEvent(GE_BANK_MONEY_SET, 0, aux.c_str()); @@ -1845,8 +1847,6 @@ void Guild::SendBankTabData(WorldSession* session, uint8 tabId) const void Guild::SendBankTabsInfo(WorldSession* session, bool sendAllSlots /*= false*/) const { - if (session->GetSecurity()) - return; _SendBankList(session, 0, sendAllSlots); } diff --git a/src/server/game/Handlers/ArenaTeamHandler.cpp b/src/server/game/Handlers/ArenaTeamHandler.cpp index 079f7f91a7..51c9e2605a 100644 --- a/src/server/game/Handlers/ArenaTeamHandler.cpp +++ b/src/server/game/Handlers/ArenaTeamHandler.cpp @@ -123,11 +123,11 @@ void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket & recvData) if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) return; - if (player->GetTeamId() != GetPlayer()->GetTeamId()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); - return; - } + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeamId() != GetPlayer()->GetTeamId()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + return; + } if (player->GetArenaTeamId(arenaTeam->GetSlot())) { @@ -174,12 +174,12 @@ void WorldSession::HandleArenaTeamAcceptOpcode(WorldPacket & /*recvData*/) return; } - // Only allow members of the other faction to join the team if cross faction interaction is enabled - if (_player->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(arenaTeam->GetCaptain())) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED); - return; - } + // Only allow members of the other faction to join the team if cross faction interaction is enabled + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(arenaTeam->GetCaptain())) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + return; + } // Add player to team if (!arenaTeam->AddMember(_player->GetGUID())) diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index e75ac20e08..fcbf36beec 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -265,7 +265,11 @@ void WorldSession::HandleAuctionSellItem(WorldPacket & recvData) AuctionEntry* AH = new AuctionEntry; AH->Id = sObjectMgr->GenerateAuctionID(); - AH->auctioneer = GUID_LOPART(auctioneer); + + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + AH->auctioneer = 23442; + else + AH->auctioneer = GUID_LOPART(auctioneer); // Required stack size of auction matches to current item stack size, just move item to auctionhouse if (itemsCount == 1 && item->GetCount() == count[i]) diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index b454cbcab8..13637b4d5d 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -462,7 +462,7 @@ void WorldSession::HandleCalendarEventInvite(WorldPacket& recvData) return; } - if (_player->GetTeamId() != inviteeTeamId /*&& !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR)*/) + if (_player->GetTeamId() != inviteeTeamId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR)) { sCalendarMgr->SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_NOT_ALLIED); return; diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 81227d6be5..872cd7df1f 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -468,7 +468,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte } } - bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || true/*sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS)*/ || !AccountMgr::IsPlayerAccount(GetSecurity()); + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); _charCreateCallback.FreeResult(); @@ -492,7 +492,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte bool haveSameRace = false; uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); bool hasHeroicReqLevel = (heroicReqLevel == 0); - bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || true/*sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS)*/ || !AccountMgr::IsPlayerAccount(GetSecurity()); + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); if (result) @@ -2327,18 +2327,24 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) } // Reset guild - if (uint32 guildId = playerData->guildId) - if (Guild* guild = sGuildMgr->GetGuildById(guildId)) - guild->DeleteMember(MAKE_NEW_GUID(lowGuid, 0, HIGHGUID_PLAYER), false, false, true); - - // Delete Friend List - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SOCIAL_BY_GUID); - stmt->setUInt32(0, lowGuid); - trans->Append(stmt); + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) + { + if (uint32 guildId = playerData->guildId) + if (Guild* guild = sGuildMgr->GetGuildById(guildId)) + guild->DeleteMember(MAKE_NEW_GUID(lowGuid, 0, HIGHGUID_PLAYER), false, false, true); + } - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SOCIAL_BY_FRIEND); - stmt->setUInt32(0, lowGuid); - trans->Append(stmt); + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND)) + { + // Delete Friend List + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SOCIAL_BY_GUID); + stmt->setUInt32(0, lowGuid); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SOCIAL_BY_FRIEND); + stmt->setUInt32(0, lowGuid); + trans->Append(stmt); + } // Leave Arena Teams Player::LeaveAllArenaTeams(guid); diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index cee25fa435..a9d628601b 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -165,25 +165,38 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket & recvData) lang = LANG_UNIVERSAL; else { - switch (type) - { - case CHAT_MSG_PARTY: - case CHAT_MSG_PARTY_LEADER: - case CHAT_MSG_RAID: - case CHAT_MSG_RAID_LEADER: - case CHAT_MSG_RAID_WARNING: - specialMessageLimit = 35; - break; - case CHAT_MSG_GUILD: - case CHAT_MSG_OFFICER: - specialMessageLimit = 15; - break; - case CHAT_MSG_WHISPER: - if (sender->getLevel() >= 80) - specialMessageLimit = 15; - break; - } - + // send in universal language in two side iteration allowed mode + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT)) + lang = LANG_UNIVERSAL; + else + { + switch (type) + { + case CHAT_MSG_PARTY: + case CHAT_MSG_PARTY_LEADER: + case CHAT_MSG_RAID: + case CHAT_MSG_RAID_LEADER: + case CHAT_MSG_RAID_WARNING: + // allow two side chat at group channel if two side group allowed + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) + lang = LANG_UNIVERSAL; + + specialMessageLimit = 35; + break; + case CHAT_MSG_GUILD: + case CHAT_MSG_OFFICER: + // allow two side chat at guild channel if two side guild allowed + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) + lang = LANG_UNIVERSAL; + + specialMessageLimit = 15; + break; + case CHAT_MSG_WHISPER: + if (sender->getLevel() >= 80) + specialMessageLimit = 15; + break; + } + } // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used) Unit::AuraEffectList const& ModLangAuras = sender->GetAuraEffectsByType(SPELL_AURA_MOD_LANGUAGE); if (!ModLangAuras.empty()) @@ -335,7 +348,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket & recvData) return; } - if (senderIsPlayer && receiverIsPlayer) + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && senderIsPlayer && receiverIsPlayer) if (GetPlayer()->GetTeamId() != receiver->GetTeamId()) { SendWrongFactionNotice(); diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index 05ea23060b..b0863fcf7d 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -97,7 +97,7 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData) } // can't group with - if (!GetPlayer()->IsGameMaster() && GetPlayer()->GetTeamId() != player->GetTeamId()) + if (!GetPlayer()->IsGameMaster() && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && GetPlayer()->GetTeamId() != player->GetTeamId()) { SendPartyResult(PARTY_OP_INVITE, membername, ERR_PLAYER_WRONG_FACTION); return; diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index d1f903301f..9fe0b5ea19 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -724,7 +724,7 @@ void WorldSession::HandleListInventoryOpcode(WorldPacket & recvData) SendListInventory(guid); } -void WorldSession::SendListInventory(uint64 vendorGuid) +void WorldSession::SendListInventory(uint64 vendorGuid, uint32 vendorEntry) { ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_LIST_INVENTORY"); @@ -744,7 +744,9 @@ void WorldSession::SendListInventory(uint64 vendorGuid) if (vendor->HasUnitState(UNIT_STATE_MOVING)) vendor->StopMoving(); - VendorItemData const* items = vendor->GetVendorItems(); + SetCurrentVendor(vendorEntry); + + VendorItemData const* items = vendorEntry ? sObjectMgr->GetNpcVendorItemList(vendorEntry) : vendor->GetVendorItems(); if (!items) { WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + 1); diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 63d30598c0..3e5e04e56d 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -189,7 +189,7 @@ void WorldSession::HandleSendMail(WorldPacket & recvData) ? receive->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(rc); - if (/*!accountBound*/ GetAccountId() != rc_account && player->GetTeamId() != rc_teamId && AccountMgr::IsPlayerAccount(GetSecurity())) + if (/*!accountBound*/ GetAccountId() != rc_account && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeamId() != rc_teamId && AccountMgr::IsPlayerAccount(GetSecurity())) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 922afa328a..cd0a585d9a 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -170,193 +170,205 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recv_data) } } -void WorldSession::HandleWhoOpcode(WorldPacket & recv_data) -{ - ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_WHO Message"); - - time_t now = time(NULL); - if (now < timeWhoCommandAllowed) - return; - timeWhoCommandAllowed = now+3; - - uint32 matchcount = 0; - uint32 level_min, level_max, racemask, classmask, zones_count, str_count; - uint32 zoneids[10]; // 10 is client limit - std::string player_name, guild_name; - - recv_data >> level_min; // maximal player level, default 0 - recv_data >> level_max; // minimal player level, default 100 (MAX_LEVEL) - recv_data >> player_name; // player name, case sensitive... - - recv_data >> guild_name; // guild name, case sensitive... +void WorldSession::HandleWhoOpcode(WorldPacket& recvData) +{ + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_WHO Message"); - recv_data >> racemask; // race mask - recv_data >> classmask; // class mask - recv_data >> zones_count; // zones count, client limit = 10 (2.0.10) + time_t now = time(NULL); + if (now < timeWhoCommandAllowed) + return; + timeWhoCommandAllowed = now + 3; - if (zones_count > 10) - return; // can't be received from real client or broken packet + uint32 matchcount = 0; - for (uint32 i = 0; i < zones_count; ++i) - { - uint32 temp; - recv_data >> temp; // zone id, 0 if zone is unknown... - zoneids[i] = temp; - ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Zone %u: %u", i, zoneids[i]); - } + uint32 level_min, level_max, racemask, classmask, zones_count, str_count; + uint32 zoneids[10]; // 10 is client limit + std::string player_name, guild_name; - recv_data >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10) + recvData >> level_min; // maximal player level, default 0 + recvData >> level_max; // minimal player level, default 100 (MAX_LEVEL) + recvData >> player_name; // player name, case sensitive... - if (str_count > 4) - return; // can't be received from real client or broken packet + recvData >> guild_name; // guild name, case sensitive... - ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count); + recvData >> racemask; // race mask + recvData >> classmask; // class mask + recvData >> zones_count; // zones count, client limit = 10 (2.0.10) - std::wstring str[4]; // 4 is client limit - for (uint32 i = 0; i < str_count; ++i) - { - std::string temp; - recv_data >> temp; // user entered string, it used as universal search pattern(guild+player name)? + if (zones_count > 10) + return; // can't be received from real client or broken packet - if (!Utf8toWStr(temp, str[i])) - continue; + for (uint32 i = 0; i < zones_count; ++i) + { + uint32 temp; + recvData >> temp; // zone id, 0 if zone is unknown... + zoneids[i] = temp; + sLog->outDebug(LOG_FILTER_NETWORKIO, "Zone %u: %u", i, zoneids[i]); + } - wstrToLower(str[i]); + recvData >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10) - ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "String %u: %s", i, temp.c_str()); - } + if (str_count > 4) + return; // can't be received from real client or broken packet - std::wstring wplayer_name; - std::wstring wguild_name; - if (!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name))) - return; - wstrToLower(wplayer_name); - wstrToLower(wguild_name); + sLog->outDebug(LOG_FILTER_NETWORKIO, "Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count); - // client send in case not set max level value 100 but Trinity supports 255 max level, - // update it to show GMs with characters after 100 level - if (level_max >= MAX_LEVEL) - level_max = STRONG_MAX_LEVEL; + std::wstring str[4]; // 4 is client limit + for (uint32 i = 0; i < str_count; ++i) + { + std::string temp; + recvData >> temp; // user entered string, it used as universal search pattern(guild+player name)? - TeamId teamId = _player->GetTeamId(); - uint32 security = GetSecurity(); - uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); - uint32 displaycount = 0; + if (!Utf8toWStr(temp, str[i])) + continue; - WorldPacket data(SMSG_WHO, 500); // guess size - data << uint32(matchcount); // placeholder, count of players matching criteria - data << uint32(displaycount); // placeholder, count of players displayed + wstrToLower(str[i]); - std::vector<WhoListPlayerInfo> * m = WhoListCacheMgr::GetWhoList(); - for (std::vector<WhoListPlayerInfo>::const_iterator itr = m->begin(); itr != m->end(); ++itr) - { - if (AccountMgr::IsPlayerAccount(security)) - { - if ((*itr).teamId != teamId) - continue; + sLog->outDebug(LOG_FILTER_NETWORKIO, "String %u: %s", i, temp.c_str()); + } - // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST - //if ((*itr).security > AccountTypes(gmLevelInWhoList)) - // continue; - } + std::wstring wplayer_name; + std::wstring wguild_name; + if (!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name))) + return; + wstrToLower(wplayer_name); + wstrToLower(wguild_name); + + // client send in case not set max level value 100 but Trinity supports 255 max level, + // update it to show GMs with characters after 100 level + if (level_max >= MAX_LEVEL) + level_max = STRONG_MAX_LEVEL; + + uint32 team = _player->GetTeamId(); + uint32 security = GetSecurity(); + bool allowTwoSideWhoList = sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); + uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); + uint32 displaycount = 0; + + WorldPacket data(SMSG_WHO, 50); // guess size + data << uint32(matchcount); // placeholder, count of players matching criteria + data << uint32(displaycount); // placeholder, count of players displayed + + TRINITY_READ_GUARD(HashMapHolder<Player>::LockType, *HashMapHolder<Player>::GetLock()); + HashMapHolder<Player>::MapType const& m = sObjectAccessor->GetPlayers(); + for (HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr) + { + if (AccountMgr::IsPlayerAccount(security)) + { + // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST + if (itr->second->GetTeamId() != team && !allowTwoSideWhoList) + continue; - //do not process players which are not in world - //if (!(itr->second->IsInWorld())) - // continue; + // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST + if ((itr->second->GetSession()->GetSecurity() > AccountTypes(gmLevelInWhoList))) + continue; + } - // check if target is globally visible for player - //if (!(itr->second->IsVisibleGloballyFor(_player))) - // continue; + //do not process players which are not in world + if (!(itr->second->IsInWorld())) + continue; - // check if target's level is in level range - uint8 lvl = (*itr).level; - if (lvl < level_min || lvl > level_max) - continue; + // check if target is globally visible for player + if (!(itr->second->IsVisibleGloballyFor(_player))) + continue; - // check if class matches classmask - uint32 class_ = (*itr).clas; - if (!(classmask & (1 << class_))) - continue; + // check if target's level is in level range + uint8 lvl = itr->second->getLevel(); + if (lvl < level_min || lvl > level_max) + continue; - // check if race matches racemask - uint32 race = (*itr).race; - if (!(racemask & (1 << race))) - continue; + // check if class matches classmask + uint32 class_ = itr->second->getClass(); + if (!(classmask & (1 << class_))) + continue; - uint32 pzoneid = (*itr).zoneid; - uint8 gender = (*itr).gender; + // check if race matches racemask + uint32 race = itr->second->getRace(); + if (!(racemask & (1 << race))) + continue; - bool z_show = true; - for (uint32 i = 0; i < zones_count; ++i) - { - if (zoneids[i] == pzoneid) - { - z_show = true; - break; - } + uint32 pzoneid = itr->second->GetZoneId(); + uint8 gender = itr->second->getGender(); - z_show = false; - } - if (!z_show) - continue; + bool z_show = true; + for (uint32 i = 0; i < zones_count; ++i) + { + if (zoneids[i] == pzoneid) + { + z_show = true; + break; + } - std::wstring wpname = (*itr).wpname; + z_show = false; + } + if (!z_show) + continue; - if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos)) - continue; + std::string pname = itr->second->GetName(); + std::wstring wpname; + if (!Utf8toWStr(pname, wpname)) + continue; + wstrToLower(wpname); - std::wstring wgname = (*itr).wgname; + if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos)) + continue; - if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos)) - continue; + std::string gname = sGuildMgr->GetGuildNameById(itr->second->GetGuildId()); + std::wstring wgname; + if (!Utf8toWStr(gname, wgname)) + continue; + wstrToLower(wgname); - std::string aname = (*itr).aname; + if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos)) + continue; - bool s_show = true; - for (uint32 i = 0; i < str_count; ++i) - { - if (!str[i].empty()) - { - if (wgname.find(str[i]) != std::wstring::npos || - wpname.find(str[i]) != std::wstring::npos || - Utf8FitTo(aname, str[i])) - { - s_show = true; - break; - } - s_show = false; - } - } - if (!s_show) - continue; + std::string aname; + if (AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId())) + aname = areaEntry->area_name[GetSessionDbcLocale()]; - // 50 is maximum player count sent to client - if (matchcount >= 50) + bool s_show = true; + for (uint32 i = 0; i < str_count; ++i) { - ++matchcount; - continue; + if (!str[i].empty()) + { + if (wgname.find(str[i]) != std::wstring::npos || + wpname.find(str[i]) != std::wstring::npos || + Utf8FitTo(aname, str[i])) + { + s_show = true; + break; + } + s_show = false; + } } + if (!s_show) + continue; - data << (*itr).pname; // player name - data << (*itr).gname; // guild name - data << uint32(lvl); // player level - data << uint32(class_); // player class - data << uint32(race); // player race - data << uint8(gender); // player gender - data << uint32(pzoneid); // player zone id + // 49 is maximum player count sent to client - can be overridden + // through config, but is unstable + if ((matchcount++) >= 50 /*sWorld->getIntConfig(CONFIG_MAX_WHO)*/) + continue; - ++matchcount; - ++displaycount; - } + data << pname; // player name + data << gname; // guild name + data << uint32(lvl); // player level + data << uint32(class_); // player class + data << uint32(race); // player race + data << uint8(gender); // player gender + data << uint32(pzoneid); // player zone id - data.put(0, displaycount); // insert right count, count displayed - data.put(4, matchcount); // insert right count, count of matches + ++displaycount; + } - SendPacket(&data); - ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Send SMSG_WHO Message"); + data.put(0, displaycount); // insert right count, count displayed + data.put(4, matchcount); // insert right count, count of matches + + SendPacket(&data); + ;// sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Send SMSG_WHO Message"); } + void WorldSession::HandleLogoutRequestOpcode(WorldPacket & /*recv_data*/) { ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity()); @@ -532,7 +544,7 @@ void WorldSession::HandleAddFriendOpcode(WorldPacket & recv_data) { if (friendGuid == GetPlayer()->GetGUID()) friendResult = FRIEND_SELF; - else if (GetPlayer()->GetTeamId() != teamId && AccountMgr::IsPlayerAccount(GetSecurity())) + else if (GetPlayer()->GetTeamId() != teamId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && AccountMgr::IsPlayerAccount(GetSecurity())) friendResult = FRIEND_ENEMY; else if (GetPlayer()->GetSocial()->HasFriend(guidLow)) friendResult = FRIEND_ALREADY; diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index f9a136a780..0640d58ee5 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -435,7 +435,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recvData) return; // not let enemies sign guild charter - if (GetPlayer()->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(ownerGuid)) + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(ownerGuid)) { if (type != GUILD_CHARTER_TYPE) SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp index 1aa78e17d4..f043ccfa8c 100644 --- a/src/server/game/Handlers/TradeHandler.cpp +++ b/src/server/game/Handlers/TradeHandler.cpp @@ -618,11 +618,11 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) return; } - if (pOther->GetTeamId() !=_player->GetTeamId()) - { - SendTradeStatus(TRADE_STATUS_WRONG_FACTION); - return; - } + if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeamId() != _player->GetTeamId()) + { + SendTradeStatus(TRADE_STATUS_WRONG_FACTION); + return; + } if (!pOther->IsWithinDistInMap(_player, 10.0f, false)) { diff --git a/src/server/game/Handlers/VehicleHandler.cpp b/src/server/game/Handlers/VehicleHandler.cpp index 86cf595053..7bedfa63f1 100644 --- a/src/server/game/Handlers/VehicleHandler.cpp +++ b/src/server/game/Handlers/VehicleHandler.cpp @@ -222,7 +222,7 @@ void WorldSession::HandleEjectPassenger(WorldPacket &data) sLog->outError("Player %u attempted to eject creature GUID %u from non-ejectable seat.", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid)); } else - sLog->outError("HandleEjectPassenger: Player %u tried to eject invalid GUID " UI64FMTD, GetPlayer()->GetGUIDLow(), guid); + sLog->outError("HandleEjectPassenger: Player %u tried to eject invalid GUID "UI64FMTD, GetPlayer()->GetGUIDLow(), guid); } void WorldSession::HandleRequestVehicleExit(WorldPacket& /*recvData*/) diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index aad9e17332..f2949ec157 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -192,6 +192,9 @@ class InstanceScript : public ZoneScript // Cast spell on all players in instance void DoCastSpellOnPlayers(uint32 spell); + // Return wether server allow two side groups or not + bool ServerAllowsTwoSideGroups() { return sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP); } + virtual bool SetBossState(uint32 id, EncounterState state); EncounterState GetBossState(uint32 id) const { return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED; } BossBoundaryMap const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : NULL; } diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 7de5d1e38a..760059bfc6 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -101,7 +101,7 @@ m_inQueue(false), m_playerLoading(false), m_playerLogout(false), m_playerSave(fa m_sessionDbcLocale(sWorld->GetDefaultDbcLocale()), m_sessionDbLocaleIndex(locale), m_latency(0), m_clientTimeDelay(0), m_TutorialsChanged(false), recruiterId(recruiter), -isRecruiter(isARecruiter), m_currentBankerGUID(0), timeWhoCommandAllowed(0), _lastAuctionListItemsMSTime(0), _lastAuctionListOwnerItemsMSTime(0), _skipQueue(skipQueue) +isRecruiter(isARecruiter), m_currentVendorEntry(0), m_currentBankerGUID(0), timeWhoCommandAllowed(0), _lastAuctionListItemsMSTime(0), _lastAuctionListOwnerItemsMSTime(0), _skipQueue(skipQueue) { memset(m_Tutorials, 0, sizeof(m_Tutorials)); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 09c337e495..34b921f0d3 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -228,6 +228,9 @@ class WorldSession Player* GetPlayer() const { return _player; } std::string const& GetPlayerName() const; std::string GetPlayerInfo() const; + + uint32 GetCurrentVendor() const { return m_currentVendorEntry; } + void SetCurrentVendor(uint32 vendorEntry) { m_currentVendorEntry = vendorEntry; } uint32 GetGuidLow() const; void SetSecurity(AccountTypes security) { _security = security; } @@ -269,7 +272,7 @@ class WorldSession void SendTrainerList(uint64 guid); void SendTrainerList(uint64 guid, std::string const& strTitle); - void SendListInventory(uint64 guid); + void SendListInventory(uint64 guid, uint32 vendorEntry = 0); void SendShowBank(uint64 guid); bool CanOpenMailBox(uint64 guid); void SendShowMailBox(uint64 guid); @@ -998,6 +1001,7 @@ class WorldSession uint32 recruiterId; bool isRecruiter; ACE_Based::LockedQueue<WorldPacket*, ACE_Thread_Mutex> _recvQueue; + uint32 m_currentVendorEntry; uint64 m_currentBankerGUID; time_t timeWhoCommandAllowed; uint32 _offlineTime; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index ccfbd1bd80..ad09aca587 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -6139,7 +6139,7 @@ SpellCastResult Spell::CheckCasterAuras(bool preventionOnly) const if (usableInStun) { bool foundNotStun = false; - uint32 mask = 1<<MECHANIC_STUN; + uint32 mask = (1 << MECHANIC_STUN) | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_HORROR); // Barkskin should skip sleep effects, sap and fears if (m_spellInfo->Id == 22812) mask |= 1<<MECHANIC_SAPPED | 1<<MECHANIC_HORROR | 1<<MECHANIC_SLEEP; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 688380d276..6b5781249b 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -680,7 +680,19 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfigMgr->GetIntDefault ("StrictCharterNames", 0); m_int_configs[CONFIG_STRICT_CHANNEL_NAMES] = sConfigMgr->GetIntDefault ("StrictChannelNames", 0); m_int_configs[CONFIG_STRICT_PET_NAMES] = sConfigMgr->GetIntDefault ("StrictPetNames", 0); - + + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfigMgr->GetBoolDefault("AllowTwoSide.Accounts", true); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR]= sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Calendar", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Chat", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Channel", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Group", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Guild", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Auction", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Mail", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfigMgr->GetBoolDefault("AllowTwoSide.WhoList", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND] = sConfigMgr->GetBoolDefault("AllowTwoSide.AddFriend", false); + m_bool_configs[CONFIG_ALLOW_TWO_SIDE_TRADE] = sConfigMgr->GetBoolDefault("AllowTwoSide.trade", false); + m_int_configs[CONFIG_MIN_PLAYER_NAME] = sConfigMgr->GetIntDefault ("MinPlayerName", 2); if (m_int_configs[CONFIG_MIN_PLAYER_NAME] < 1 || m_int_configs[CONFIG_MIN_PLAYER_NAME] > MAX_PLAYER_NAME) { @@ -1664,6 +1676,10 @@ void World::SetInitialWorldSettings() ///- Handle outdated emails (delete/return) sLog->outString("Returning old mails..."); sObjectMgr->ReturnOrDeleteOldMails(false); + + ///- Load AutoBroadCast + sLog->outString("Loading Autobroadcasts..."); + LoadAutobroadcasts(); ///- Load and initialize scripts sObjectMgr->LoadSpellScripts(); // must be after load Creature/Gameobject(Template/Data) @@ -1842,6 +1858,40 @@ void World::DetectDBCLang() sLog->outString(); } +void World::LoadAutobroadcasts() +{ + uint32 oldMSTime = getMSTime(); + + m_Autobroadcasts.clear(); + m_AutobroadcastsWeights.clear(); + + uint32 realmId = sConfigMgr->GetIntDefault("RealmID", 0); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST); + stmt->setInt32(0, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (!result) + { + sLog->outString(">> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for this realm!"); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint8 id = fields[0].GetUInt8(); + + m_Autobroadcasts[id] = fields[2].GetString(); + m_AutobroadcastsWeights[id] = fields[1].GetUInt8(); + + ++count; + } while (result->NextRow()); + + sLog->outString(">> Loaded %u autobroadcast definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + /// Update the World ! void World::Update(uint32 diff) { @@ -2579,10 +2629,37 @@ void World::SendAutoBroadcast() { if (m_Autobroadcasts.empty()) return; + + uint32 weight = 0; + AutobroadcastsWeightMap selectionWeights; std::string msg; - msg = Trinity::Containers::SelectRandomContainerElement(m_Autobroadcasts); + for (AutobroadcastsWeightMap::const_iterator it = m_AutobroadcastsWeights.begin(); it != m_AutobroadcastsWeights.end(); ++it) + { + if (it->second) + { + weight += it->second; + selectionWeights[it->first] = it->second; + } + } + + if (weight) + { + uint32 selectedWeight = urand(0, weight - 1); + weight = 0; + for (AutobroadcastsWeightMap::const_iterator it = selectionWeights.begin(); it != selectionWeights.end(); ++it) + { + weight += it->second; + if (selectedWeight < weight) + { + msg = m_Autobroadcasts[it->first]; + break; + } + } + } + else + msg = m_Autobroadcasts[urand(0, m_Autobroadcasts.size())]; uint32 abcenter = sWorld->getIntConfig(CONFIG_AUTOBROADCAST_CENTER); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index b9a5aaeca7..d5a052fe42 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -90,6 +90,17 @@ enum WorldBoolConfigs CONFIG_ALLOW_PLAYER_COMMANDS, CONFIG_CLEAN_CHARACTER_DB, CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, + CONFIG_ALLOW_TWO_SIDE_ACCOUNTS, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, + CONFIG_ALLOW_TWO_SIDE_WHO_LIST, + CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, + CONFIG_ALLOW_TWO_SIDE_TRADE, CONFIG_ALL_TAXI_PATHS, CONFIG_INSTANT_TAXI, CONFIG_INSTANCE_IGNORE_LEVEL, @@ -741,6 +752,8 @@ class World void LoadDBVersion(); char const* GetDBVersion() const { return m_DBVersion.c_str(); } + void LoadAutobroadcasts(); + void UpdateAreaDependentAuras(); uint32 GetCleaningFlags() const { return m_CleaningFlags; } @@ -841,7 +854,11 @@ class World // used versions std::string m_DBVersion; - std::list<std::string> m_Autobroadcasts; + typedef std::map<uint8, std::string> AutobroadcastsMap; + AutobroadcastsMap m_Autobroadcasts; + + typedef std::map<uint8, uint8> AutobroadcastsWeightMap; + AutobroadcastsWeightMap m_AutobroadcastsWeights; void ProcessQueryCallbacks(); ACE_Future_Set<PreparedQueryResult> m_realmCharCallbacks; diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index 86175ca6f4..0fd076021a 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -1,19 +1,19 @@ /* - * Copyright (C) - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ +* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ /* ScriptData Name: account_commandscript @@ -22,36 +22,581 @@ Comment: All account related commands Category: commandscripts EndScriptData */ +#include "ScriptMgr.h" #include "AccountMgr.h" #include "Chat.h" #include "Language.h" -#include "Player.h" -#include "ScriptMgr.h" class account_commandscript : public CommandScript { public: - account_commandscript() : CommandScript("account_commandscript") { } - - ChatCommand* GetCommands() const - { - static ChatCommand commandTable[] = - { - { "account", SEC_PLAYER, false, &HandleAccountCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - return commandTable; - } - - static bool HandleAccountCommand(ChatHandler* handler, char const* /*args*/) - { - AccountTypes gmLevel = handler->GetSession()->GetSecurity(); - handler->PSendSysMessage(LANG_ACCOUNT_LEVEL, uint32(gmLevel)); - return true; - } + account_commandscript() : CommandScript("account_commandscript") { } + + ChatCommand* GetCommands() const + { + static ChatCommand accountSetCommandTable[] = + { + { "addon", SEC_ADMINISTRATOR, true, &HandleAccountSetAddonCommand, "", NULL }, + { "gmlevel", SEC_CONSOLE, true, &HandleAccountSetGmLevelCommand, "", NULL }, + { "password", SEC_CONSOLE, true, &HandleAccountSetPasswordCommand, "", NULL }, + { NULL, SEC_PLAYER, false, NULL, "", NULL } + }; + static ChatCommand accountCommandTable[] = + { + { "addon", SEC_MODERATOR, false, &HandleAccountAddonCommand, "", NULL }, + { "create", SEC_CONSOLE, true, &HandleAccountCreateCommand, "", NULL }, + { "delete", SEC_CONSOLE, true, &HandleAccountDeleteCommand, "", NULL }, + { "onlinelist", SEC_CONSOLE, true, &HandleAccountOnlineListCommand, "", NULL }, + { "lock", SEC_PLAYER, false, &HandleAccountLockCommand, "", NULL }, + { "set", SEC_ADMINISTRATOR, true, NULL, "", accountSetCommandTable }, + { "password", SEC_PLAYER, false, &HandleAccountPasswordCommand, "", NULL }, + { "", SEC_PLAYER, false, &HandleAccountCommand, "", NULL }, + { NULL, SEC_PLAYER, false, NULL, "", NULL } + }; + static ChatCommand commandTable[] = + { + { "account", SEC_PLAYER, true, NULL, "", accountCommandTable }, + { NULL, SEC_PLAYER, false, NULL, "", NULL } + }; + return commandTable; + } + + static bool HandleAccountAddonCommand(ChatHandler* handler, char const* args) + { + if (!*args) + { + handler->SendSysMessage(LANG_CMD_SYNTAX); + handler->SetSentErrorMessage(true); + return false; + } + + char* exp = strtok((char*)args, " "); + + uint32 accountId = handler->GetSession()->GetAccountId(); + + int expansion = atoi(exp); //get int anyway (0 if error) + if (expansion < 0 || uint8(expansion) > sWorld->getIntConfig(CONFIG_EXPANSION)) + { + handler->SendSysMessage(LANG_IMPROPER_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); + + stmt->setUInt8(0, uint8(expansion)); + stmt->setUInt32(1, accountId); + + LoginDatabase.Execute(stmt); + + handler->PSendSysMessage(LANG_ACCOUNT_ADDON, expansion); + return true; + } + + /// Create an account + static bool HandleAccountCreateCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + ///- %Parse the command line arguments + char* accountName = strtok((char*)args, " "); + char* password = strtok(NULL, " "); + if (!accountName || !password) + return false; + + AccountOpResult result = AccountMgr::CreateAccount(std::string(accountName), std::string(password)); + switch (result) + { + case AOR_OK: + handler->PSendSysMessage(LANG_ACCOUNT_CREATED, accountName); + if (handler->GetSession()) + { + sLog->outDebug(LOG_FILTER_WARDEN, "Account: %d (IP: %s) Character:[%s] (GUID: %u) Change Password.", + handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), + handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUIDLow()); + } + break; + case AOR_NAME_TOO_LONG: + handler->SendSysMessage(LANG_ACCOUNT_TOO_LONG); + handler->SetSentErrorMessage(true); + return false; + case AOR_NAME_ALREDY_EXIST: + handler->SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST); + handler->SetSentErrorMessage(true); + return false; + case AOR_DB_INTERNAL_ERROR: + handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED_SQL_ERROR, accountName); + handler->SetSentErrorMessage(true); + return false; + default: + handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED, accountName); + handler->SetSentErrorMessage(true); + return false; + } + + return true; + } + + /// Delete a user account and all associated characters in this realm + /// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm then delete account) + static bool HandleAccountDeleteCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + ///- Get the account name from the command line + char* account = strtok((char*)args, " "); + if (!account) + return false; + + std::string accountName = account; + if (!AccountMgr::normalizeString(accountName)) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + uint32 accountId = AccountMgr::GetId(accountName); + if (!accountId) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + /// Commands not recommended call from chat, but support anyway + /// can delete only for account with less security + /// This is also reject self apply in fact + if (handler->HasLowerSecurityAccount(NULL, accountId, true)) + return false; + + AccountOpResult result = AccountMgr::DeleteAccount(accountId); + switch (result) + { + case AOR_OK: + handler->PSendSysMessage(LANG_ACCOUNT_DELETED, accountName.c_str()); + break; + case AOR_NAME_NOT_EXIST: + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + case AOR_DB_INTERNAL_ERROR: + handler->PSendSysMessage(LANG_ACCOUNT_NOT_DELETED_SQL_ERROR, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + default: + handler->PSendSysMessage(LANG_ACCOUNT_NOT_DELETED, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + return true; + } + + /// Display info on users currently in the realm + static bool HandleAccountOnlineListCommand(ChatHandler* handler, char const* /*args*/) + { + ///- Get the list of accounts ID logged to the realm + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ONLINE); + + PreparedQueryResult result = CharacterDatabase.Query(stmt); + + if (!result) + { + handler->SendSysMessage(LANG_ACCOUNT_LIST_EMPTY); + return true; + } + + ///- Display the list of account/characters online + handler->SendSysMessage(LANG_ACCOUNT_LIST_BAR_HEADER); + handler->SendSysMessage(LANG_ACCOUNT_LIST_HEADER); + handler->SendSysMessage(LANG_ACCOUNT_LIST_BAR); + + ///- Cycle through accounts + do + { + Field* fieldsDB = result->Fetch(); + std::string name = fieldsDB[0].GetString(); + uint32 account = fieldsDB[1].GetUInt32(); + + ///- Get the username, last IP and GM level of each account + // No SQL injection. account is uint32. + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO); + stmt->setUInt32(0, account); + PreparedQueryResult resultLogin = LoginDatabase.Query(stmt); + + if (resultLogin) + { + Field* fieldsLogin = resultLogin->Fetch(); + handler->PSendSysMessage(LANG_ACCOUNT_LIST_LINE, + fieldsLogin[0].GetCString(), name.c_str(), fieldsLogin[1].GetCString(), + fieldsDB[2].GetUInt16(), fieldsDB[3].GetUInt16(), fieldsLogin[3].GetUInt8(), + fieldsLogin[2].GetUInt8()); + } + else + handler->PSendSysMessage(LANG_ACCOUNT_LIST_ERROR, name.c_str()); + } while (result->NextRow()); + + handler->SendSysMessage(LANG_ACCOUNT_LIST_BAR); + return true; + } + + static bool HandleAccountLockCommand(ChatHandler* handler, char const* args) + { + if (!*args) + { + handler->SendSysMessage(LANG_USE_BOL); + handler->SetSentErrorMessage(true); + return false; + } + + std::string param = (char*)args; + + if (!param.empty()) + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK); + + if (param == "on") + { + stmt->setBool(0, true); // locked + handler->PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED); + } + else if (param == "off") + { + stmt->setBool(0, false); // unlocked + handler->PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED); + } + + stmt->setUInt32(1, handler->GetSession()->GetAccountId()); + + LoginDatabase.Execute(stmt); + return true; + } + + handler->SendSysMessage(LANG_USE_BOL); + handler->SetSentErrorMessage(true); + return false; + } + + static bool HandleAccountPasswordCommand(ChatHandler* handler, char const* args) + { + if (!*args) + { + handler->SendSysMessage(LANG_CMD_SYNTAX); + handler->SetSentErrorMessage(true); + return false; + } + + char* oldPassword = strtok((char*)args, " "); + char* newPassword = strtok(NULL, " "); + char* passwordConfirmation = strtok(NULL, " "); + + if (!oldPassword || !newPassword || !passwordConfirmation) + { + handler->SendSysMessage(LANG_CMD_SYNTAX); + handler->SetSentErrorMessage(true); + return false; + } + + if (!AccountMgr::CheckPassword(handler->GetSession()->GetAccountId(), std::string(oldPassword))) + { + handler->SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD); + handler->SetSentErrorMessage(true); + return false; + } + + if (strcmp(newPassword, passwordConfirmation) != 0) + { + handler->SendSysMessage(LANG_NEW_PASSWORDS_NOT_MATCH); + handler->SetSentErrorMessage(true); + return false; + } + + AccountOpResult result = AccountMgr::ChangePassword(handler->GetSession()->GetAccountId(), std::string(newPassword)); + switch (result) + { + case AOR_OK: + handler->SendSysMessage(LANG_COMMAND_PASSWORD); + break; + case AOR_PASS_TOO_LONG: + handler->SendSysMessage(LANG_PASSWORD_TOO_LONG); + handler->SetSentErrorMessage(true); + return false; + default: + handler->SendSysMessage(LANG_COMMAND_NOTCHANGEPASSWORD); + handler->SetSentErrorMessage(true); + return false; + } + + return true; + } + + static bool HandleAccountCommand(ChatHandler* handler, char const* /*args*/) + { + AccountTypes gmLevel = handler->GetSession()->GetSecurity(); + handler->PSendSysMessage(LANG_ACCOUNT_LEVEL, uint32(gmLevel)); + return true; + } + + /// Set/Unset the expansion level for an account + static bool HandleAccountSetAddonCommand(ChatHandler* handler, char const* args) + { + ///- Get the command line arguments + char* account = strtok((char*)args, " "); + char* exp = strtok(NULL, " "); + + if (!account) + return false; + + std::string accountName; + uint32 accountId; + + if (!exp) + { + Player* player = handler->getSelectedPlayer(); + if (!player) + return false; + + accountId = player->GetSession()->GetAccountId(); + AccountMgr::GetName(accountId, accountName); + exp = account; + } + else + { + ///- Convert Account name to Upper Format + accountName = account; + if (!AccountMgr::normalizeString(accountName)) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + accountId = AccountMgr::GetId(accountName); + if (!accountId) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + } + + // Let set addon state only for lesser (strong) security level + // or to self account + if (handler->GetSession() && handler->GetSession()->GetAccountId() != accountId && + handler->HasLowerSecurityAccount(NULL, accountId, true)) + return false; + + int expansion = atoi(exp); //get int anyway (0 if error) + if (expansion < 0 || uint8(expansion) > sWorld->getIntConfig(CONFIG_EXPANSION)) + return false; + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); + + stmt->setUInt8(0, expansion); + stmt->setUInt32(1, accountId); + + LoginDatabase.Execute(stmt); + + handler->PSendSysMessage(LANG_ACCOUNT_SETADDON, accountName.c_str(), accountId, expansion); + return true; + } + + static bool HandleAccountSetGmLevelCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + std::string targetAccountName; + uint32 targetAccountId = 0; + uint32 targetSecurity = 0; + uint32 gm = 0; + char* arg1 = strtok((char*)args, " "); + char* arg2 = strtok(NULL, " "); + char* arg3 = strtok(NULL, " "); + bool isAccountNameGiven = true; + + if (arg1 && !arg3) + { + if (!handler->getSelectedPlayer()) + return false; + isAccountNameGiven = false; + } + + // Check for second parameter + if (!isAccountNameGiven && !arg2) + return false; + + // Check for account + if (isAccountNameGiven) + { + targetAccountName = arg1; + if (!AccountMgr::normalizeString(targetAccountName)) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, targetAccountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + } + + // Check for invalid specified GM level. + gm = (isAccountNameGiven) ? atoi(arg2) : atoi(arg1); + if (gm > SEC_CONSOLE) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + + // handler->getSession() == NULL only for console + targetAccountId = (isAccountNameGiven) ? AccountMgr::GetId(targetAccountName) : handler->getSelectedPlayer()->GetSession()->GetAccountId(); + int32 gmRealmID = (isAccountNameGiven) ? atoi(arg3) : atoi(arg2); + uint32 playerSecurity; + if (handler->GetSession()) + playerSecurity = AccountMgr::GetSecurity(handler->GetSession()->GetAccountId(), gmRealmID); + else + playerSecurity = SEC_CONSOLE; + + // can set security level only for target with less security and to less security that we have + // This is also reject self apply in fact + targetSecurity = AccountMgr::GetSecurity(targetAccountId, gmRealmID); + if (targetSecurity >= playerSecurity || gm >= playerSecurity) + { + handler->SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); + handler->SetSentErrorMessage(true); + return false; + } + + // Check and abort if the target gm has a higher rank on one of the realms and the new realm is -1 + if (gmRealmID == -1 && !AccountMgr::IsConsoleAccount(playerSecurity)) + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST); + + stmt->setUInt32(0, targetAccountId); + stmt->setUInt8(1, uint8(gm)); + + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (result) + { + handler->SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); + handler->SetSentErrorMessage(true); + return false; + } + } + + // Check if provided realmID has a negative value other than -1 + if (gmRealmID < -1) + { + handler->SendSysMessage(LANG_INVALID_REALMID); + handler->SetSentErrorMessage(true); + return false; + } + + // If gmRealmID is -1, delete all values for the account id, else, insert values for the specific realmID + PreparedStatement* stmt; + + if (gmRealmID == -1) + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); + + stmt->setUInt32(0, targetAccountId); + } + else + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM); + + stmt->setUInt32(0, targetAccountId); + stmt->setUInt32(1, realmID); + } + + LoginDatabase.Execute(stmt); + + if (gm != 0) + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_ACCESS); + + stmt->setUInt32(0, targetAccountId); + stmt->setUInt8(1, uint8(gm)); + stmt->setInt32(2, gmRealmID); + + LoginDatabase.Execute(stmt); + } + + + handler->PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetAccountName.c_str(), gm); + return true; + } + + /// Set password for account + static bool HandleAccountSetPasswordCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + ///- Get the command line arguments + char* account = strtok((char*)args, " "); + char* password = strtok(NULL, " "); + char* passwordConfirmation = strtok(NULL, " "); + + if (!account || !password || !passwordConfirmation) + return false; + + std::string accountName = account; + if (!AccountMgr::normalizeString(accountName)) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + uint32 targetAccountId = AccountMgr::GetId(accountName); + if (!targetAccountId) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + /// can set password only for target with less security + /// This is also reject self apply in fact + if (handler->HasLowerSecurityAccount(NULL, targetAccountId, true)) + return false; + + if (strcmp(password, passwordConfirmation)) + { + handler->SendSysMessage(LANG_NEW_PASSWORDS_NOT_MATCH); + handler->SetSentErrorMessage(true); + return false; + } + + AccountOpResult result = AccountMgr::ChangePassword(targetAccountId, password); + + switch (result) + { + case AOR_OK: + handler->SendSysMessage(LANG_COMMAND_PASSWORD); + break; + case AOR_NAME_NOT_EXIST: + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + case AOR_PASS_TOO_LONG: + handler->SendSysMessage(LANG_PASSWORD_TOO_LONG); + handler->SetSentErrorMessage(true); + return false; + default: + handler->SendSysMessage(LANG_COMMAND_NOTCHANGEPASSWORD); + handler->SetSentErrorMessage(true); + return false; + } + return true; + } }; void AddSC_account_commandscript() { - new account_commandscript(); + new account_commandscript(); } diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 069f6f719b..fb627e3891 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -112,7 +112,7 @@ public: { std::string name = param1; WorldDatabase.EscapeString(name); - whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name " _LIKE_" '" << name << '\''; + whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name "_LIKE_" '" << name << '\''; } else whereClause << "WHERE guid = '" << guid << '\''; diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index 7b5c264352..9daaf82b78 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -244,7 +244,7 @@ public: WorldDatabase.EscapeString(name); result = WorldDatabase.PQuery( "SELECT guid, id, position_x, position_y, position_z, orientation, map, phaseMask, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ " - "FROM gameobject, gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name " _LIKE_" " _CONCAT3_("'%%'", "'%s'", "'%%'")" ORDER BY order_ ASC LIMIT 1", + "FROM gameobject, gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name "_LIKE_" "_CONCAT3_("'%%'", "'%s'", "'%%'")" ORDER BY order_ ASC LIMIT 1", player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), name.c_str()); } } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index d642f3f6fc..ed4e0c040d 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -260,7 +260,8 @@ public: return false; } - uint32 vendor_entry = vendor ? vendor->GetEntry() : 0; + char* addMulti = strtok(NULL, " "); + uint32 vendor_entry = addMulti ? handler->GetSession()->GetCurrentVendor() : vendor ? vendor->GetEntry() : 0; if (!sObjectMgr->IsVendorItemValid(vendor_entry, itemId, maxcount, incrtime, extendedcost, handler->GetSession()->GetPlayer())) { @@ -483,7 +484,8 @@ public: } uint32 itemId = atol(pitem); - if (!sObjectMgr->RemoveVendorItem(vendor->GetEntry(), itemId)) + char* addMulti = strtok(NULL, " "); + if (!sObjectMgr->RemoveVendorItem(addMulti ? handler->GetSession()->GetCurrentVendor() : vendor->GetEntry(), itemId)) { handler->PSendSysMessage(LANG_ITEM_NOT_IN_LIST, itemId); handler->SetSentErrorMessage(true); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index dac7bafc2e..3913adf095 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -363,7 +363,7 @@ public: static bool HandleReloadAutobroadcastCommand(ChatHandler* handler, const char* /*args*/) { sLog->outString("Re-Loading Autobroadcasts..."); - //sWorld->LoadAutobroadcasts(); + sWorld->LoadAutobroadcasts(); handler->SendGlobalGMSysMessage("DB table `autobroadcast` reloaded."); return true; } diff --git a/src/server/scripts/Custom/Multivendor/MultivendorExample.sql b/src/server/scripts/Custom/Multivendor/MultivendorExample.sql new file mode 100644 index 0000000000..ef94f213da --- /dev/null +++ b/src/server/scripts/Custom/Multivendor/MultivendorExample.sql @@ -0,0 +1,10 @@ +SET @ENTRY = (SELECT max(entry)+1 FROM creature_template); +SET @MENU_ID = (SELECT max(menu_id)+1 FROM gossip_menu_option); + +INSERT INTO `creature_template` (`entry`, `modelid1`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `speed_walk`, `speed_run`, `scale`, `rank`, `dmgschool`, `baseattacktime`, `rangeattacktime`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `trainer_type`, `trainer_spell`, `trainer_class`, `trainer_race`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `InhabitType`, `HoverHeight`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`) VALUES +(@ENTRY, 1298, "Herbert", "MultiVendor", NULL, @MENU_ID, 10, 10, 0, 35, 129, 1, 1.14286, 1, 0, 0, 1500, 0, 1, 512, 2048, 8, 0, 0, 0, 0, 0, 7, 138412032, 0, 0, 0, 0, 0, 0, 0, '', 0, 3, 1, 0, 0, 1, 0, 2, ''); + +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`) VALUES +(@MENU_ID, 0, 4, 'VendorTest 465', 3, 128, 465, 0, 0, 0, ''), +(@MENU_ID, 1, 9, 'VendorTest 54', 3, 128, 54, 0, 0, 0, ''), +(@MENU_ID, 2, 6, 'VendorTest 35574', 3, 128, 35574, 0, 0, 100, 'These goods are special, so pay up!'); diff --git a/src/server/scripts/Custom/Multivendor/README.md b/src/server/scripts/Custom/Multivendor/README.md new file mode 100644 index 0000000000..39680beb37 --- /dev/null +++ b/src/server/scripts/Custom/Multivendor/README.md @@ -0,0 +1,42 @@ +#Multivendor [](https://travis-ci.org/Rochet2/TrinityCore) + +####About +Allows you to show gossip options that show different vendors from npc_vendor.<br /> +Source: http://rochet2.github.io/Multivendor.html + +####Installation + +Available as: +- Direct merge: https://github.com/Rochet2/TrinityCore/tree/multivendor +- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...multivendor.diff +- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...multivendor + +Using direct merge: +- open git bash to source location +- do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git` +- do `git pull rochet2 multivendor` +- use cmake and compile + +Using diff: +- DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail. +- download the diff by __right clicking__ the link and select __Save link as__ +- place the downloaded `multivendor.diff` to the source root folder +- open git bash to source location +- do `git apply multivendor.diff` +- use cmake and compile + +####Usage +Set your NPC to have gossip and vendor NPCflags (129)<br /> +Add a gossip menu for him and add a new option to it.<br /> +The option needs to have option_id set to 3 so it acts as a vendor button,<br /> +npc_option_npcflag can be 1 or 128 (shows only if the NPC has vendor flag then)<br /> +and the action_menu_id is the vendor entry from npc_vendor that you want to show to the player.<br /> + +YOU CAN also send menus from C++. All you need to do is to provide the vendor entry to the<br /> +`void WorldSession::SendListInventory(ObjectGuid guid, uint32 vendorEntry)` function. + +The npc add item command was modified so you can add items to multivendors as well.<br /> +The last vendor window must have been the multivendor you want to add your item to.<br /> +After opening the window you need to type `.npc add item #itemId <#maxcount><#incrtime><#extendedcost> 1`<br /> +The 1 in the end specifies that you are adding the item to the multivendor currently open.<br /> +Same thing works with `.npc delete item #itemId 1` diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp index f8aad99be9..660994cfba 100644 --- a/src/server/shared/Database/Implementation/LoginDatabase.cpp +++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp @@ -55,7 +55,8 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_DEL_REALM_CHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_REALM_CHARACTERS, "INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_SUM_REALM_CHARACTERS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, sha_pass_hash, joindate) VALUES(?, ?, NOW())", CONNECTION_SYNCH); + PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, sha_pass_hash, joindate) VALUES(?, ?, NOW())", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_REALM_CHARACTERS_INIT, "INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist, account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_EXPANSION, "UPDATE account SET expansion = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK, "UPDATE account SET locked = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_USERNAME, "UPDATE account SET v = 0, s = 0, username = ?, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC); @@ -84,4 +85,5 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_WHOIS, "SELECT username, email, last_ip FROM account WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH); } diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h index 60acd47c44..8ccf28b64e 100644 --- a/src/server/shared/Database/Implementation/LoginDatabase.h +++ b/src/server/shared/Database/Implementation/LoginDatabase.h @@ -76,6 +76,7 @@ enum LoginDatabaseStatements LOGIN_INS_REALM_CHARACTERS, LOGIN_SEL_SUM_REALM_CHARACTERS, LOGIN_INS_ACCOUNT, + LOGIN_INS_REALM_CHARACTERS_INIT, LOGIN_UPD_EXPANSION, LOGIN_UPD_ACCOUNT_LOCK, LOGIN_UPD_USERNAME, @@ -104,6 +105,7 @@ enum LoginDatabaseStatements LOGIN_SEL_ACCOUNT_WHOIS, LOGIN_SEL_REALMLIST_SECURITY_LEVEL, LOGIN_DEL_ACCOUNT, + LOGIN_SEL_AUTOBROADCAST, MAX_LOGINDATABASE_STATEMENTS }; diff --git a/src/server/shared/Database/MySQLThreading.h b/src/server/shared/Database/MySQLThreading.h index b31f693c91..0b0a5e6cca 100644 --- a/src/server/shared/Database/MySQLThreading.h +++ b/src/server/shared/Database/MySQLThreading.h @@ -33,7 +33,7 @@ class MySQL static void Thread_Init() { mysql_thread_init(); - sLog->outSQLDriver("Core thread with ID [" UI64FMTD"] initializing MySQL thread.", + sLog->outSQLDriver("Core thread with ID ["UI64FMTD"] initializing MySQL thread.", (uint64)ACE_Based::Thread::currentId()); } @@ -44,7 +44,7 @@ class MySQL static void Thread_End() { mysql_thread_end(); - sLog->outSQLDriver("Core thread with ID [" UI64FMTD"] shutting down MySQL thread.", + sLog->outSQLDriver("Core thread with ID ["UI64FMTD"] shutting down MySQL thread.", (uint64)ACE_Based::Thread::currentId()); } diff --git a/src/server/shared/Debugging/WheatyExceptionReport.cpp b/src/server/shared/Debugging/WheatyExceptionReport.cpp index 52ad4939ee..3b6bd3d2cc 100644 --- a/src/server/shared/Debugging/WheatyExceptionReport.cpp +++ b/src/server/shared/Debugging/WheatyExceptionReport.cpp @@ -1080,10 +1080,10 @@ PVOID pAddress) switch (basicType) { case btChar: - pszCurrBuffer += sprintf(pszCurrBuffer, " = \" %s\"", pAddress); + pszCurrBuffer += sprintf(pszCurrBuffer, " = \"%s\"", pAddress); break; case btStdString: - pszCurrBuffer += sprintf(pszCurrBuffer, " = \" %s\"", static_cast<std::string*>(pAddress)->c_str()); + pszCurrBuffer += sprintf(pszCurrBuffer, " = \"%s\"", static_cast<std::string*>(pAddress)->c_str()); break; default: // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!) |