diff options
author | Shauren <shauren.trinity@gmail.com> | 2014-09-12 20:26:46 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2014-09-12 20:26:46 +0200 |
commit | 7f2cdfd166d99efdd308f0b4d195a6b972092dd9 (patch) | |
tree | 8871e168077155d8daabca787b223940f8774af9 /src | |
parent | be8111b0a21308e78435e9bc20bbbb2804d51c78 (diff) | |
parent | 6dff6b74256748f182ad65634a1ab16c5989d40e (diff) |
Merge branch 'master' of https://github.com/TrinityCore/TrinityCore into 4.3.4
Conflicts:
src/server/authserver/Main.cpp
src/server/authserver/Server/AuthSession.cpp
src/server/authserver/Server/AuthSession.h
src/server/game/Server/WorldSocket.cpp
src/server/game/Server/WorldSocket.h
src/server/scripts/World/npcs_special.cpp
Diffstat (limited to 'src')
48 files changed, 1621 insertions, 1289 deletions
diff --git a/src/server/authserver/CMakeLists.txt b/src/server/authserver/CMakeLists.txt index d6f0515a8e1..d7f1eb4fa30 100644 --- a/src/server/authserver/CMakeLists.txt +++ b/src/server/authserver/CMakeLists.txt @@ -44,6 +44,7 @@ endif() include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src/server/shared + ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration ${CMAKE_SOURCE_DIR}/src/server/shared/Database ${CMAKE_SOURCE_DIR}/src/server/shared/Debugging ${CMAKE_SOURCE_DIR}/src/server/shared/Packets diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index af7ecd15160..5c06a1e1c34 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -24,23 +24,21 @@ * authentication server */ - -#include "AsyncAcceptor.h" -#include "AuthSession.h" +#include "AuthSocketMgr.h" #include "BattlenetManager.h" #include "BattlenetSessionManager.h" #include "Common.h" -#include "Configuration/Config.h" -#include "Database/DatabaseEnv.h" +#include "Config.h" +#include "DatabaseEnv.h" #include "Log.h" #include "ProcessPriority.h" #include "RealmList.h" #include "SystemConfig.h" #include "Util.h" #include <cstdlib> +#include <iostream> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/program_options.hpp> -#include <iostream> #include <openssl/opensslv.h> #include <openssl/crypto.h> @@ -128,9 +126,10 @@ int main(int argc, char** argv) } std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - AsyncAcceptor<AuthSession> authServer(_ioService, bindIp, port); AsyncAcceptor<Battlenet::Session> bnetServer(_ioService, bindIp, bnport); + sAuthSocketMgr.StartNetwork(_ioService, bindIp, port); + // Set signal handlers boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM); #if PLATFORM == PLATFORM_WINDOWS diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 91a2ff9757a..0ba832af483 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -53,6 +53,7 @@ enum eStatus typedef struct AUTH_LOGON_CHALLENGE_C { + uint8 cmd; uint8 error; uint16 size; uint8 gamename[4]; @@ -71,6 +72,7 @@ typedef struct AUTH_LOGON_CHALLENGE_C typedef struct AUTH_LOGON_PROOF_C { + uint8 cmd; uint8 A[32]; uint8 M1[20]; uint8 crc_hash[20]; @@ -98,6 +100,7 @@ typedef struct AUTH_LOGON_PROOF_S_OLD typedef struct AUTH_RECONNECT_PROOF_C { + uint8 cmd; uint8 R1[16]; uint8 R2[20]; uint8 R3[20]; @@ -112,79 +115,88 @@ enum class BufferSizes : uint32 SRP_6_S = 0x20, }; -#define REALM_LIST_PACKET_SIZE 4 -#define XFER_ACCEPT_SIZE 0 -#define XFER_RESUME_SIZE 8 -#define XFER_CANCEL_SIZE 0 +#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4 +#define REALM_LIST_PACKET_SIZE 5 +#define XFER_ACCEPT_SIZE 1 +#define XFER_RESUME_SIZE 9 +#define XFER_CANCEL_SIZE 1 std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers() { std::unordered_map<uint8, AuthHandler> handlers; - handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CONNECTED, sizeof(AUTH_LOGON_CHALLENGE_C), &AuthSession::HandleLogonChallenge }; - handlers[AUTH_LOGON_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof }; - handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CONNECTED, sizeof(AUTH_LOGON_CHALLENGE_C), &AuthSession::HandleReconnectChallenge }; - handlers[AUTH_RECONNECT_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof }; - handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList }; - handlers[XFER_ACCEPT] = { STATUS_AUTHED, XFER_ACCEPT_SIZE, &AuthSession::HandleXferAccept }; - handlers[XFER_RESUME] = { STATUS_AUTHED, XFER_RESUME_SIZE, &AuthSession::HandleXferResume }; - handlers[XFER_CANCEL] = { STATUS_AUTHED, XFER_CANCEL_SIZE, &AuthSession::HandleXferCancel }; + handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CONNECTED, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge }; + handlers[AUTH_LOGON_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof }; + handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CONNECTED, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge }; + handlers[AUTH_RECONNECT_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof }; + handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList }; + handlers[XFER_ACCEPT] = { STATUS_AUTHED, XFER_ACCEPT_SIZE, &AuthSession::HandleXferAccept }; + handlers[XFER_RESUME] = { STATUS_AUTHED, XFER_RESUME_SIZE, &AuthSession::HandleXferResume }; + handlers[XFER_CANCEL] = { STATUS_AUTHED, XFER_CANCEL_SIZE, &AuthSession::HandleXferCancel }; return handlers; } std::unordered_map<uint8, AuthHandler> const Handlers = AuthSession::InitHandlers(); -void AuthSession::ReadHeaderHandler() +void AuthSession::ReadHandler() { - uint8 cmd = GetHeaderBuffer()[0]; - auto itr = Handlers.find(cmd); - if (itr != Handlers.end()) + MessageBuffer& packet = GetReadBuffer(); + while (packet.GetActiveSize()) { - // Handle dynamic size packet + uint8 cmd = packet.GetReadPointer()[0]; + auto itr = Handlers.find(cmd); + if (itr == Handlers.end()) + { + // well we dont handle this, lets just ignore it + packet.Reset(); + break; + } + + uint16 size = uint16(itr->second.packetSize); + if (packet.GetActiveSize() < size) + break; + if (cmd == AUTH_LOGON_CHALLENGE || cmd == AUTH_RECONNECT_CHALLENGE) { - ReadData(sizeof(uint8) + sizeof(uint16)); //error + size - sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetDataBuffer()); + sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(packet.GetReadPointer()); + size += challenge->size; + } - AsyncReadData(challenge->size); + if (packet.GetActiveSize() < size) + break; + + if (!(*this.*Handlers.at(cmd).handler)()) + { + CloseSocket(); + return; } - else - AsyncReadData(itr->second.packetSize); - } - else - CloseSocket(); -} -void AuthSession::ReadDataHandler() -{ - if (!(*this.*Handlers.at(GetHeaderBuffer()[0]).handler)()) - { - CloseSocket(); - return; + packet.ReadCompleted(size); } - AsyncReadHeader(); + AsyncRead(); } -void AuthSession::AsyncWrite(ByteBuffer& packet) +void AuthSession::SendPacket(ByteBuffer& packet) { if (!IsOpen()) return; - std::lock_guard<std::mutex> guard(_writeLock); - - bool needsWriteStart = _writeQueue.empty(); + if (!packet.empty()) + { + MessageBuffer buffer; + buffer.Write(packet.contents(), packet.size()); - _writeQueue.push(std::move(packet)); + std::unique_lock<std::mutex> guard(_writeLock); - if (needsWriteStart) - AuthSocket::AsyncWrite(_writeQueue.front()); + QueuePacket(std::move(buffer), guard); + } } bool AuthSession::HandleLogonChallenge() { - sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetDataBuffer()); + sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer()); //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size); TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I); @@ -395,7 +407,7 @@ bool AuthSession::HandleLogonChallenge() pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); } - AsyncWrite(pkt); + SendPacket(pkt); return true; } @@ -405,7 +417,7 @@ bool AuthSession::HandleLogonProof() TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof"); // Read the packet - sAuthLogonProof_C *logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetDataBuffer()); + sAuthLogonProof_C *logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetReadBuffer().GetReadPointer()); // If the client has no valid version if (_expversion == NO_VALID_EXP_FLAG) @@ -512,10 +524,9 @@ bool AuthSession::HandleLogonProof() // Check auth token if ((logonProof->securityFlags & 0x04) || !_tokenKey.empty()) { - ReadData(1); - uint8 size = *(GetDataBuffer() + sizeof(sAuthLogonProof_C)); - ReadData(size); - std::string token(reinterpret_cast<char*>(GetDataBuffer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size); + uint8 size = *(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C)); + std::string token(reinterpret_cast<char*>(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size); + GetReadBuffer().ReadCompleted(sizeof(size) + size); uint32 validToken = TOTP::GenerateToken(_tokenKey.c_str()); uint32 incomingToken = atoi(token.c_str()); if (validToken != incomingToken) @@ -525,7 +536,7 @@ bool AuthSession::HandleLogonProof() packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); packet << uint8(3); packet << uint8(0); - AsyncWrite(packet); + SendPacket(packet); return false; } } @@ -556,7 +567,7 @@ bool AuthSession::HandleLogonProof() std::memcpy(packet.contents(), &proof, sizeof(proof)); } - AsyncWrite(packet); + SendPacket(packet); _isAuthenticated = true; } else @@ -566,7 +577,7 @@ bool AuthSession::HandleLogonProof() packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); packet << uint8(3); packet << uint8(0); - AsyncWrite(packet); + SendPacket(packet); TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str()); @@ -635,7 +646,7 @@ bool AuthSession::HandleLogonProof() bool AuthSession::HandleReconnectChallenge() { TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectChallenge"); - sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetDataBuffer()); + sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer()); //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size); TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I); @@ -679,14 +690,14 @@ bool AuthSession::HandleReconnectChallenge() pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros - AsyncWrite(pkt); + SendPacket(pkt); return true; } bool AuthSession::HandleReconnectProof() { TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof"); - sAuthReconnectProof_C *reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetDataBuffer()); + sAuthReconnectProof_C *reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetReadBuffer().GetReadPointer()); if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) return false; @@ -707,7 +718,7 @@ bool AuthSession::HandleReconnectProof() pkt << uint8(AUTH_RECONNECT_PROOF); pkt << uint8(0x00); pkt << uint16(0x00); // 2 bytes zeros - AsyncWrite(pkt); + SendPacket(pkt); _isAuthenticated = true; return true; } @@ -832,7 +843,7 @@ bool AuthSession::HandleRealmList() hdr << uint16(pkt.size() + RealmListSizeBuffer.size()); hdr.append(RealmListSizeBuffer); // append RealmList's size buffer hdr.append(pkt); // append realms in the realmlist - AsyncWrite(hdr); + SendPacket(hdr); return true; } diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h index 04ee339df8e..07af61d9c1d 100644 --- a/src/server/authserver/Server/AuthSession.h +++ b/src/server/authserver/Server/AuthSession.h @@ -30,14 +30,12 @@ using boost::asio::ip::tcp; struct AuthHandler; -class AuthSession : public Socket<AuthSession, ByteBuffer> +class AuthSession : public Socket<AuthSession> { - typedef Socket<AuthSession, ByteBuffer> AuthSocket; - public: static std::unordered_map<uint8, AuthHandler> InitHandlers(); - AuthSession(tcp::socket&& socket) : Socket(std::move(socket), 1), + AuthSession(tcp::socket&& socket) : Socket(std::move(socket)), _isAuthenticated(false), _build(0), _expversion(0), _accountSecurityLevel(SEC_PLAYER) { N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); @@ -46,14 +44,13 @@ public: void Start() override { - AsyncReadHeader(); + AsyncRead(); } - void AsyncWrite(ByteBuffer& packet); + void SendPacket(ByteBuffer& packet); protected: - void ReadHeaderHandler() override; - void ReadDataHandler() override; + void ReadHandler() override; private: bool HandleLogonChallenge(); diff --git a/src/server/authserver/Server/AuthSocketMgr.h b/src/server/authserver/Server/AuthSocketMgr.h new file mode 100644 index 00000000000..a14ee26ed56 --- /dev/null +++ b/src/server/authserver/Server/AuthSocketMgr.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008-2014 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/>. + */ + +#ifndef AuthSocketMgr_h__ +#define AuthSocketMgr_h__ + +#include "SocketMgr.h" +#include "AuthSession.h" + +void OnSocketAccept(tcp::socket&& sock); + +class AuthSocketMgr : public SocketMgr<AuthSession> +{ + typedef SocketMgr<AuthSession> BaseSocketMgr; + +public: + static AuthSocketMgr& Instance() + { + static AuthSocketMgr instance; + return instance; + } + + bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) override + { + if (!BaseSocketMgr::StartNetwork(service, bindIp, port)) + return false; + + _acceptor->AsyncAcceptManaged(&OnSocketAccept); + return true; + } + +protected: + NetworkThread<AuthSession>* CreateThreads() const override + { + return new NetworkThread<AuthSession>[1]; + } +}; + +#define sAuthSocketMgr AuthSocketMgr::Instance() + +void OnSocketAccept(tcp::socket&& sock) +{ + sAuthSocketMgr.OnSocketOpen(std::forward<tcp::socket>(sock)); +} + + +#endif // AuthSocketMgr_h__ diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h index 9c9a39cf567..df930367878 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h @@ -72,7 +72,7 @@ class AuctionBotBuyer : public AuctionBotAgent { public: AuctionBotBuyer(); - ~AuctionBotBuyer() override; + ~AuctionBotBuyer(); bool Initialize() override; bool Update(AuctionHouseType houseType) override; diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h index 014fe23f71a..42677a52f49 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h @@ -118,7 +118,7 @@ public: typedef std::vector<uint32> ItemPool; AuctionBotSeller(); - ~AuctionBotSeller() override; + ~AuctionBotSeller(); bool Initialize() override; bool Update(AuctionHouseType houseType) override; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ee8fee198bf..bab96b15292 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3878,7 +3878,8 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent if (!pSkill) continue; - if (_spell_idx->second->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->id)) + ///@todo: confirm if rogues start with lockpicking skill at level 1 but only receive the spell to use it at level 16 + if ((_spell_idx->second->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->id)) || (pSkill->id == SKILL_LOCKPICKING && _spell_idx->second->max_value == 0)) LearnDefaultSkill(pSkill->id, 0); } } @@ -17943,6 +17944,7 @@ bool Player::isAllowedToLoot(const Creature* creature) switch (thisGroup->GetLootMethod()) { + case MASTER_LOOT: case FREE_FOR_ALL: return true; case ROUND_ROBIN: @@ -17952,7 +17954,6 @@ bool Player::isAllowedToLoot(const Creature* creature) return true; return loot->hasItemFor(this); - case MASTER_LOOT: case GROUP_LOOT: case NEED_BEFORE_GREED: // may only loot if the player is the loot roundrobin player @@ -23664,6 +23665,8 @@ void Player::LearnDefaultSkill(uint32 skillId, uint16 rank) skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue); else if (skillId == SKILL_FIST_WEAPONS) skillValue = std::max<uint16>(1, GetSkillValue(SKILL_UNARMED)); + else if (skillId == SKILL_LOCKPICKING) + skillValue = std::max<uint16>(1, GetSkillValue(SKILL_LOCKPICKING)); SetSkill(skillId, 0, skillValue, maxValue); break; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f6615d67c99..6f37f74b607 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -14108,7 +14108,8 @@ void Unit::SetFeared(bool apply) } if (Player* player = ToPlayer()) - player->SetClientControl(this, !apply); + if(!player->HasUnitState(UNIT_STATE_POSSESSED)) + player->SetClientControl(this, !apply); } void Unit::SetConfused(bool apply) @@ -14130,7 +14131,8 @@ void Unit::SetConfused(bool apply) } if (Player* player = ToPlayer()) - player->SetClientControl(this, !apply); + if (!player->HasUnitState(UNIT_STATE_POSSESSED)) + player->SetClientControl(this, !apply); } bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* aurApp) diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 638cc19784a..fd89ed80429 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -226,7 +226,7 @@ void Group::LoadMemberFromDB(uint32 guidLow, uint8 memberFlags, uint8 subgroup, void Group::ConvertToLFG() { - m_groupType = GroupType(m_groupType | GROUPTYPE_LFG | GROUPTYPE_UNK1); + m_groupType = GroupType(m_groupType | GROUPTYPE_LFG | GROUPTYPE_LFG_RESTRICTED); m_lootMethod = NEED_BEFORE_GREED; if (!isBGGroup() && !isBFGroup()) { diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index 4559c5841ae..84e3a5454cd 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -82,13 +82,15 @@ enum GroupMemberAssignment enum GroupType { - GROUPTYPE_NORMAL = 0x00, - GROUPTYPE_BG = 0x01, - GROUPTYPE_RAID = 0x02, - GROUPTYPE_BGRAID = GROUPTYPE_BG | GROUPTYPE_RAID, // mask - GROUPTYPE_UNK1 = 0x04, - GROUPTYPE_LFG = 0x08 + GROUPTYPE_NORMAL = 0x00, + GROUPTYPE_BG = 0x01, + GROUPTYPE_RAID = 0x02, + GROUPTYPE_BGRAID = GROUPTYPE_BG | GROUPTYPE_RAID, // mask + GROUPTYPE_LFG_RESTRICTED = 0x04, // Script_HasLFGRestrictions() + GROUPTYPE_LFG = 0x08, // 0x10, leave/change group?, I saw this flag when leaving group and after leaving BG while in group + // GROUPTYPE_ONE_PERSON_PARTY = 0x20, 4.x Script_IsOnePersonParty() + // GROUPTYPE_EVERYONE_ASSISTANT = 0x40 4.x Script_IsEveryoneAssistant() }; enum GroupUpdateFlags diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index cfb7df6c34e..ac18a10c9a9 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -310,10 +310,14 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket& recvData) { if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(nextQuest, true)) { - if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(nextQuest, true)) - _player->AddQuestAndCheckCompletion(nextQuest, object); - - _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true); + // Only send the quest to the player if the conditions are met + if (_player->CanTakeQuest(nextQuest, true)) + { + if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true)) + _player->AddQuestAndCheckCompletion(nextQuest, object); + + _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true); + } } _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true); @@ -334,10 +338,14 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket& recvData) { if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(nextQuest, true)) { - if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(nextQuest, true)) - _player->AddQuestAndCheckCompletion(nextQuest, object); - - _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true); + // Only send the quest to the player if the conditions are met + if (_player->CanTakeQuest(nextQuest, true)) + { + if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true)) + _player->AddQuestAndCheckCompletion(nextQuest, object); + + _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true); + } } _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true); diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 60475382d5e..26d348737ab 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -902,8 +902,13 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING; break; case MASTER_PERMISSION: - slot_type = LOOT_SLOT_TYPE_MASTER; + { + if (lv.viewer->GetGroup() && lv.viewer->GetGroup()->GetMasterLooterGuid() == lv.viewer->GetGUID()) + slot_type = LOOT_SLOT_TYPE_MASTER; + else + slot_type = LOOT_SLOT_TYPE_LOCKED; break; + } case RESTRICTED_PERMISSION: slot_type = LOOT_SLOT_TYPE_LOCKED; break; diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index feef22c8a21..6d0ea9ea7a4 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -268,7 +268,7 @@ void WorldSession::SendPacket(WorldPacket* packet, bool forced /*= false*/) sScriptMgr->OnPacketSend(this, *packet); - m_Socket->AsyncWrite(*packet); + m_Socket->SendPacket(*packet); } /// Add an incoming packet to the queue diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 9a7b212da04..6cf406f21fa 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -32,19 +32,24 @@ std::string const WorldSocket::ServerConnectionInitialize("WORLD OF WARCRAFT CON std::string const WorldSocket::ClientConnectionInitialize("WORLD OF WARCRAFT CONNECTION - CLIENT TO SERVER"); -WorldSocket::WorldSocket(tcp::socket&& socket) : Socket(std::move(socket), sizeof(ClientPktHeader)), - _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), _initialized(false) + +WorldSocket::WorldSocket(tcp::socket&& socket) + : Socket(std::move(socket)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), _initialized(false) { + _headerBuffer.Resize(sizeof(ClientPktHeader)); } void WorldSocket::Start() { - sScriptMgr->OnSocketOpen(shared_from_this()); + AsyncRead(); - AsyncReadData(ClientConnectionInitialize.length() + 2 /*sizeof(ClientPktHeader::size)*/ + 1 /*null terminator*/); + MessageBuffer initializer; + ServerPktHeader header(ServerConnectionInitialize.size(), 0); + initializer.Write(header.header, header.getHeaderLength() - 2); + initializer.Write((void*)ServerConnectionInitialize.c_str(), ServerConnectionInitialize.length()); - _writeQueue.emplace(ServerConnectionInitialize); - AsyncWrite(_writeQueue.front()); + std::unique_lock<std::mutex> dummy(_writeLock, std::defer_lock); + QueuePacket(std::move(initializer), dummy); } void WorldSocket::HandleSendAuthSession() @@ -60,14 +65,69 @@ void WorldSocket::HandleSendAuthSession() packet << uint32(_authSeed); packet << uint8(1); - AsyncWrite(packet); + SendPacket(packet); +} + +void WorldSocket::ReadHandler() +{ + if (!IsOpen()) + return; + + MessageBuffer& packet = GetReadBuffer(); + while (packet.GetActiveSize() > 0) + { + if (_headerBuffer.GetRemainingSpace() > 0) + { + // need to receive the header + std::size_t readHeaderSize = std::min(packet.GetActiveSize(), _headerBuffer.GetRemainingSpace()); + _headerBuffer.Write(packet.GetReadPointer(), readHeaderSize); + packet.ReadCompleted(readHeaderSize); + + if (_headerBuffer.GetRemainingSpace() > 0) + { + // Couldn't receive the whole header this time. + ASSERT(packet.GetActiveSize() == 0); + break; + } + + // We just received nice new header + if (!ReadHeaderHandler()) + return; + } + + // We have full read header, now check the data payload + if (_packetBuffer.GetRemainingSpace() > 0) + { + // need more data in the payload + std::size_t readDataSize = std::min(packet.GetActiveSize(), _packetBuffer.GetRemainingSpace()); + _packetBuffer.Write(packet.GetReadPointer(), readDataSize); + packet.ReadCompleted(readDataSize); + + if (_packetBuffer.GetRemainingSpace() > 0) + { + // Couldn't receive the whole data this time. + ASSERT(packet.GetActiveSize() == 0); + break; + } + } + + // just received fresh new payload + if (!ReadDataHandler()) + return; + + _headerBuffer.Reset(); + } + + AsyncRead(); } -void WorldSocket::ReadHeaderHandler() +bool WorldSocket::ReadHeaderHandler() { - _authCrypt.DecryptRecv(GetHeaderBuffer(), sizeof(ClientPktHeader)); + ASSERT(_headerBuffer.GetActiveSize() == sizeof(ClientPktHeader)); + + _authCrypt.DecryptRecv(_headerBuffer.GetReadPointer(), sizeof(ClientPktHeader)); - ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(GetHeaderBuffer()); + ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(_headerBuffer.GetReadPointer()); EndianConvertReverse(header->size); EndianConvert(header->cmd); @@ -81,26 +141,28 @@ void WorldSocket::ReadHeaderHandler() } else TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %hu, cmd: %u)", - GetRemoteIpAddress().to_string().c_str(), header->size, header->cmd); + GetRemoteIpAddress().to_string().c_str(), header->size, header->cmd); CloseSocket(); - return; + return false; } - AsyncReadData(header->size - sizeof(header->cmd)); + header->size -= sizeof(header->cmd); + _packetBuffer.Resize(header->size); + return true; } -void WorldSocket::ReadDataHandler() +bool WorldSocket::ReadDataHandler() { if (_initialized) { - ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(GetHeaderBuffer()); + ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(_headerBuffer.GetReadPointer()); Opcodes opcode = PacketFilter::DropHighBytes(Opcodes(header->cmd)); std::string opcodeName = GetOpcodeNameForLogging(opcode); - WorldPacket packet(opcode, MoveData()); + WorldPacket packet(opcode, std::move(_packetBuffer)); if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort()); @@ -129,7 +191,7 @@ void WorldSocket::ReadDataHandler() packet.rfinish(); // contains uint32 disconnectReason; TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); sScriptMgr->OnPacketReceive(_worldSession, packet); - return; + return true; case CMSG_ENABLE_NAGLE: { TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); @@ -144,7 +206,7 @@ void WorldSocket::ReadDataHandler() { TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); CloseSocket(); - return; + return false; } // Our Idle timer will reset on any non PING opcodes. @@ -159,23 +221,23 @@ void WorldSocket::ReadDataHandler() } else { - ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(GetDataBuffer()); + ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(_headerBuffer.GetReadPointer()); - std::string initializer(reinterpret_cast<char const*>(header) + sizeof(header->size)); + std::string initializer(reinterpret_cast<char const*>(_packetBuffer.GetReadPointer()), header->size); if (initializer != ClientConnectionInitialize) { CloseSocket(); - return; + return false; } _initialized = true; HandleSendAuthSession(); } - AsyncReadHeader(); + return true; } -void WorldSocket::AsyncWrite(WorldPacket& packet) +void WorldSocket::SendPacket(WorldPacket& packet) { if (!IsOpen()) return; @@ -190,15 +252,27 @@ void WorldSocket::AsyncWrite(WorldPacket& packet) ServerPktHeader header(packet.size() + 2, packet.GetOpcode()); - std::lock_guard<std::mutex> guard(_writeLock); + std::unique_lock<std::mutex> guard(_writeLock); - bool needsWriteStart = _writeQueue.empty(); _authCrypt.EncryptSend(header.header, header.getHeaderLength()); - _writeQueue.emplace(header, packet); +#ifndef BOOST_ASIO_HAS_IOCP + if (_writeQueue.empty() && _writeBuffer.GetRemainingSpace() >= header.getHeaderLength() + packet.size()) + { + _writeBuffer.Write(header.header, header.getHeaderLength()); + if (!packet.empty()) + _writeBuffer.Write(packet.contents(), packet.size()); + } + else +#endif + { + MessageBuffer buffer(header.getHeaderLength() + packet.size()); + buffer.Write(header.header, header.getHeaderLength()); + if (!packet.empty()) + buffer.Write(packet.contents(), packet.size()); - if (needsWriteStart) - AsyncWrite(_writeQueue.front()); + QueuePacket(std::move(buffer), guard); + } } void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) @@ -480,7 +554,7 @@ void WorldSocket::SendAuthResponseError(uint8 code) packet.WriteBit(0); // has account info packet << uint8(code); - AsyncWrite(packet); + SendPacket(packet); } void WorldSocket::HandlePing(WorldPacket& recvPacket) @@ -541,12 +615,5 @@ void WorldSocket::HandlePing(WorldPacket& recvPacket) WorldPacket packet(SMSG_PONG, 4); packet << ping; - return AsyncWrite(packet); -} - -void WorldSocket::CloseSocket() -{ - sScriptMgr->OnSocketClose(shared_from_this()); - - Socket::CloseSocket(); + return SendPacket(packet); } diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 9e5f785d939..6ba76584909 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -19,16 +19,6 @@ #ifndef __WORLDSOCKET_H__ #define __WORLDSOCKET_H__ -// Forward declare buffer function here - Socket.h must know about it -struct WorldPacketBuffer; -namespace boost -{ - namespace asio - { - WorldPacketBuffer const& buffer(WorldPacketBuffer const& buf); - } -} - #include "Common.h" #include "WorldPacketCrypt.h" #include "ServerPktHeader.h" @@ -54,58 +44,8 @@ struct ClientPktHeader #pragma pack(pop) -struct WorldPacketBuffer -{ - typedef boost::asio::const_buffer value_type; - - typedef boost::asio::const_buffer const* const_iterator; - - WorldPacketBuffer(ServerPktHeader header, WorldPacket const& packet) : _header(header), _packet(packet), _bufferCount(0) - { - _buffers[_bufferCount++] = boost::asio::const_buffer(_header.header, _header.getHeaderLength()); - if (!_packet.empty()) - _buffers[_bufferCount++] = boost::asio::const_buffer(_packet.contents(), _packet.size()); - } - - WorldPacketBuffer(std::string const& str) : _header(str.length() + 1 /*null terminator*/, 0), _packet(), _bufferCount(0) - { - _buffers[_bufferCount++] = boost::asio::const_buffer(_header.header, _header.getHeaderLength() - 2 /*sizeof(opcode)*/); - if (!str.empty()) - _buffers[_bufferCount++] = boost::asio::const_buffer(str.c_str(), _header.size); - } - - const_iterator begin() const - { - return _buffers; - } - - const_iterator end() const - { - return _buffers + _bufferCount; - } - -private: - boost::asio::const_buffer _buffers[2]; - ServerPktHeader _header; - WorldPacket _packet; - uint32 _bufferCount; -}; - -namespace boost -{ - namespace asio - { - inline WorldPacketBuffer const& buffer(WorldPacketBuffer const& buf) - { - return buf; - } - } -} - -class WorldSocket : public Socket<WorldSocket, WorldPacketBuffer> +class WorldSocket : public Socket<WorldSocket> { - typedef Socket<WorldSocket, WorldPacketBuffer> Base; - static std::string const ServerConnectionInitialize; static std::string const ClientConnectionInitialize; @@ -118,14 +58,12 @@ public: void Start() override; - void CloseSocket() override; - - using Base::AsyncWrite; - void AsyncWrite(WorldPacket& packet); + void SendPacket(WorldPacket& packet); protected: - void ReadHeaderHandler() override; - void ReadDataHandler() override; + void ReadHandler() override; + bool ReadHeaderHandler(); + bool ReadDataHandler(); private: void HandleSendAuthSession(); @@ -141,6 +79,10 @@ private: uint32 _OverSpeedPings; WorldSession* _worldSession; + + MessageBuffer _headerBuffer; + MessageBuffer _packetBuffer; + bool _initialized; }; diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp new file mode 100644 index 00000000000..21f62fa265c --- /dev/null +++ b/src/server/game/Server/WorldSocketMgr.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * 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 "Config.h" +#include "NetworkThread.h" +#include "ScriptMgr.h" +#include "WorldSocket.h" +#include "WorldSocketMgr.h" +#include <boost/system/error_code.hpp> + +static void OnSocketAccept(tcp::socket&& sock) +{ + sWorldSocketMgr.OnSocketOpen(std::forward<tcp::socket>(sock)); +} + +class WorldSocketThread : public NetworkThread<WorldSocket> +{ +public: + void SocketAdded(std::shared_ptr<WorldSocket> sock) override + { + sScriptMgr->OnSocketOpen(sock); + } + + void SocketRemoved(std::shared_ptr<WorldSocket> sock) override + { + sScriptMgr->OnSocketClose(sock); + } +}; + +WorldSocketMgr::WorldSocketMgr() : BaseSocketMgr(), _socketSendBufferSize(-1), m_SockOutUBuff(65536), _tcpNoDelay(true) +{ +} + +bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) +{ + _tcpNoDelay = sConfigMgr->GetBoolDefault("Network.TcpNodelay", true); + + TC_LOG_DEBUG("misc", "Max allowed socket connections %d", boost::asio::socket_base::max_connections); + + // -1 means use default + _socketSendBufferSize = sConfigMgr->GetIntDefault("Network.OutKBuff", -1); + + m_SockOutUBuff = sConfigMgr->GetIntDefault("Network.OutUBuff", 65536); + + if (m_SockOutUBuff <= 0) + { + TC_LOG_ERROR("misc", "Network.OutUBuff is wrong in your config file"); + return false; + } + + BaseSocketMgr::StartNetwork(service, bindIp, port); + + _acceptor->AsyncAcceptManaged(&OnSocketAccept); + + sScriptMgr->OnNetworkStart(); + return true; +} + +void WorldSocketMgr::StopNetwork() +{ + BaseSocketMgr::StopNetwork(); + + sScriptMgr->OnNetworkStop(); +} + +void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock) +{ + // set some options here + if (_socketSendBufferSize >= 0) + { + boost::system::error_code err; + sock.set_option(boost::asio::socket_base::send_buffer_size(_socketSendBufferSize), err); + if (err && err != boost::system::errc::not_supported) + { + TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::socket_base::send_buffer_size) err = %s", err.message().c_str()); + return; + } + } + + // Set TCP_NODELAY. + if (_tcpNoDelay) + { + boost::system::error_code err; + sock.set_option(boost::asio::ip::tcp::no_delay(true), err); + if (err) + { + TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::ip::tcp::no_delay) err = %s", err.message().c_str()); + return; + } + } + + //sock->m_OutBufferSize = static_cast<size_t> (m_SockOutUBuff); + + BaseSocketMgr::OnSocketOpen(std::forward<tcp::socket>(sock)); +} + +NetworkThread<WorldSocket>* WorldSocketMgr::CreateThreads() const +{ + return new WorldSocketThread[GetNetworkThreadCount()]; +} diff --git a/src/server/game/Server/WorldSocketMgr.h b/src/server/game/Server/WorldSocketMgr.h new file mode 100644 index 00000000000..92a28d0c135 --- /dev/null +++ b/src/server/game/Server/WorldSocketMgr.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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/>. + */ + +/** \addtogroup u2w User to World Communication + * @{ + * \file WorldSocketMgr.h + * \author Derex <derex101@gmail.com> + */ + +#ifndef __WORLDSOCKETMGR_H +#define __WORLDSOCKETMGR_H + +#include "SocketMgr.h" + +class WorldSocket; + +/// Manages all sockets connected to peers and network threads +class WorldSocketMgr : public SocketMgr<WorldSocket> +{ + typedef SocketMgr<WorldSocket> BaseSocketMgr; + +public: + static WorldSocketMgr& Instance() + { + static WorldSocketMgr instance; + return instance; + } + + /// Start network, listen at address:port . + bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) override; + + /// Stops all network threads, It will wait for all running threads . + void StopNetwork() override; + + void OnSocketOpen(tcp::socket&& sock) override; + +protected: + WorldSocketMgr(); + + NetworkThread<WorldSocket>* CreateThreads() const override; + +private: + int32 _socketSendBufferSize; + int32 m_SockOutUBuff; + bool _tcpNoDelay; +}; + +#define sWorldSocketMgr WorldSocketMgr::Instance() + +#endif +/// @} diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp b/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp index ca46ff36079..651e487522d 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp +++ b/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp @@ -72,7 +72,7 @@ public: void Reset() override { Initialize(); - + events.Reset(); summons.DespawnAll(); } diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp b/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp index 49c5a61b7d0..d9b0c1490b5 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp +++ b/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp @@ -30,12 +30,21 @@ enum Spells SPELL_STORMPIKE = 51876 // not sure }; -enum Yells +enum Texts { - YELL_AGGRO = 0, - YELL_EVADE = 1, - YELL_RESPAWN = 2, - YELL_RANDOM = 3 + SAY_AGGRO = 0, + SAY_EVADE = 1, + SAY_RESPAWN = 2, + SAY_RANDOM = 3 +}; + +enum Events +{ + EVENT_WHIRLWIND = 1, + EVENT_WHIRLWIND2, + EVENT_KNOCKDOWN, + EVENT_FRENZY, + EVENT_RANDOM_YELL }; class boss_drekthar : public CreatureScript @@ -45,92 +54,85 @@ public: struct boss_drektharAI : public ScriptedAI { - boss_drektharAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - WhirlwindTimer = urand(1 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); - Whirlwind2Timer = urand(1 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); - KnockdownTimer = 12 * IN_MILLISECONDS; - FrenzyTimer = 6 * IN_MILLISECONDS; - ResetTimer = 5 * IN_MILLISECONDS; - YellTimer = urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); //20 to 30 seconds - } - - uint32 WhirlwindTimer; - uint32 Whirlwind2Timer; - uint32 KnockdownTimer; - uint32 FrenzyTimer; - uint32 YellTimer; - uint32 ResetTimer; + boss_drektharAI(Creature* creature) : ScriptedAI(creature) { } void Reset() override { - Initialize(); + events.Reset(); } void EnterCombat(Unit* /*who*/) override { - Talk(YELL_AGGRO); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_WHIRLWIND, urand(1 * IN_MILLISECONDS, 20 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_WHIRLWIND2, urand(1 * IN_MILLISECONDS, 20 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_KNOCKDOWN, 12 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_FRENZY, 6 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_RANDOM_YELL, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); //20 to 30 seconds } void JustRespawned() override { Reset(); - Talk(YELL_RESPAWN); + Talk(SAY_RESPAWN); } - void UpdateAI(uint32 diff) override + bool CheckInRoom() { - if (!UpdateVictim()) - return; - - if (WhirlwindTimer <= diff) + if (me->GetDistance2d(me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY()) > 50) { - DoCastVictim(SPELL_WHIRLWIND); - WhirlwindTimer = urand(8 * IN_MILLISECONDS, 18 * IN_MILLISECONDS); - } else WhirlwindTimer -= diff; + EnterEvadeMode(); + Talk(SAY_EVADE); + return false; + } - if (Whirlwind2Timer <= diff) - { - DoCastVictim(SPELL_WHIRLWIND2); - Whirlwind2Timer = urand(7 * IN_MILLISECONDS, 25 * IN_MILLISECONDS); - } else Whirlwind2Timer -= diff; + return true; + } - if (KnockdownTimer <= diff) - { - DoCastVictim(SPELL_KNOCKDOWN); - KnockdownTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); - } else KnockdownTimer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || !CheckInRoom()) + return; - if (FrenzyTimer <= diff) - { - DoCastVictim(SPELL_FRENZY); - FrenzyTimer = urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); - } else FrenzyTimer -= diff; + events.Update(diff); - if (YellTimer <= diff) - { - Talk(YELL_RANDOM); - YellTimer = urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); //20 to 30 seconds - } else YellTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - // check if creature is not outside of building - if (ResetTimer <= diff) + while (uint32 eventId = events.ExecuteEvent()) { - if (me->GetDistance2d(me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY()) > 50) + switch (eventId) { - EnterEvadeMode(); - Talk(YELL_EVADE); + case EVENT_WHIRLWIND: + DoCastVictim(SPELL_WHIRLWIND); + events.ScheduleEvent(EVENT_WHIRLWIND, urand(8 * IN_MILLISECONDS, 18 * IN_MILLISECONDS)); + break; + case EVENT_WHIRLWIND2: + DoCastVictim(SPELL_WHIRLWIND2); + events.ScheduleEvent(EVENT_WHIRLWIND2, urand(7 * IN_MILLISECONDS, 25 * IN_MILLISECONDS)); + break; + case EVENT_KNOCKDOWN: + DoCastVictim(SPELL_KNOCKDOWN); + events.ScheduleEvent(EVENT_KNOCKDOWN, urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS)); + break; + case EVENT_FRENZY: + DoCastVictim(SPELL_FRENZY); + events.ScheduleEvent(EVENT_FRENZY, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); + break; + case EVENT_RANDOM_YELL: + Talk(SAY_RANDOM); + events.ScheduleEvent(EVENT_RANDOM_YELL, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); + break; + default: + break; } - ResetTimer = 5 * IN_MILLISECONDS; - } else ResetTimer -= diff; + } DoMeleeAttackIfReady(); } + + private: + EventMap events; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_galvangar.cpp b/src/server/scripts/EasternKingdoms/AlteracValley/boss_galvangar.cpp index e422975bd1a..0c029537add 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_galvangar.cpp +++ b/src/server/scripts/EasternKingdoms/AlteracValley/boss_galvangar.cpp @@ -27,11 +27,20 @@ enum Spells SPELL_MORTAL_STRIKE = 16856 }; -enum Yells +enum Texts { - YELL_AGGRO = 0, - YELL_EVADE = 1, - YELL_BUFF = 2 + SAY_AGGRO = 0, + SAY_EVADE = 1, + SAY_BUFF = 2 +}; + +enum Events +{ + EVENT_CLEAVE = 1, + EVENT_FRIGHTENING_SHOUT, + EVENT_WHIRLWIND1, + EVENT_WHIRLWIND2, + EVENT_MORTAL_STRIKE }; enum Action @@ -46,97 +55,85 @@ public: struct boss_galvangarAI : public ScriptedAI { - boss_galvangarAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - CleaveTimer = urand(1 * IN_MILLISECONDS, 9 * IN_MILLISECONDS); - FrighteningShoutTimer = urand(2 * IN_MILLISECONDS, 19 * IN_MILLISECONDS); - Whirlwind1Timer = urand(1 * IN_MILLISECONDS, 13 * IN_MILLISECONDS); - Whirlwind2Timer = urand(5 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); - MortalStrikeTimer = urand(5 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); - ResetTimer = 5 * IN_MILLISECONDS; - } - - uint32 CleaveTimer; - uint32 FrighteningShoutTimer; - uint32 Whirlwind1Timer; - uint32 Whirlwind2Timer; - uint32 MortalStrikeTimer; - uint32 ResetTimer; + boss_galvangarAI(Creature* creature) : ScriptedAI(creature) { } void Reset() override { - Initialize(); + events.Reset(); } void EnterCombat(Unit* /*who*/) override { - Talk(YELL_AGGRO); - } - - void JustRespawned() override - { - Reset(); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_CLEAVE, urand(1 * IN_MILLISECONDS, 9 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_FRIGHTENING_SHOUT, urand(2 * IN_MILLISECONDS, 19 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_WHIRLWIND1, urand(1 * IN_MILLISECONDS, 13 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_WHIRLWIND2, urand(5 * IN_MILLISECONDS, 20 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(5 * IN_MILLISECONDS, 20 * IN_MILLISECONDS)); } void DoAction(int32 actionId) override { if (actionId == ACTION_BUFF_YELL) - Talk(YELL_BUFF); + Talk(SAY_BUFF); } - void UpdateAI(uint32 diff) override + bool CheckInRoom() { - if (!UpdateVictim()) - return; - - if (CleaveTimer <= diff) + if (me->GetDistance2d(me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY()) > 50) { - DoCastVictim(SPELL_CLEAVE); - CleaveTimer = urand(10 * IN_MILLISECONDS, 16 * IN_MILLISECONDS); - } else CleaveTimer -= diff; + EnterEvadeMode(); + Talk(SAY_EVADE); + return false; + } - if (FrighteningShoutTimer <= diff) - { - DoCastVictim(SPELL_FRIGHTENING_SHOUT); - FrighteningShoutTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); - } else FrighteningShoutTimer -= diff; + return true; + } - if (Whirlwind1Timer <= diff) - { - DoCastVictim(SPELL_WHIRLWIND1); - Whirlwind1Timer = urand(6 * IN_MILLISECONDS, 10 * IN_MILLISECONDS); - } else Whirlwind1Timer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || !CheckInRoom()) + return; - if (Whirlwind2Timer <= diff) - { - DoCastVictim(SPELL_WHIRLWIND2); - Whirlwind2Timer = urand(10 * IN_MILLISECONDS, 25 * IN_MILLISECONDS); - } else Whirlwind2Timer -= diff; + events.Update(diff); - if (MortalStrikeTimer <= diff) - { - DoCastVictim(SPELL_MORTAL_STRIKE); - MortalStrikeTimer = urand(10 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); - } else MortalStrikeTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - // check if creature is not outside of building - if (ResetTimer <= diff) + while (uint32 eventId = events.ExecuteEvent()) { - if (me->GetDistance2d(me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY()) > 50) + switch (eventId) { - EnterEvadeMode(); - Talk(YELL_EVADE); + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, urand(10 * IN_MILLISECONDS, 16 * IN_MILLISECONDS)); + break; + case EVENT_FRIGHTENING_SHOUT: + DoCastVictim(SPELL_FRIGHTENING_SHOUT); + events.ScheduleEvent(EVENT_FRIGHTENING_SHOUT, urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS)); + break; + case EVENT_WHIRLWIND1: + DoCastVictim(SPELL_WHIRLWIND1); + events.ScheduleEvent(EVENT_WHIRLWIND1, urand(6 * IN_MILLISECONDS, 10 * IN_MILLISECONDS)); + break; + case EVENT_WHIRLWIND2: + DoCastVictim(SPELL_WHIRLWIND2); + events.ScheduleEvent(EVENT_WHIRLWIND2, urand(10 * IN_MILLISECONDS, 25 * IN_MILLISECONDS)); + break; + case EVENT_MORTAL_STRIKE: + DoCastVictim(SPELL_MORTAL_STRIKE); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(10 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); + break; + default: + break; } - ResetTimer = 5 * IN_MILLISECONDS; - } else ResetTimer -= diff; + } DoMeleeAttackIfReady(); } + + private: + EventMap events; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_jandice_barov.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_jandice_barov.cpp index b218e3f2978..c5f7a5da6d7 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/boss_jandice_barov.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_jandice_barov.cpp @@ -21,7 +21,7 @@ enum Spells { SPELL_CURSE_OF_BLOOD = 24673, - SPELL_ILLUSION = 17773, + SPELL_ILLUSION = 17773 }; enum Events @@ -113,7 +113,7 @@ public: CreatureAI* GetAI(Creature* creature) const override { - return new boss_jandicebarovAI(creature); + return GetInstanceAI<boss_jandicebarovAI>(creature); } }; diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp index 897799a708c..348e2212870 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp @@ -81,7 +81,7 @@ public: events.ScheduleEvent(EVENT_ICE_ARMOR, 180000); break; case EVENT_FROSTBOLT: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f, true)) DoCast(target, SPELL_FROSTBOLT); events.ScheduleEvent(EVENT_FROSTBOLT, 8000); break; diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_vectus.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_vectus.cpp index 792649f2998..dcb212eead8 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/boss_vectus.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_vectus.cpp @@ -106,7 +106,6 @@ public: EventMap events; }; - CreatureAI* GetAI(Creature* creature) const override { return new boss_vectusAI(creature); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp index 0a666488e84..fa87247f19f 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp @@ -87,14 +87,14 @@ public: { Initialize(); - instance->SetData(DATA_EPOCH_EVENT, NOT_STARTED); + instance->SetBossState(DATA_EPOCH, NOT_STARTED); } void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - instance->SetData(DATA_EPOCH_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_EPOCH, IN_PROGRESS); } void UpdateAI(uint32 diff) override @@ -136,7 +136,7 @@ public: { Talk(SAY_DEATH); - instance->SetData(DATA_EPOCH_EVENT, DONE); + instance->SetBossState(DATA_EPOCH, DONE); } void KilledUnit(Unit* victim) override diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp index f3333c0b0b6..d693ec38e44 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp @@ -22,7 +22,9 @@ enum Spells { SPELL_CORRUPTING_BLIGHT = 60588, - SPELL_VOID_STRIKE = 60590 + SPELL_VOID_STRIKE = 60590, + SPELL_CORRUPTION_OF_TIME_CHANNEL = 60422, + SPELL_CORRUPTION_OF_TIME_TARGET = 60451 }; enum Yells @@ -32,52 +34,78 @@ enum Yells SAY_FAIL = 2 }; -class boss_infinite_corruptor : public CreatureScript +enum Events { -public: - boss_infinite_corruptor() : CreatureScript("boss_infinite_corruptor") { } + EVENT_CORRUPTING_BLIGHT = 1, + EVENT_VOID_STRIKE +}; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_infinite_corruptorAI>(creature); - } +class boss_infinite_corruptor : public CreatureScript +{ + public: + boss_infinite_corruptor() : CreatureScript("boss_infinite_corruptor") { } - struct boss_infinite_corruptorAI : public ScriptedAI - { - boss_infinite_corruptorAI(Creature* creature) : ScriptedAI(creature) + struct boss_infinite_corruptorAI : public BossAI { - instance = creature->GetInstanceScript(); - } + boss_infinite_corruptorAI(Creature* creature) : BossAI(creature, DATA_INFINITE) { } - InstanceScript* instance; + void Reset() override + { + _Reset(); - void Reset() override - { - instance->SetData(DATA_INFINITE_EVENT, NOT_STARTED); - } + if (Creature* guardian = me->FindNearestCreature(NPC_GUARDIAN_OF_TIME, 100.0f)) + { + DoCast((Unit*)NULL, SPELL_CORRUPTION_OF_TIME_CHANNEL, false); + guardian->CastSpell(guardian, SPELL_CORRUPTION_OF_TIME_TARGET, false); + } + } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - instance->SetData(DATA_INFINITE_EVENT, IN_PROGRESS); - } + void EnterCombat(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _EnterCombat(); + events.ScheduleEvent(EVENT_CORRUPTING_BLIGHT, 7000); + events.ScheduleEvent(EVENT_VOID_STRIKE, 5000); + } - void UpdateAI(uint32 /*diff*/) override - { - //Return since we have no target - if (!UpdateVictim()) - return; + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + _JustDied(); - DoMeleeAttackIfReady(); - } + if (Creature* guardian = me->FindNearestCreature(NPC_GUARDIAN_OF_TIME, 100.0f)) + { + guardian->RemoveAurasDueToSpell(SPELL_CORRUPTION_OF_TIME_TARGET); + guardian->DespawnOrUnsummon(5000); + } - void JustDied(Unit* /*killer*/) override + if (Creature* rift = me->FindNearestCreature(NPC_TIME_RIFT, 100.0f)) + rift->DespawnOrUnsummon(); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_CORRUPTING_BLIGHT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true)) + DoCast(target, SPELL_CORRUPTING_BLIGHT); + events.ScheduleEvent(EVENT_CORRUPTING_BLIGHT, 17000); + break; + case EVENT_VOID_STRIKE: + DoCastVictim(SPELL_VOID_STRIKE); + events.ScheduleEvent(EVENT_VOID_STRIKE, 5000); + break; + default: + break; + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override { - Talk(SAY_DEATH); - instance->SetData(DATA_INFINITE_EVENT, DONE); + return GetInstanceAI<boss_infinite_corruptorAI>(creature); } - }; - }; void AddSC_boss_infinite_corruptor() diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp index d9356b724c0..3d1e9363cd8 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp @@ -26,15 +26,13 @@ Script Data End */ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "culling_of_stratholme.h" +#include "Player.h" enum Spells { SPELL_CARRION_SWARM = 52720, //A cresting wave of chaotic magic splashes over enemies in front of the caster, dealing 3230 to 3570 Shadow damage and 380 to 420 Shadow damage every 3 sec. for 15 sec. - H_SPELL_CARRION_SWARM = 58852, SPELL_MIND_BLAST = 52722, //Inflicts 4163 to 4837 Shadow damage to an enemy. - H_SPELL_MIND_BLAST = 58850, SPELL_SLEEP = 52721, //Puts an enemy to sleep for up to 10 sec. Any damage caused will awaken the target. - H_SPELL_SLEEP = 58849, SPELL_VAMPIRIC_TOUCH = 52723, //Heals the caster for half the damage dealt by a melee attack. SPELL_MAL_GANIS_KILL_CREDIT = 58124, // Quest credit SPELL_KILL_CREDIT = 58630 // Non-existing spell as encounter credit, created in spell_dbc @@ -75,7 +73,6 @@ public: { Initialize(); instance = creature->GetInstanceScript(); - uiOutroStep = 0; } void Initialize() @@ -108,14 +105,13 @@ public: void Reset() override { Initialize(); - - instance->SetData(DATA_MAL_GANIS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_MAL_GANIS, NOT_STARTED); } void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - instance->SetData(DATA_MAL_GANIS_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_MAL_GANIS, IN_PROGRESS); } void DamageTaken(Unit* done_by, uint32 &damage) override @@ -159,7 +155,7 @@ public: { EnterEvadeMode(); me->DisappearAndDie(); - instance->SetData(DATA_MAL_GANIS_EVENT, FAIL); + instance->SetBossState(DATA_MAL_GANIS, FAIL); } if (uiCarrionSwarmTimer < diff) @@ -197,7 +193,7 @@ public: switch (uiOutroStep) { case 1: - Talk(SAY_ESCAPE_SPEECH_1); + Talk(SAY_OUTRO); me->GetMotionMaster()->MoveTargetedHome(); ++uiOutroStep; uiOutroTimer = 8000; @@ -212,7 +208,7 @@ public: case 3: Talk(SAY_OUTRO); ++uiOutroStep; - uiOutroTimer = 16000; + //uiOutroTimer = 16000; break; case 4: me->HandleEmoteCommand(33); @@ -232,7 +228,7 @@ public: void JustDied(Unit* /*killer*/) override { - instance->SetData(DATA_MAL_GANIS_EVENT, DONE); + instance->SetBossState(DATA_MAL_GANIS, DONE); DoCastAOE(SPELL_MAL_GANIS_KILL_CREDIT); // give achievement credit and LFG rewards to players. criteria use spell 58630 which doesn't exist, but it was created in spell_dbc DoCastAOE(SPELL_KILL_CREDIT); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_meathook.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_meathook.cpp index c961bf9b2c6..143f901ec0b 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_meathook.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_meathook.cpp @@ -15,14 +15,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* Script Data Start -SDName: Boss meathook -SDAuthor: Tartalo -SD%Complete: 100 -SDComment: It may need timer adjustment -SDCategory: -Script Data End */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "culling_of_stratholme.h" @@ -30,9 +22,7 @@ Script Data End */ enum Spells { SPELL_CONSTRICTING_CHAINS = 52696, //Encases the targets in chains, dealing 1800 Physical damage every 1 sec. and stunning the target for 5 sec. - H_SPELL_CONSTRICTING_CHAINS = 58823, SPELL_DISEASE_EXPULSION = 52666, //Meathook belches out a cloud of disease, dealing 1710 to 1890 Nature damage and interrupting the spell casting of nearby enemy targets for 4 sec. - H_SPELL_DISEASE_EXPULSION = 58824, SPELL_FRENZY = 58841 //Increases the caster's Physical damage by 10% for 30 sec. }; @@ -44,96 +34,72 @@ enum Yells SAY_DEATH = 3 }; -class boss_meathook : public CreatureScript +enum Events { -public: - boss_meathook() : CreatureScript("boss_meathook") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_meathookAI>(creature); - } - - struct boss_meathookAI : public ScriptedAI - { - boss_meathookAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - Talk(SAY_SPAWN); - } - - void Initialize() - { - uiChainTimer = urand(12000, 17000); //seen on video 13, 17, 15, 12, 16 - uiDiseaseTimer = urand(2000, 4000); //approx 3s - uiFrenzyTimer = urand(21000, 26000); //made it up - } - - uint32 uiChainTimer; - uint32 uiDiseaseTimer; - uint32 uiFrenzyTimer; - - InstanceScript* instance; - - void Reset() override - { - Initialize(); - - instance->SetData(DATA_MEATHOOK_EVENT, NOT_STARTED); - } - - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); + EVENT_CHAIN = 1, + EVENT_DISEASE, + EVENT_FRENZY +}; - instance->SetData(DATA_MEATHOOK_EVENT, IN_PROGRESS); - } +class boss_meathook : public CreatureScript +{ + public: + boss_meathook() : CreatureScript("boss_meathook") { } - void UpdateAI(uint32 diff) override + struct boss_meathookAI : public BossAI { - //Return since we have no target - if (!UpdateVictim()) - return; - - if (uiDiseaseTimer <= diff) + boss_meathookAI(Creature* creature) : BossAI(creature, DATA_MEATHOOK) { - DoCastAOE(SPELL_DISEASE_EXPULSION); - uiDiseaseTimer = urand(1500, 4000); - } else uiDiseaseTimer -= diff; + Talk(SAY_SPAWN); + } - if (uiFrenzyTimer <= diff) + void EnterCombat(Unit* /*who*/) override { - DoCast(me, SPELL_FRENZY); - uiFrenzyTimer = urand(21000, 26000); - } else uiFrenzyTimer -= diff; - - if (uiChainTimer <= diff) + Talk(SAY_AGGRO); + _EnterCombat(); + events.ScheduleEvent(EVENT_CHAIN, urand(12000, 17000)); + events.ScheduleEvent(EVENT_DISEASE, urand(2000, 4000)); + events.ScheduleEvent(EVENT_FRENZY, urand(21000, 26000)); + } + + void ExecuteEvent(uint32 eventId) override { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_CONSTRICTING_CHAINS); //anyone but the tank - uiChainTimer = urand(2000, 4000); - } else uiChainTimer -= diff; - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); + switch (eventId) + { + case EVENT_CHAIN: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_CONSTRICTING_CHAINS); + events.ScheduleEvent(EVENT_CHAIN, urand(2000, 4000)); + case EVENT_DISEASE: + DoCastAOE(SPELL_DISEASE_EXPULSION); + events.ScheduleEvent(EVENT_DISEASE, urand(1500, 4000)); + break; + case EVENT_FRENZY: + DoCast(me, SPELL_FRENZY); + events.ScheduleEvent(EVENT_FRENZY, urand(21000, 26000)); + break; + default: + break; + } + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + _JustDied(); + } - instance->SetData(DATA_MEATHOOK_EVENT, DONE); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + }; - void KilledUnit(Unit* victim) override + CreatureAI* GetAI(Creature* creature) const override { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - - Talk(SAY_SLAY); + return GetInstanceAI<boss_meathookAI>(creature); } - }; - }; void AddSC_boss_meathook() diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp index 1c35a38a5e3..6925badf272 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp @@ -89,14 +89,14 @@ public: { Initialize(); - instance->SetData(DATA_SALRAMM_EVENT, NOT_STARTED); + instance->SetBossState(DATA_SALRAMM, NOT_STARTED); } void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - instance->SetData(DATA_SALRAMM_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_SALRAMM, IN_PROGRESS); } void UpdateAI(uint32 diff) override @@ -145,7 +145,7 @@ public: { Talk(SAY_DEATH); - instance->SetData(DATA_SALRAMM_EVENT, DONE); + instance->SetBossState(DATA_SALRAMM, DONE); } void KilledUnit(Unit* victim) override diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index b8a9b295161..3c80be2734a 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -103,7 +103,7 @@ enum Says //Drakonian SAY_PHASE302 = 0, - SAY_PHASE305 = 1, + SAY_PHASE305 = 1 }; enum NPCs @@ -111,7 +111,6 @@ enum NPCs NPC_INFINITE_ADVERSARY = 27742, NPC_INFINITE_HUNTER = 27743, NPC_INFINITE_AGENT = 27744, - NPC_TIME_RIFT = 28409, NPC_ZOMBIE = 27737, NPC_GHOUL = 28249, NPC_NECROMANCER = 28200, @@ -128,7 +127,7 @@ enum NPCs NPC_CITY_MAN = 28167, NPC_CITY_MAN2 = 28169, NPC_CITY_MAN3 = 31126, - NPC_CITY_MAN4 = 31127, + NPC_CITY_MAN4 = 31127 }; enum Spells @@ -138,7 +137,7 @@ enum Spells SPELL_EXORCISM_N = 52445, SPELL_EXORCISM_H = 58822, SPELL_HOLY_LIGHT = 52444, - SPELL_ARCANE_DISRUPTION = 49590, + SPELL_ARCANE_DISRUPTION = 49590 }; enum GossipMenuArthas @@ -392,6 +391,7 @@ public: uint32 gossipStep; uint32 bossEvent; uint32 wave; + uint32 WavesCounter; uint64 utherGUID; uint64 jainaGUID; @@ -411,17 +411,19 @@ public: { Initialize(); - instance->SetData(DATA_ARTHAS_EVENT, NOT_STARTED); - switch (instance->GetData(DATA_ARTHAS_EVENT)) - { - case NOT_STARTED: - bStepping = true; - step = 0; - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - bossEvent = DATA_MEATHOOK_EVENT; - gossipStep = 0; - break; - } + instance->SetBossState(DATA_ARTHAS, NOT_STARTED); + + bStepping = true; + step = 0; + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + bossEvent = DATA_MEATHOOK; + gossipStep = 0; + } + + void AttackStart(Unit* who) + { + if (who && !who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC)) + npc_escortAI::AttackStart(who); } void EnterCombat(Unit* /*who*/) override @@ -431,7 +433,7 @@ public: void JustDied(Unit* /*killer*/) override { - instance->SetData(DATA_ARTHAS_EVENT, FAIL); + instance->SetBossState(DATA_ARTHAS, FAIL); } void SpawnTimeRift(uint32 timeRiftID, uint64* guidVector) @@ -488,16 +490,15 @@ public: case 11: case 22: case 23: - case 26: case 55: case 56: SetHoldState(true); bStepping = true; break; case 7: - if (Unit* cityman0 = me->SummonCreature(NPC_CITY_MAN, 2091.977f, 1275.021f, 140.757f, 0.558f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000)) + if (Unit* cityman0 = me->FindNearestCreature(NPC_CITY_MAN, 160.0f)) citymenGUID[0] = cityman0->GetGUID(); - if (Unit* cityman1 = me->SummonCreature(NPC_CITY_MAN2, 2093.514f, 1275.842f, 140.408f, 3.801f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000)) + if (Unit* cityman1 = me->FindNearestCreature(NPC_CITY_MAN2, 160.0f)) citymenGUID[1] = cityman1->GetGUID(); break; case 8: @@ -534,10 +535,12 @@ public: case 21: Talk(SAY_PHASE301); break; - case 25: + case 26: SetRun(false); SpawnTimeRift(0, &infiniteDraconianGUID[0]); Talk(SAY_PHASE307); + SetHoldState(true); + bStepping = true; break; case 29: SetRun(false); @@ -565,8 +568,8 @@ public: Talk(SAY_PHASE403); break; case 36: - if (GameObject* pGate = instance->instance->GetGameObject(instance->GetData64(DATA_SHKAF_GATE))) - pGate->SetGoState(GO_STATE_ACTIVE); + if (GameObject* gate = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_SHKAF_GATE))) + gate->SetGoState(GO_STATE_ACTIVE); break; case 45: SetRun(true); @@ -575,18 +578,18 @@ public: me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); SetHoldState(true); break; - case 47: + case 48: SetRun(false); Talk(SAY_PHASE405); break; - case 48: + case 49: SetRun(true); Talk(SAY_PHASE406); break; - case 53: + case 50: Talk(SAY_PHASE407); break; - case 54: + case 51: gossipStep = 5; me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); SetHoldState(true); @@ -598,8 +601,6 @@ public: { npc_escortAI::UpdateAI(diff); - DoMeleeAttackIfReady(); - if (bStepping) { if (phaseTimer <= diff) @@ -765,6 +766,9 @@ public: stalkerGUID = pStalker->GetGUID(); me->SetTarget(stalkerGUID); } + + instance->DoUpdateWorldState(WORLDSTATE_WAVE_COUNT, 0); + JumpToNextStep(1000); break; case 25: @@ -892,14 +896,15 @@ public: } Talk(SAY_PHASE209); - bossEvent = DATA_MEATHOOK_EVENT; - instance->SetData(DATA_ARTHAS_EVENT, IN_PROGRESS); + bossEvent = DATA_MEATHOOK; + instance->SetBossState(DATA_ARTHAS, IN_PROGRESS); me->SetReactState(REACT_DEFENSIVE); SetDespawnAtFar(false); JumpToNextStep(5000); break; - case 41: //Summon wave group + // Summon wave groups - start the Infinite Corruptor timer + case 41: case 43: case 45: case 47: @@ -907,10 +912,15 @@ public: case 53: case 55: case 57: - if (instance->GetData(bossEvent) != DONE) + if (!wave && IsHeroic() && instance->GetData(DATA_INFINITE_COUNTER) == NOT_STARTED) + instance->SetData(DATA_INFINITE_COUNTER, IN_PROGRESS); + + if (instance->GetBossState(bossEvent) != DONE) { SpawnWaveGroup(wave, waveGUID); wave++; + WavesCounter++; + instance->DoUpdateWorldState(WORLDSTATE_WAVE_COUNT, WavesCounter); } JumpToNextStep(500); break; @@ -922,7 +932,7 @@ public: case 54: case 56: case 58: - if (instance->GetData(bossEvent) != DONE) + if (instance->GetBossState(bossEvent) != DONE) { uint32 mobCounter = 0; uint32 deadCounter = 0; @@ -946,12 +956,14 @@ public: break; case 49: //Summon Boss case 59: - if (instance->GetData(bossEvent) != DONE) + if (instance->GetBossState(bossEvent) != DONE) { + WavesCounter++; + instance->DoUpdateWorldState(WORLDSTATE_WAVE_COUNT, WavesCounter); uint32 uiBossID = 0; - if (bossEvent == DATA_MEATHOOK_EVENT) + if (bossEvent == DATA_MEATHOOK) uiBossID = NPC_MEATHOOK; - else if (bossEvent == DATA_SALRAMM_EVENT) + else if (bossEvent == DATA_SALRAMM) uiBossID = NPC_SALRAMM; if (Unit* pBoss = me->SummonCreature(uiBossID, 2232.19f, 1331.933f, 126.662f, 3.15f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900000)) @@ -965,19 +977,19 @@ public: break; case 50: //Wait Boss death case 60: - if (instance->GetData(bossEvent) == DONE) + if (instance->GetBossState(bossEvent) == DONE) { JumpToNextStep(1000); - if (bossEvent == DATA_MEATHOOK_EVENT) - bossEvent = DATA_SALRAMM_EVENT; - else if (bossEvent == DATA_SALRAMM_EVENT) + if (bossEvent == DATA_MEATHOOK) + bossEvent = DATA_SALRAMM; + else if (bossEvent == DATA_SALRAMM) { SetHoldState(false); bStepping = false; - bossEvent = DATA_EPOCH_EVENT; + bossEvent = DATA_EPOCH; } } - else if (instance->GetData(bossEvent) == FAIL) + else if (instance->GetBossState(bossEvent) == FAIL) npc_escortAI::EnterEvadeMode(); else phaseTimer = 10000; @@ -1074,9 +1086,12 @@ public: phaseTimer = 1000; else { - if (step == 72) Talk(SAY_PHASE308); - if (step == 74) Talk(SAY_PHASE308); - if (step == 76) Talk(SAY_PHASE310); + if (step == 72) + Talk(SAY_PHASE308); + if (step == 74) + Talk(SAY_PHASE308); + if (step == 76) + Talk(SAY_PHASE310); SetHoldState(false); bStepping = false; SetRun(true); @@ -1097,7 +1112,7 @@ public: JumpToNextStep(1000); break; case 80: - if (instance->GetData(DATA_EPOCH_EVENT) != DONE) + if (instance->GetBossState(DATA_EPOCH) != DONE) { SpawnTimeRift(17, &epochGUID); if (Creature* epoch = ObjectAccessor::GetCreature(*me, epochGUID)) @@ -1107,12 +1122,12 @@ public: JumpToNextStep(18000); break; case 81: - if (instance->GetData(DATA_EPOCH_EVENT) != DONE) + if (instance->GetBossState(DATA_EPOCH) != DONE) Talk(SAY_PHASE315); JumpToNextStep(6000); break; case 82: - if (instance->GetData(DATA_EPOCH_EVENT) != DONE) + if (instance->GetBossState(DATA_EPOCH) != DONE) { if (Creature* epoch = ObjectAccessor::GetCreature(*me, epochGUID)) { @@ -1126,15 +1141,15 @@ public: JumpToNextStep(1000); break; case 83: - if (instance->GetData(DATA_EPOCH_EVENT) == DONE) + if (instance->GetBossState(DATA_EPOCH) == DONE) { gossipStep = 3; me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); bStepping = false; - bossEvent = DATA_MAL_GANIS_EVENT; + bossEvent = DATA_MAL_GANIS; JumpToNextStep(15000); } - else if (instance->GetData(DATA_EPOCH_EVENT) == FAIL) + else if (instance->GetBossState(DATA_EPOCH) == FAIL) npc_escortAI::EnterEvadeMode(); else phaseTimer = 10000; @@ -1153,8 +1168,8 @@ public: malganisGUID = malganis->GetGUID(); malganis->SetReactState(REACT_PASSIVE); } - if (GameObject* pGate = instance->instance->GetGameObject(instance->GetData64(DATA_MAL_GANIS_GATE_1))) - pGate->SetGoState(GO_STATE_ACTIVE); + if (GameObject* gate = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_MAL_GANIS_GATE_1))) + gate->SetGoState(GO_STATE_ACTIVE); SetHoldState(false); bStepping = false; JumpToNextStep(0); @@ -1174,12 +1189,12 @@ public: JumpToNextStep(1000); break; case 88: - if (instance->GetData(DATA_MAL_GANIS_EVENT) == DONE) + if (instance->GetBossState(DATA_MAL_GANIS) == DONE) { SetHoldState(false); JumpToNextStep(1000); } - else if (instance->GetData(DATA_MAL_GANIS_EVENT) == FAIL) + else if (instance->GetBossState(DATA_MAL_GANIS) == FAIL) npc_escortAI::EnterEvadeMode(); else phaseTimer = 10000; @@ -1192,7 +1207,7 @@ public: JumpToNextStep(7000); break; case 90: - instance->SetData(DATA_ARTHAS_EVENT, DONE); //Rewards: Achiev & Chest ;D + instance->SetBossState(DATA_ARTHAS, DONE); //Rewards: Achiev & Chest ;D me->SetTarget(instance->GetData64(DATA_MAL_GANIS_GATE_2)); //Look behind Talk(SAY_PHASE504); bStepping = false; @@ -1214,6 +1229,8 @@ public: if (HealthBelowPct(40)) DoCast(me, SPELL_HOLY_LIGHT); + + DoMeleeAttackIfReady(); } }; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h index e7d1033e55f..192654c87f1 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h @@ -19,19 +19,10 @@ #define DEF_CULLING_OF_STRATHOLME_H #define DataHeader "CS" +#define CoSScriptName "instance_culling_of_stratholme" +uint32 const EncounterCount = 5; -enum Data -{ - DATA_MEATHOOK_EVENT, - DATA_SALRAMM_EVENT, - DATA_EPOCH_EVENT, - DATA_MAL_GANIS_EVENT, - DATA_INFINITE_EVENT, - DATA_ARTHAS_EVENT, - DATA_CRATE_COUNT, -}; - -enum Data64 +enum DataTypes { DATA_ARTHAS, DATA_MEATHOOK, @@ -39,28 +30,35 @@ enum Data64 DATA_EPOCH, DATA_MAL_GANIS, DATA_INFINITE, + DATA_CRATE_COUNT, DATA_SHKAF_GATE, DATA_MAL_GANIS_GATE_1, DATA_MAL_GANIS_GATE_2, DATA_EXIT_GATE, - DATA_MAL_GANIS_CHEST + DATA_MAL_GANIS_CHEST, + DATA_INFINITE_COUNTER }; -enum Creatures +enum CreatureIds { - NPC_MEATHOOK = 26529, - NPC_SALRAMM = 26530, - NPC_EPOCH = 26532, - NPC_MAL_GANIS = 26533, - NPC_INFINITE = 32273, - NPC_ARTHAS = 26499, - NPC_JAINA = 26497, - NPC_UTHER = 26528, - NPC_CHROMIE_2 = 27915, - NPC_GENERIC_BUNNY = 28960, + NPC_MEATHOOK = 26529, + NPC_SALRAMM = 26530, + NPC_EPOCH = 26532, + NPC_MAL_GANIS = 26533, + NPC_INFINITE = 32273, + NPC_ARTHAS = 26499, + NPC_JAINA = 26497, + NPC_UTHER = 26528, + NPC_CHROMIE = 26527, + NPC_CHROMIE_2 = 27915, + NPC_CHROMIE_3 = 30997, + NPC_GENERIC_BUNNY = 28960, + + NPC_TIME_RIFT = 28409, + NPC_GUARDIAN_OF_TIME = 32281 }; -enum GameObjects +enum GameObjectIds { GO_SHKAF_GATE = 188686, GO_MALGANIS_GATE_1 = 187711, @@ -69,7 +67,7 @@ enum GameObjects GO_MALGANIS_CHEST_N = 190663, GO_MALGANIS_CHEST_H = 193597, GO_SUSPICIOUS_CRATE = 190094, - GO_PLAGUED_CRATE = 190095, + GO_PLAGUED_CRATE = 190095 }; enum WorldStatesCoT @@ -78,12 +76,28 @@ enum WorldStatesCoT WORLDSTATE_CRATES_REVEALED = 3480, WORLDSTATE_WAVE_COUNT = 3504, WORLDSTATE_TIME_GUARDIAN = 3931, - WORLDSTATE_TIME_GUARDIAN_SHOW = 3932, + WORLDSTATE_TIME_GUARDIAN_SHOW = 3932 }; enum CrateSpells { - SPELL_CRATES_CREDIT = 58109, + SPELL_CRATES_CREDIT = 58109 +}; + +enum Texts +{ + SAY_CRATES_COMPLETED = 0, + // Chromie + SAY_INFINITE_START = 0, // On Infinite Corruptor event start + SAY_INFINITE = 1, // On Infinite Corruptor event at 5 minutes + SAY_INFINITE_FAIL = 2, // On Infinite Corruptor event fail + // Infinite Corruptor + SAY_FAIL_EVENT = 2 // On Infinite Corruptor event fail +}; + +enum InstanceEvents +{ + EVENT_INFINITE_TIMER = 1 }; #endif diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp index 7af2b3f63aa..b3e8d88bc2a 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp @@ -23,8 +23,6 @@ #include "TemporarySummon.h" #include "SpellInfo.h" -#define MAX_ENCOUNTER 5 - /* Culling of Stratholme encounters: 0 - Meathook 1 - Salramm the Fleshcrafter @@ -33,51 +31,51 @@ 4 - Infinite Corruptor (Heroic only) */ -enum Texts +Position const ChromieSummonPos[] = { - SAY_CRATES_COMPLETED = 0, + { 1813.298f, 1283.578f, 142.3258f, 3.878161f }, + { 2273.725f, 1483.684f, 128.7205f, 6.057528f } }; -Position const ChromieSummonPos = {1813.298f, 1283.578f, 142.3258f, 3.878161f}; +Position const InfiniteCorruptorPos = { 2335.47f, 1262.04f, 132.921f, 1.42079f }; +Position const TimeRiftPos = { 2334.626f, 1280.45f, 133.0066f, 1.727876f }; +Position const GuardianOfTimePos = { 2321.489f, 1268.383f, 132.8507f, 0.418879f }; + +DoorData const doorData[] = +{ + { GO_MALGANIS_GATE_2, DATA_MAL_GANIS, DOOR_TYPE_ROOM, BOUNDARY_NONE }, + { GO_EXIT_GATE, DATA_MAL_GANIS, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { 0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE } // END +}; class instance_culling_of_stratholme : public InstanceMapScript { public: - instance_culling_of_stratholme() : InstanceMapScript("instance_culling_of_stratholme", 595) { } - - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_culling_of_stratholme_InstanceMapScript(map); - } + instance_culling_of_stratholme() : InstanceMapScript(CoSScriptName, 595) { } struct instance_culling_of_stratholme_InstanceMapScript : public InstanceScript { instance_culling_of_stratholme_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); - _arthasGUID = 0; - _meathookGUID = 0; - _salrammGUID = 0; - _epochGUID = 0; - _malGanisGUID = 0; - _infiniteGUID = 0; - _shkafGateGUID = 0; + SetBossNumber(EncounterCount); + LoadDoorData(doorData); + + _chromieGUID = 0; + _arthasGUID = 0; + _meathookGUID = 0; + _salrammGUID = 0; + _epochGUID = 0; + _malGanisGUID = 0; + _infiniteGUID = 0; + _shkafGateGUID = 0; _malGanisGate1GUID = 0; _malGanisGate2GUID = 0; - _exitGateGUID = 0; + _exitGateGUID = 0; _malGanisChestGUID = 0; - _genericBunnyGUID = 0; - memset(&_encounterState[0], 0, sizeof(uint32) * MAX_ENCOUNTER); - _crateCount = 0; - } - - bool IsEncounterInProgress() const override - { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (_encounterState[i] == IN_PROGRESS) - return true; - - return false; + _genericBunnyGUID = 0; + _crateCount = 0; + _eventTimer = 0; } void FillInitialWorldStates(WorldPacket& data) override @@ -93,6 +91,9 @@ class instance_culling_of_stratholme : public InstanceMapScript { switch (creature->GetEntry()) { + case NPC_CHROMIE: + _chromieGUID = creature->GetGUID(); + break; case NPC_ARTHAS: _arthasGUID = creature->GetGUID(); break; @@ -110,10 +111,13 @@ class instance_culling_of_stratholme : public InstanceMapScript break; case NPC_INFINITE: _infiniteGUID = creature->GetGUID(); + DoUpdateWorldState(WORLDSTATE_TIME_GUARDIAN_SHOW, 1); break; case NPC_GENERIC_BUNNY: _genericBunnyGUID = creature->GetGUID(); break; + default: + break; } } @@ -129,55 +133,38 @@ class instance_culling_of_stratholme : public InstanceMapScript break; case GO_MALGANIS_GATE_2: _malGanisGate2GUID = go->GetGUID(); + AddDoor(go, true); break; case GO_EXIT_GATE: _exitGateGUID = go->GetGUID(); - if (_encounterState[3] == DONE) - HandleGameObject(_exitGateGUID, true); + AddDoor(go, true); break; case GO_MALGANIS_CHEST_N: case GO_MALGANIS_CHEST_H: _malGanisChestGUID = go->GetGUID(); - if (_encounterState[3] == DONE) - go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); + break; + default: break; } } - void SetData(uint32 type, uint32 data) override + void OnGameObjectRemove(GameObject* go) override { - switch (type) + switch (go->GetEntry()) { - case DATA_MEATHOOK_EVENT: - _encounterState[0] = data; - break; - case DATA_SALRAMM_EVENT: - _encounterState[1] = data; + case GO_MALGANIS_GATE_2: + case GO_EXIT_GATE: + AddDoor(go, false); break; - case DATA_EPOCH_EVENT: - _encounterState[2] = data; + default: break; - case DATA_MAL_GANIS_EVENT: - _encounterState[3] = data; + } + } - switch (_encounterState[3]) - { - case NOT_STARTED: - HandleGameObject(_malGanisGate2GUID, true); - break; - case IN_PROGRESS: - HandleGameObject(_malGanisGate2GUID, false); - break; - case DONE: - HandleGameObject(_exitGateGUID, true); - if (GameObject* go = instance->GetGameObject(_malGanisChestGUID)) - go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); - break; - } - break; - case DATA_INFINITE_EVENT: - _encounterState[4] = data; - break; + void SetData(uint32 type, uint32 data) override + { + switch (type) + { case DATA_CRATE_COUNT: _crateCount = data; if (_crateCount == 5) @@ -186,34 +173,70 @@ class instance_culling_of_stratholme : public InstanceMapScript bunny->CastSpell(bunny, SPELL_CRATES_CREDIT, true); // Summon Chromie and global whisper - if (Creature* chromie = instance->SummonCreature(NPC_CHROMIE_2, ChromieSummonPos)) + if (Creature* chromie = instance->SummonCreature(NPC_CHROMIE_2, ChromieSummonPos[0])) if (!instance->GetPlayers().isEmpty()) chromie->AI()->TalkToMap(SAY_CRATES_COMPLETED); } DoUpdateWorldState(WORLDSTATE_CRATES_REVEALED, _crateCount); break; + case DATA_INFINITE_COUNTER: + _infiniteCouterState = data; + if (data == IN_PROGRESS) + { + if (!_infiniteGUID) + { + _eventTimer = 25; + instance->SummonCreature(NPC_INFINITE, InfiniteCorruptorPos); + instance->SummonCreature(NPC_TIME_RIFT, TimeRiftPos); + instance->SummonCreature(NPC_GUARDIAN_OF_TIME, GuardianOfTimePos); + events.ScheduleEvent(EVENT_INFINITE_TIMER, 1); + } + } + break; + default: + break; + } + } + + bool SetBossState(uint32 type, EncounterState state) override + { + if (!InstanceScript::SetBossState(type, state)) + return false; + + switch (type) + { + case DATA_INFINITE: + if (state == DONE) + { + DoUpdateWorldState(WORLDSTATE_TIME_GUARDIAN_SHOW, 0); + DoUpdateWorldState(WORLDSTATE_TIME_GUARDIAN, 0); + } + break; + case DATA_MAL_GANIS: + if (state == DONE) + { + if (GameObject* go = instance->GetGameObject(_malGanisChestGUID)) + go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); + instance->SummonCreature(NPC_CHROMIE_3, ChromieSummonPos[1]); + } + break; + default: + break; } - if (data == DONE) - SaveToDB(); + return true; } uint32 GetData(uint32 type) const override { switch (type) { - case DATA_MEATHOOK_EVENT: - return _encounterState[0]; - case DATA_SALRAMM_EVENT: - return _encounterState[1]; - case DATA_EPOCH_EVENT: - return _encounterState[2]; - case DATA_MAL_GANIS_EVENT: - return _encounterState[3]; - case DATA_INFINITE_EVENT: - return _encounterState[4]; case DATA_CRATE_COUNT: return _crateCount; + case DATA_INFINITE_COUNTER: + return _infiniteCouterState; + default: + break; } return 0; } @@ -244,58 +267,70 @@ class instance_culling_of_stratholme : public InstanceMapScript return _exitGateGUID; case DATA_MAL_GANIS_CHEST: return _malGanisChestGUID; + default: + break; } return 0; } - std::string GetSaveData() override + void Update(uint32 diff) override { - OUT_SAVE_INST_DATA; + events.Update(diff); - std::ostringstream saveStream; - saveStream << "C S " << _encounterState[0] << ' ' << _encounterState[1] << ' ' - << _encounterState[2] << ' ' << _encounterState[3] << ' ' << _encounterState[4]; - - OUT_SAVE_INST_DATA_COMPLETE; - return saveStream.str(); - } - - void Load(const char* in) override - { - if (!in) + while (uint32 eventId = events.ExecuteEvent()) { - OUT_LOAD_INST_DATA_FAIL; - return; - } - - OUT_LOAD_INST_DATA(in); - - char dataHead1, dataHead2; - uint16 data0, data1, data2, data3, data4; - - std::istringstream loadStream(in); - loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4; - - if (dataHead1 == 'C' && dataHead2 == 'S') - { - _encounterState[0] = data0; - _encounterState[1] = data1; - _encounterState[2] = data2; - _encounterState[3] = data3; - _encounterState[4] = data4; - - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (_encounterState[i] == IN_PROGRESS) - _encounterState[i] = NOT_STARTED; - + switch (eventId) + { + case EVENT_INFINITE_TIMER: + DoUpdateWorldState(WORLDSTATE_TIME_GUARDIAN, _eventTimer); + + switch (_eventTimer) + { + case 25: + if (instance->HavePlayers()) + if (Creature* chromie = instance->GetCreature(_chromieGUID)) + chromie->AI()->TalkToMap(SAY_INFINITE_START); + break; + case 5: + if (instance->HavePlayers()) + if (Creature* chromie = instance->GetCreature(_chromieGUID)) + chromie->AI()->TalkToMap(SAY_INFINITE); + break; + case 0: + if (instance->HavePlayers()) + if (Creature* chromie = instance->GetCreature(_chromieGUID)) + chromie->AI()->TalkToMap(SAY_INFINITE_FAIL); + + if (Creature* infinite = instance->GetCreature(_infiniteGUID)) + { + if (Creature* guardian = infinite->FindNearestCreature(NPC_GUARDIAN_OF_TIME, 100.0f)) + infinite->Kill(guardian); + + if (Creature* rift = infinite->FindNearestCreature(NPC_TIME_RIFT, 100.0f)) + { + infinite->GetMotionMaster()->MovePoint(0, rift->GetPositionX(), rift->GetPositionY(), rift->GetPositionZ()); + rift->DespawnOrUnsummon(3000); + } + + infinite->DespawnOrUnsummon(3000); + infinite->AI()->Talk(SAY_FAIL_EVENT); + } + DoUpdateWorldState(WORLDSTATE_TIME_GUARDIAN_SHOW, 0); + return; + default: + break; + } + events.ScheduleEvent(EVENT_INFINITE_TIMER, 60000); + --_eventTimer; + break; + default: + break; + } } - else - OUT_LOAD_INST_DATA_FAIL; - - OUT_LOAD_INST_DATA_COMPLETE; } private: + uint64 _chromieGUID; uint64 _arthasGUID; uint64 _meathookGUID; uint64 _salrammGUID; @@ -308,9 +343,18 @@ class instance_culling_of_stratholme : public InstanceMapScript uint64 _exitGateGUID; uint64 _malGanisChestGUID; uint64 _genericBunnyGUID; - uint32 _encounterState[MAX_ENCOUNTER]; + uint32 _crateCount; + uint32 _eventTimer; + uint32 _infiniteCouterState; + + EventMap events; }; + + InstanceScript* GetInstanceScript(InstanceMap* map) const override + { + return new instance_culling_of_stratholme_InstanceMapScript(map); + } }; void AddSC_instance_culling_of_stratholme() diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index ff507f32bae..3670e225fd7 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -487,6 +487,14 @@ public: Talk(SAY_TWIGGY_FLATHEAD_OVER); Reset(); } + else if (creature) // Makes BIG WILL attackable. + { + creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + creature->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + creature->setFaction(14); + creature->AI()->AttackStart(warrior); + } } } else WaveTimer -= diff; } diff --git a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp index 0783a79b381..9f8cc818958 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp @@ -28,146 +28,92 @@ enum Spells SPELL_ECK_SPRING_2 = 55837 //Eck leaps at a distant target. }; -static Position EckSpawnPoint = { 1643.877930f, 936.278015f, 107.204948f, 0.668432f }; +enum Events +{ + EVENT_BITE = 1, + EVENT_SPIT, + EVENT_SPRING, + EVENT_BERSERK +}; class boss_eck : public CreatureScript { public: boss_eck() : CreatureScript("boss_eck") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_eckAI>(creature); - } - - struct boss_eckAI : public ScriptedAI + struct boss_eckAI : public BossAI { - boss_eckAI(Creature* creature) : ScriptedAI(creature) + boss_eckAI(Creature* creature) : BossAI(creature, DATA_ECK_THE_FEROCIOUS_EVENT) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() { - uiBerserkTimer = urand(60 * IN_MILLISECONDS, 90 * IN_MILLISECONDS); //60-90 secs according to wowwiki - uiBiteTimer = 5 * IN_MILLISECONDS; - uiSpitTimer = 10 * IN_MILLISECONDS; - uiSpringTimer = 8 * IN_MILLISECONDS; - - bBerserk = false; + Berserk = false; } - uint32 uiBerserkTimer; - uint32 uiBiteTimer; - uint32 uiSpitTimer; - uint32 uiSpringTimer; - - bool bBerserk; - - InstanceScript* instance; - void Reset() override { + _Reset(); Initialize(); - - instance->SetData(DATA_ECK_THE_FEROCIOUS_EVENT, NOT_STARTED); } void EnterCombat(Unit* /*who*/) override { - instance->SetData(DATA_ECK_THE_FEROCIOUS_EVENT, IN_PROGRESS); + _EnterCombat(); + events.ScheduleEvent(EVENT_BITE, 5 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SPIT, 10 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SPRING, 8 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_BERSERK, urand(60 * IN_MILLISECONDS, 90 * IN_MILLISECONDS)); //60-90 secs according to wowwiki } - void UpdateAI(uint32 diff) override + void DamageTaken(Unit* /*attacker*/, uint32& damage) override { - //Return since we have no target - if (!UpdateVictim()) - return; - - if (uiBiteTimer <= diff) - { - DoCastVictim(SPELL_ECK_BITE); - uiBiteTimer = urand(8*IN_MILLISECONDS, 12*IN_MILLISECONDS); - } else uiBiteTimer -= diff; - - if (uiSpitTimer <= diff) + if (me->HealthBelowPctDamaged(20, damage) && !Berserk) { - DoCastVictim(SPELL_ECK_SPIT); - uiSpitTimer = urand(6*IN_MILLISECONDS, 14*IN_MILLISECONDS); - } else uiSpitTimer -= diff; - - if (uiSpringTimer <= diff) - { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1); - if (target && target->GetTypeId() == TYPEID_PLAYER) - { - DoCast(target, RAND(SPELL_ECK_SPRING_1, SPELL_ECK_SPRING_2)); - uiSpringTimer = urand(5*IN_MILLISECONDS, 10*IN_MILLISECONDS); - } - } else uiSpringTimer -= diff; + events.RescheduleEvent(EVENT_BERSERK, 1000); + Berserk = true; + } + } - //Berserk on timer or 20% of health - if (!bBerserk) + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - if (uiBerserkTimer <= diff) - { + case EVENT_BITE: + DoCastVictim(SPELL_ECK_BITE); + events.ScheduleEvent(EVENT_BITE, urand(8 * IN_MILLISECONDS, 12 * IN_MILLISECONDS)); + break; + case EVENT_SPIT: + DoCastVictim(SPELL_ECK_SPIT); + events.ScheduleEvent(EVENT_SPIT, urand(6 * IN_MILLISECONDS, 14 * IN_MILLISECONDS)); + break; + case EVENT_SPRING: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 35.0f, true)) + DoCast(target, RAND(SPELL_ECK_SPRING_1, SPELL_ECK_SPRING_2)); + events.ScheduleEvent(EVENT_SPRING, urand(5 * IN_MILLISECONDS, 10 * IN_MILLISECONDS)); + break; + case EVENT_BERSERK: DoCast(me, SPELL_ECK_BERSERK); - bBerserk = true; - } - else - { - uiBerserkTimer -= diff; - if (HealthBelowPct(20)) - { - DoCast(me, SPELL_ECK_BERSERK); - bBerserk = true; - } - } + Berserk = true; + break; + default: + break; } - - DoMeleeAttackIfReady(); } - void JustDied(Unit* /*killer*/) override - { - instance->SetData(DATA_ECK_THE_FEROCIOUS_EVENT, DONE); - } + private: + bool Berserk; }; -}; - -class npc_ruins_dweller : public CreatureScript -{ -public: - npc_ruins_dweller() : CreatureScript("npc_ruins_dweller") { } - CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<npc_ruins_dwellerAI>(creature); + return GetInstanceAI<boss_eckAI>(creature); } - - struct npc_ruins_dwellerAI : public ScriptedAI - { - npc_ruins_dwellerAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - - void JustDied(Unit* /*killer*/) override - { - instance->SetData64(DATA_RUIN_DWELLER_DIED, me->GetGUID()); - if (instance->GetData(DATA_ALIVE_RUIN_DWELLERS) == 0) - me->SummonCreature(CREATURE_ECK, EckSpawnPoint, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 300*IN_MILLISECONDS); - } - }; - }; void AddSC_boss_eck() { new boss_eck(); - new npc_ruins_dweller(); } diff --git a/src/server/scripts/Northrend/Gundrak/gundrak.h b/src/server/scripts/Northrend/Gundrak/gundrak.h index 8f269705a45..fffeca82f39 100644 --- a/src/server/scripts/Northrend/Gundrak/gundrak.h +++ b/src/server/scripts/Northrend/Gundrak/gundrak.h @@ -26,8 +26,7 @@ enum Data DATA_MOORABI_EVENT, DATA_DRAKKARI_COLOSSUS_EVENT, DATA_GAL_DARAH_EVENT, - DATA_ECK_THE_FEROCIOUS_EVENT, - DATA_ALIVE_RUIN_DWELLERS + DATA_ECK_THE_FEROCIOUS_EVENT }; enum Data64 @@ -39,8 +38,7 @@ enum Data64 DATA_MOORABI_STATUE, DATA_DRAKKARI_COLOSSUS_STATUE, DATA_DRAKKARI_COLOSSUS, - DATA_RUIN_DWELLER_DIED, - DATA_STATUE_ACTIVATE, + DATA_STATUE_ACTIVATE }; enum mainCreatures diff --git a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp index e5efdad27f3..388d43e8dca 100644 --- a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp +++ b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp @@ -31,6 +31,8 @@ 4 - Eck the Ferocious */ +Position const EckSpawnPoint = { 1643.877930f, 936.278015f, 107.204948f, 0.668432f }; + class instance_gundrak : public InstanceMapScript { public: @@ -134,7 +136,7 @@ public: memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); } - bool IsEncounterInProgress() const override + bool IsEncounterInProgress() const override { for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) if (m_auiEncounter[i] == IN_PROGRESS) @@ -274,6 +276,17 @@ public: } } + void OnUnitDeath(Unit* unit) override + { + if (unit->GetEntry() == CREATURE_RUIN_DWELLER) + { + DwellerGUIDs.erase(unit->GetGUID()); + + if (DwellerGUIDs.empty()) + unit->SummonCreature(CREATURE_ECK, EckSpawnPoint, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 300 * IN_MILLISECONDS); + } + } + void SetData(uint32 type, uint32 data) override { switch (type) @@ -329,9 +342,7 @@ public: void SetData64(uint32 type, uint64 data) override { - if (type == DATA_RUIN_DWELLER_DIED) - DwellerGUIDs.erase(data); - else if (type == DATA_STATUE_ACTIVATE) + if (type == DATA_STATUE_ACTIVATE) { toActivate = data; timer = 3500; @@ -353,8 +364,6 @@ public: return m_auiEncounter[3]; case DATA_ECK_THE_FEROCIOUS_EVENT: return m_auiEncounter[4]; - case DATA_ALIVE_RUIN_DWELLERS: - return DwellerGUIDs.size(); } return 0; diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 7f34e470c7e..a83f4feb70a 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -26,7 +26,6 @@ EndScriptData */ /* ContentData npc_iruk npc_corastrasza -npc_jenny npc_sinkhole_kill_credit npc_khunok_the_behemoth npc_nerubar_victim @@ -407,125 +406,6 @@ public: }; /*###### -## npc_jenny -######*/ - -enum Jenny -{ - QUEST_LOADER_UP = 11881, - - NPC_FEZZIX_GEARTWIST = 25849, - NPC_JENNY = 25969, - - SPELL_GIVE_JENNY_CREDIT = 46358, - SPELL_CRATES_CARRIED = 46340, - SPELL_DROP_CRATE = 46342 -}; - -class npc_jenny : public CreatureScript -{ -public: - npc_jenny() : CreatureScript("npc_jenny") { } - - struct npc_jennyAI : public ScriptedAI - { - npc_jennyAI(Creature* creature) : ScriptedAI(creature) - { - setCrateNumber = false; - } - - bool setCrateNumber; - - void Reset() override - { - if (!setCrateNumber) - setCrateNumber = true; - - me->SetReactState(REACT_PASSIVE); - - if (!me->GetOwner()) - return; - - switch (me->GetOwner()->ToPlayer()->GetTeamId()) - { - case TEAM_ALLIANCE: - me->setFaction(FACTION_ESCORT_A_NEUTRAL_ACTIVE); - break; - default: - case TEAM_HORDE: - me->setFaction(FACTION_ESCORT_H_NEUTRAL_ACTIVE); - break; - } - } - - void DamageTaken(Unit* /*pDone_by*/, uint32& /*uiDamage*/) override - { - DoCast(me, SPELL_DROP_CRATE, true); - } - - void UpdateAI(uint32 /*diff*/) override - { - if (setCrateNumber) - { - me->AddAura(SPELL_CRATES_CARRIED, me); - setCrateNumber = false; - } - - if (!setCrateNumber && !me->HasAura(SPELL_CRATES_CARRIED)) - me->DisappearAndDie(); - - if (!UpdateVictim()) - return; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_jennyAI(creature); - } -}; - -/*###### -## npc_fezzix_geartwist -######*/ - -class npc_fezzix_geartwist : public CreatureScript -{ -public: - npc_fezzix_geartwist() : CreatureScript("npc_fezzix_geartwist") { } - - struct npc_fezzix_geartwistAI : public ScriptedAI - { - npc_fezzix_geartwistAI(Creature* creature) : ScriptedAI(creature) { } - - void MoveInLineOfSight(Unit* who) override - - { - ScriptedAI::MoveInLineOfSight(who); - - if (who->GetEntry() != NPC_JENNY || !who->HasAura(SPELL_CRATES_CARRIED)) - return; - - Unit* owner = who->GetOwner(); - if (!owner || !me->IsWithinDistInMap(who, 10.0f)) - return; - - if (Player* player = owner->ToPlayer()) - { - owner->CastSpell(owner, SPELL_GIVE_JENNY_CREDIT, true); // Maybe is not working. - player->CompleteQuest(QUEST_LOADER_UP); - who->ToCreature()->DisappearAndDie(); - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_fezzix_geartwistAI(creature); - } -}; - -/*###### ## npc_nesingwary_trapper ######*/ @@ -2112,65 +1992,6 @@ public: }; /*###### -## Quest 11608: Bury Those Cockroaches! -######*/ - -enum BuryThoseCockroaches -{ - // Quest - QUEST_BURY_THOSE_COCKROACHES = 11608, - - // Spells - SPELL_SEAFORIUM_DEPTH_CHARGE_EXPLOSION = 45502 - -}; - -class npc_seaforium_depth_charge : public CreatureScript -{ -public: - npc_seaforium_depth_charge() : CreatureScript("npc_seaforium_depth_charge") { } - - struct npc_seaforium_depth_chargeAI : public ScriptedAI - { - npc_seaforium_depth_chargeAI(Creature* creature) : ScriptedAI(creature) { } - - uint32 uiExplosionTimer; - - void Reset() override - { - uiExplosionTimer = urand(5000, 10000); - } - - void UpdateAI(uint32 diff) override - { - if (uiExplosionTimer < diff) - { - DoCast(SPELL_SEAFORIUM_DEPTH_CHARGE_EXPLOSION); - for (uint8 i = 0; i < 4; ++i) - { - if (Creature* cCredit = me->FindNearestCreature(25402 + i, 10.0f))//25402-25405 credit markers - { - if (Unit* uOwner = me->GetOwner()) - { - Player* owner = uOwner->ToPlayer(); - if (owner && owner->GetQuestStatus(QUEST_BURY_THOSE_COCKROACHES) == QUEST_STATUS_INCOMPLETE) - owner->KilledMonsterCredit(cCredit->GetEntry(), cCredit->GetGUID()); - } - } - } - me->Kill(me); - return; - } else uiExplosionTimer -= diff; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_seaforium_depth_chargeAI(creature); - } -}; - -/*###### ## Help Those That Cannot Help Themselves, Quest 11876 ######*/ @@ -2577,8 +2398,6 @@ void AddSC_borean_tundra() new npc_corastrasza(); new npc_iruk(); new npc_nerubar_victim(); - new npc_jenny(); - new npc_fezzix_geartwist(); new npc_nesingwary_trapper(); new npc_lurgglbr(); new npc_nexus_drake_hatchling(); @@ -2593,7 +2412,6 @@ void AddSC_borean_tundra() new npc_bonker_togglevolt(); new npc_trapped_mammoth_calf(); new npc_magmoth_crusher(); - new npc_seaforium_depth_charge(); new npc_valiance_keep_cannoneer(); new npc_warmage_coldarra(); new npc_hidden_cultist(); diff --git a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp index 8cb7630e60e..8bc0ba1722a 100644 --- a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp +++ b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp @@ -80,7 +80,7 @@ public: switch (eventId) { case EVENT_VOID_BLAST: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) { DoCast(target, SPELL_VOID_BLAST); ++VoidBlastCounter; diff --git a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_broggok.cpp b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_broggok.cpp index bd16fe9edd2..15c660b4ad7 100644 --- a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_broggok.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_broggok.cpp @@ -114,7 +114,6 @@ class boss_broggok : public CreatureScript break; } } - }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Outland/zone_shattrath_city.cpp b/src/server/scripts/Outland/zone_shattrath_city.cpp index 9a62cde005c..c2e7df19ca1 100644 --- a/src/server/scripts/Outland/zone_shattrath_city.cpp +++ b/src/server/scripts/Outland/zone_shattrath_city.cpp @@ -30,7 +30,6 @@ npc_shattrathflaskvendors npc_zephyr npc_kservant npc_ishanah -npc_khadgar EndContentData */ #include "ScriptMgr.h" @@ -469,73 +468,6 @@ public: } }; -/*###### -# npc_khadgar -######*/ - -#define KHADGAR_GOSSIP_1 "I've heard your name spoken only in whispers, mage. Who are you?" -#define KHADGAR_GOSSIP_2 "Go on, please." -#define KHADGAR_GOSSIP_3 "I see." //6th too this -#define KHADGAR_GOSSIP_4 "What did you do then?" -#define KHADGAR_GOSSIP_5 "What happened next?" -#define KHADGAR_GOSSIP_7 "There was something else I wanted to ask you." - -class npc_khadgar : public CreatureScript -{ -public: - npc_khadgar() : CreatureScript("npc_khadgar") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF+1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - player->SEND_GOSSIP_MENU(9876, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+2: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); - player->SEND_GOSSIP_MENU(9877, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+3: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); - player->SEND_GOSSIP_MENU(9878, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+4: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); - player->SEND_GOSSIP_MENU(9879, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+5: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); - player->SEND_GOSSIP_MENU(9880, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+6: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_7, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+7); - player->SEND_GOSSIP_MENU(9881, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+7: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->SEND_GOSSIP_MENU(9243, creature->GetGUID()); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(10211) != QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - - player->SEND_GOSSIP_MENU(9243, creature->GetGUID()); - - return true; - } -}; - void AddSC_shattrath_city() { new npc_raliq_the_drunk(); @@ -544,5 +476,4 @@ void AddSC_shattrath_city() new npc_zephyr(); new npc_kservant(); new npc_ishanah(); - new npc_khadgar(); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index ea90c13f747..d04b25147c7 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -33,7 +33,6 @@ npc_guardian 100% guardianAI used to prevent players from accessin npc_garments_of_quests 80% NPC's related to all Garments of-quests 5621, 5624, 5625, 5648, 565 npc_injured_patient 100% patients for triage-quests (6622 and 6624) npc_doctor 100% Gustaf Vanhowzen and Gregory Victor, quest 6622 and 6624 (Triage) -npc_mount_vendor 100% Regular mount vendors all over the world. Display gossip if player doesn't meet the requirements to buy npc_sayge 100% Darkmoon event fortune teller, buff player based on answers given npc_snake_trap_serpents 80% AI for snakes that summoned by Snake Trap npc_shadowfiend 100% restore 5% of owner's mana when shadowfiend die from damage @@ -1170,105 +1169,6 @@ public: }; /*###### -## npc_mount_vendor -######*/ - -class npc_mount_vendor : public CreatureScript -{ -public: - npc_mount_vendor() : CreatureScript("npc_mount_vendor") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - bool canBuy = false; - uint32 vendor = creature->GetEntry(); - uint8 race = player->getRace(); - - switch (vendor) - { - case 384: //Katie Hunter - case 1460: //Unger Statforth - case 2357: //Merideth Carlson - case 4885: //Gregor MacVince - if (player->GetReputationRank(72) != REP_EXALTED && race != RACE_HUMAN) - player->SEND_GOSSIP_MENU(5855, creature->GetGUID()); - else canBuy = true; - break; - case 1261: //Veron Amberstill - if (player->GetReputationRank(47) != REP_EXALTED && race != RACE_DWARF) - player->SEND_GOSSIP_MENU(5856, creature->GetGUID()); - else canBuy = true; - break; - case 3362: //Ogunaro Wolfrunner - if (player->GetReputationRank(76) != REP_EXALTED && race != RACE_ORC) - player->SEND_GOSSIP_MENU(5841, creature->GetGUID()); - else canBuy = true; - break; - case 3685: //Harb Clawhoof - if (player->GetReputationRank(81) != REP_EXALTED && race != RACE_TAUREN) - player->SEND_GOSSIP_MENU(5843, creature->GetGUID()); - else canBuy = true; - break; - case 4730: //Lelanai - if (player->GetReputationRank(69) != REP_EXALTED && race != RACE_NIGHTELF) - player->SEND_GOSSIP_MENU(5844, creature->GetGUID()); - else canBuy = true; - break; - case 4731: //Zachariah Post - if (player->GetReputationRank(68) != REP_EXALTED && race != RACE_UNDEAD_PLAYER) - player->SEND_GOSSIP_MENU(5840, creature->GetGUID()); - else canBuy = true; - break; - case 7952: //Zjolnir - if (player->GetReputationRank(530) != REP_EXALTED && race != RACE_TROLL) - player->SEND_GOSSIP_MENU(5842, creature->GetGUID()); - else canBuy = true; - break; - case 7955: //Milli Featherwhistle - if (player->GetReputationRank(54) != REP_EXALTED && race != RACE_GNOME) - player->SEND_GOSSIP_MENU(5857, creature->GetGUID()); - else canBuy = true; - break; - case 16264: //Winaestra - if (player->GetReputationRank(911) != REP_EXALTED && race != RACE_BLOODELF) - player->SEND_GOSSIP_MENU(10305, creature->GetGUID()); - else canBuy = true; - break; - case 17584: //Torallius the Pack Handler - if (player->GetReputationRank(930) != REP_EXALTED && race != RACE_DRAENEI) - player->SEND_GOSSIP_MENU(10239, creature->GetGUID()); - else canBuy = true; - break; - case 48510: //Kall Worthaton - if (player->GetReputationRank(1133) != REP_EXALTED && race != RACE_GOBLIN) - player->SEND_GOSSIP_MENU(17494, creature->GetGUID()); - else canBuy = true; - break; - } - - if (canBuy) - { - if (creature->IsVendor()) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - } - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; - } -}; - -/*###### ## npc_sayge ######*/ @@ -2488,7 +2388,6 @@ void AddSC_npcs_special() new npc_injured_patient(); new npc_garments_of_quests(); new npc_guardian(); - new npc_mount_vendor(); new npc_sayge(); new npc_steam_tonk(); new npc_tonk_mine(); diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp index 4e46ff0e3a1..abdf2a2cc3c 100644 --- a/src/server/shared/Database/MySQLConnection.cpp +++ b/src/server/shared/Database/MySQLConnection.cpp @@ -57,14 +57,14 @@ m_connectionFlags(CONNECTION_ASYNC) MySQLConnection::~MySQLConnection() { + if (m_worker) + delete m_worker; + for (size_t i = 0; i < m_stmts.size(); ++i) delete m_stmts[i]; if (m_Mysql) mysql_close(m_Mysql); - - if (m_worker) - delete m_worker; } void MySQLConnection::Close() diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h index 64665c2b198..c8f8e2dbd90 100644 --- a/src/server/shared/Networking/AsyncAcceptor.h +++ b/src/server/shared/Networking/AsyncAcceptor.h @@ -23,51 +23,68 @@ using boost::asio::ip::tcp; -template <class T> class AsyncAcceptor { public: - AsyncAcceptor(boost::asio::io_service& ioService, std::string bindIp, int port) : - _acceptor(ioService, tcp::endpoint(boost::asio::ip::address::from_string(bindIp), port)), - _socket(ioService) - { - AsyncAccept(); - }; + typedef void(*ManagerAcceptHandler)(tcp::socket&& newSocket); - AsyncAcceptor(boost::asio::io_service& ioService, std::string bindIp, int port, bool tcpNoDelay) : + AsyncAcceptor(boost::asio::io_service& ioService, std::string const& bindIp, uint16 port) : _acceptor(ioService, tcp::endpoint(boost::asio::ip::address::from_string(bindIp), port)), _socket(ioService) { - _acceptor.set_option(boost::asio::ip::tcp::no_delay(tcpNoDelay)); + } - AsyncAccept(); - }; + template <class T> + void AsyncAccept(); -private: - void AsyncAccept() + void AsyncAcceptManaged(ManagerAcceptHandler mgrHandler) { - _acceptor.async_accept(_socket, [this](boost::system::error_code error) + _acceptor.async_accept(_socket, [this, mgrHandler](boost::system::error_code error) { if (!error) { try { - // this-> is required here to fix an segmentation fault in gcc 4.7.2 - reason is lambdas in a templated class - std::make_shared<T>(std::move(this->_socket))->Start(); + _socket.non_blocking(true); + + mgrHandler(std::move(_socket)); } catch (boost::system::system_error const& err) { - TC_LOG_INFO("network", "Failed to retrieve client's remote address %s", err.what()); + TC_LOG_INFO("network", "Failed to initialize client's socket %s", err.what()); } } - // lets slap some more this-> on this so we can fix this bug with gcc 4.7.2 throwing internals in yo face - this->AsyncAccept(); + AsyncAcceptManaged(mgrHandler); }); } +private: tcp::acceptor _acceptor; tcp::socket _socket; }; +template<class T> +void AsyncAcceptor::AsyncAccept() +{ + _acceptor.async_accept(_socket, [this](boost::system::error_code error) + { + if (!error) + { + try + { + // this-> is required here to fix an segmentation fault in gcc 4.7.2 - reason is lambdas in a templated class + std::make_shared<T>(std::move(this->_socket))->Start(); + } + catch (boost::system::system_error const& err) + { + TC_LOG_INFO("network", "Failed to retrieve client's remote address %s", err.what()); + } + } + + // lets slap some more this-> on this so we can fix this bug with gcc 4.7.2 throwing internals in yo face + this->AsyncAccept<T>(); + }); +} + #endif /* __ASYNCACCEPT_H_ */ diff --git a/src/server/shared/Networking/MessageBuffer.h b/src/server/shared/Networking/MessageBuffer.h index c7f8ba31a71..2115bea3f47 100644 --- a/src/server/shared/Networking/MessageBuffer.h +++ b/src/server/shared/Networking/MessageBuffer.h @@ -26,42 +26,74 @@ class MessageBuffer typedef std::vector<uint8>::size_type size_type; public: - MessageBuffer() : _wpos(0), _storage() { } + MessageBuffer() : _wpos(0), _rpos(0), _storage() + { + _storage.resize(4096); + } - MessageBuffer(MessageBuffer const& right) : _wpos(right._wpos), _storage(right._storage) { } + explicit MessageBuffer(std::size_t initialSize) : _wpos(0), _rpos(0), _storage() + { + _storage.resize(initialSize); + } + + MessageBuffer(MessageBuffer const& right) : _wpos(right._wpos), _rpos(right._rpos), _storage(right._storage) + { + } - MessageBuffer(MessageBuffer&& right) : _wpos(right._wpos), _storage(right.Move()) { } + MessageBuffer(MessageBuffer&& right) : _wpos(right._wpos), _rpos(right._rpos), _storage(right.Move()) { } void Reset() { - _storage.clear(); _wpos = 0; + _rpos = 0; } - bool IsMessageReady() const { return _wpos == _storage.size(); } + void Resize(size_type bytes) + { + _storage.resize(bytes); + } - size_type GetSize() const { return _storage.size(); } + uint8* GetBasePointer() { return _storage.data(); } - size_type GetReadyDataSize() const { return _wpos; } + uint8* GetReadPointer() { return &_storage[_rpos]; } - size_type GetMissingSize() const { return _storage.size() - _wpos; } + uint8* GetWritePointer() { return &_storage[_wpos]; } - uint8* Data() { return _storage.data(); } + void ReadCompleted(size_type bytes) { _rpos += bytes; } - void Grow(size_type bytes) - { - _storage.resize(_storage.size() + bytes); - } + void WriteCompleted(size_type bytes) { _wpos += bytes; } - uint8* GetWritePointer() { return &_storage[_wpos]; } + size_type GetActiveSize() const { return _wpos - _rpos; } - void WriteCompleted(size_type bytes) { _wpos += bytes; } + size_type GetRemainingSpace() const { return _storage.size() - _wpos; } + + size_type GetBufferSize() const { return _storage.size(); } + + // Discards inactive data + void Normalize() + { + if (_rpos) + { + if (_rpos != _wpos) + memmove(GetBasePointer(), GetReadPointer(), GetActiveSize()); + _wpos -= _rpos; + _rpos = 0; + } + } - void ResetWritePointer() { _wpos = 0; } + void Write(void* data, std::size_t size) + { + if (size) + { + memcpy(GetWritePointer(), data, size); + WriteCompleted(size); + } + } std::vector<uint8>&& Move() { _wpos = 0; + _rpos = 0; return std::move(_storage); } @@ -70,6 +102,7 @@ public: if (this != &right) { _wpos = right._wpos; + _rpos = right._rpos; _storage = right._storage; } @@ -81,6 +114,7 @@ public: if (this != &right) { _wpos = right._wpos; + _rpos = right._rpos; _storage = right.Move(); } @@ -89,6 +123,7 @@ public: private: size_type _wpos; + size_type _rpos; std::vector<uint8> _storage; }; diff --git a/src/server/shared/Networking/NetworkThread.h b/src/server/shared/Networking/NetworkThread.h new file mode 100644 index 00000000000..043aee56504 --- /dev/null +++ b/src/server/shared/Networking/NetworkThread.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2008-2014 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/>. + */ + +#ifndef NetworkThread_h__ +#define NetworkThread_h__ + +#include "Define.h" +#include "Errors.h" +#include "Log.h" +#include "Timer.h" +#include <atomic> +#include <chrono> +#include <memory> +#include <mutex> +#include <set> +#include <thread> + +template<class SocketType> +class NetworkThread +{ +public: + NetworkThread() : _connections(0), _stopped(false), _thread(nullptr) + { + } + + virtual ~NetworkThread() + { + Stop(); + if (_thread) + { + Wait(); + delete _thread; + } + } + + void Stop() + { + _stopped = true; + } + + bool Start() + { + if (_thread) + return false; + + _thread = new std::thread(&NetworkThread::Run, this); + return true; + } + + void Wait() + { + ASSERT(_thread); + + _thread->join(); + delete _thread; + _thread = nullptr; + } + + int32 GetConnectionCount() const + { + return _connections; + } + + virtual void AddSocket(std::shared_ptr<SocketType> sock) + { + std::lock_guard<std::mutex> lock(_newSocketsLock); + + ++_connections; + _newSockets.insert(sock); + SocketAdded(sock); + } + +protected: + virtual void SocketAdded(std::shared_ptr<SocketType> /*sock*/) { } + virtual void SocketRemoved(std::shared_ptr<SocketType> /*sock*/) { } + + void AddNewSockets() + { + std::lock_guard<std::mutex> lock(_newSocketsLock); + + if (_newSockets.empty()) + return; + + for (typename SocketSet::const_iterator i = _newSockets.begin(); i != _newSockets.end(); ++i) + { + if (!(*i)->IsOpen()) + { + SocketRemoved(*i); + + --_connections; + } + else + _Sockets.insert(*i); + } + + _newSockets.clear(); + } + + void Run() + { + TC_LOG_DEBUG("misc", "Network Thread Starting"); + + typename SocketSet::iterator i, t; + + uint32 sleepTime = 10; + uint32 tickStart = 0, diff = 0; + while (!_stopped) + { + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + + tickStart = getMSTime(); + + AddNewSockets(); + + for (i = _Sockets.begin(); i != _Sockets.end();) + { + if (!(*i)->Update()) + { + if ((*i)->IsOpen()) + (*i)->CloseSocket(); + + SocketRemoved(*i); + + --_connections; + _Sockets.erase(i++); + } + else + ++i; + } + + diff = GetMSTimeDiffToNow(tickStart); + sleepTime = diff > 10 ? 0 : 10 - diff; + } + + TC_LOG_DEBUG("misc", "Network Thread exits"); + } + +private: + typedef std::set<std::shared_ptr<SocketType> > SocketSet; + + std::atomic<int32> _connections; + std::atomic<bool> _stopped; + + std::thread* _thread; + + SocketSet _Sockets; + + std::mutex _newSocketsLock; + SocketSet _newSockets; +}; + +#endif // NetworkThread_h__ diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index 3bd30bd731b..17f48343485 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -35,32 +35,40 @@ using boost::asio::ip::tcp; #define READ_BLOCK_SIZE 4096 -template<class T, class PacketType> +template<class T> class Socket : public std::enable_shared_from_this<T> { - typedef typename std::conditional<std::is_pointer<PacketType>::value, PacketType, PacketType const&>::type WritePacketType; - public: - Socket(tcp::socket&& socket, std::size_t headerSize) : _socket(std::move(socket)), _remoteAddress(_socket.remote_endpoint().address()), - _remotePort(_socket.remote_endpoint().port()), _readHeaderBuffer(), _readDataBuffer(), _closed(false), _closing(false) + explicit Socket(tcp::socket&& socket) : _socket(std::move(socket)), _remoteAddress(_socket.remote_endpoint().address()), + _remotePort(_socket.remote_endpoint().port()), _readBuffer(), _closed(false), _closing(false), _isWritingAsync(false) { - _readHeaderBuffer.Grow(headerSize); + _readBuffer.Resize(READ_BLOCK_SIZE); } virtual ~Socket() { boost::system::error_code error; _socket.close(error); - - while (!_writeQueue.empty()) - { - DeletePacket(_writeQueue.front()); - _writeQueue.pop(); - } } virtual void Start() = 0; + virtual bool Update() + { + if (!IsOpen()) + return false; + +#ifndef BOOST_ASIO_HAS_IOCP + if (_isWritingAsync || (!_writeBuffer.GetActiveSize() && _writeQueue.empty())) + return true; + + for (; WriteHandler(boost::system::error_code(), 0);) + ; +#endif + + return true; + } + boost::asio::ip::address GetRemoteIpAddress() const { return _remoteAddress; @@ -71,31 +79,14 @@ public: return _remotePort; } - void AsyncReadHeader() + void AsyncRead() { if (!IsOpen()) return; - _readHeaderBuffer.ResetWritePointer(); - _readDataBuffer.Reset(); - - AsyncReadMissingHeaderData(); - } - - void AsyncReadData(std::size_t size) - { - if (!IsOpen()) - return; - - if (!size) - { - // if this is a packet with 0 length body just invoke handler directly - ReadDataHandler(); - return; - } - - _readDataBuffer.Grow(size); - AsyncReadMissingData(); + _readBuffer.Normalize(); + _socket.async_read_some(boost::asio::buffer(_readBuffer.GetWritePointer(), READ_BLOCK_SIZE), + std::bind(&Socket<T>::ReadHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void ReadData(std::size_t size) @@ -105,13 +96,11 @@ public: boost::system::error_code error; - _readDataBuffer.Grow(size); - - std::size_t bytesRead = boost::asio::read(_socket, boost::asio::buffer(_readDataBuffer.GetWritePointer(), size), error); + std::size_t bytesRead = boost::asio::read(_socket, boost::asio::buffer(_readBuffer.GetWritePointer(), size), error); - _readDataBuffer.WriteCompleted(bytesRead); + _readBuffer.WriteCompleted(bytesRead); - if (error || !_readDataBuffer.IsMessageReady()) + if (error || bytesRead != size) { TC_LOG_DEBUG("network", "Socket::ReadData: %s errored with: %i (%s)", GetRemoteIpAddress().to_string().c_str(), error.value(), error.message().c_str()); @@ -120,15 +109,19 @@ public: } } - void AsyncWrite(WritePacketType data) + void QueuePacket(MessageBuffer&& buffer, std::unique_lock<std::mutex>& guard) { - boost::asio::async_write(_socket, boost::asio::buffer(data), std::bind(&Socket<T, PacketType>::WriteHandler, this->shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); + + _writeQueue.push(std::move(buffer)); + +#ifdef BOOST_ASIO_HAS_IOCP + AsyncProcessQueue(guard); +#endif } bool IsOpen() const { return !_closed && !_closing; } - virtual void CloseSocket() + void CloseSocket() { if (_closed.exchange(true)) return; @@ -143,39 +136,37 @@ public: /// Marks the socket for closing after write buffer becomes empty void DelayedCloseSocket() { _closing = true; } - virtual bool IsHeaderReady() const { return _readHeaderBuffer.IsMessageReady(); } - virtual bool IsDataReady() const { return _readDataBuffer.IsMessageReady(); } - - uint8* GetHeaderBuffer() { return _readHeaderBuffer.Data(); } - uint8* GetDataBuffer() { return _readDataBuffer.Data(); } - - size_t GetHeaderSize() const { return _readHeaderBuffer.GetReadyDataSize(); } - size_t GetDataSize() const { return _readDataBuffer.GetReadyDataSize(); } - - MessageBuffer&& MoveHeader() { return std::move(_readHeaderBuffer); } - MessageBuffer&& MoveData() { return std::move(_readDataBuffer); } + MessageBuffer& GetReadBuffer() { return _readBuffer; } protected: - virtual void ReadHeaderHandler() = 0; - virtual void ReadDataHandler() = 0; - - std::mutex _writeLock; - std::queue<PacketType> _writeQueue; + virtual void ReadHandler() = 0; -private: - void AsyncReadMissingHeaderData() + bool AsyncProcessQueue(std::unique_lock<std::mutex>&) { - _socket.async_read_some(boost::asio::buffer(_readHeaderBuffer.GetWritePointer(), std::min<std::size_t>(READ_BLOCK_SIZE, _readHeaderBuffer.GetMissingSize())), - std::bind(&Socket<T, PacketType>::ReadHeaderHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + if (_isWritingAsync) + return true; + + _isWritingAsync = true; + +#ifdef BOOST_ASIO_HAS_IOCP + MessageBuffer& buffer = _writeQueue.front(); + _socket.async_write_some(boost::asio::buffer(buffer.GetReadPointer(), buffer.GetActiveSize()), std::bind(&Socket<T>::WriteHandler, + this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +#else + _socket.async_write_some(boost::asio::null_buffers(), std::bind(&Socket<T>::WriteHandler, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); +#endif + + return true; } - void AsyncReadMissingData() - { - _socket.async_read_some(boost::asio::buffer(_readDataBuffer.GetWritePointer(), std::min<std::size_t>(READ_BLOCK_SIZE, _readDataBuffer.GetMissingSize())), - std::bind(&Socket<T, PacketType>::ReadDataHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } + std::mutex _writeLock; + std::queue<MessageBuffer> _writeQueue; +#ifndef BOOST_ASIO_HAS_IOCP + MessageBuffer _writeBuffer; +#endif - void ReadHeaderHandlerInternal(boost::system::error_code error, size_t transferredBytes) +private: + void ReadHandlerInternal(boost::system::error_code error, size_t transferredBytes) { if (error) { @@ -183,70 +174,130 @@ private: return; } - _readHeaderBuffer.WriteCompleted(transferredBytes); - if (!IsHeaderReady()) + _readBuffer.WriteCompleted(transferredBytes); + ReadHandler(); + } + +#ifdef BOOST_ASIO_HAS_IOCP + + void WriteHandler(boost::system::error_code error, std::size_t transferedBytes) + { + if (!error) { - // incomplete, read more - AsyncReadMissingHeaderData(); - return; - } + std::unique_lock<std::mutex> deleteGuard(_writeLock); + + _isWritingAsync = false; + _writeQueue.front().ReadCompleted(transferedBytes); + if (!_writeQueue.front().GetActiveSize()) + _writeQueue.pop(); - ReadHeaderHandler(); + if (!_writeQueue.empty()) + AsyncProcessQueue(deleteGuard); + else if (_closing) + CloseSocket(); + } + else + CloseSocket(); } - void ReadDataHandlerInternal(boost::system::error_code error, size_t transferredBytes) +#else + + bool WriteHandler(boost::system::error_code /*error*/, std::size_t /*transferedBytes*/) { + std::unique_lock<std::mutex> guard(_writeLock, std::try_to_lock); + if (!guard) + return false; + + if (!IsOpen()) + return false; + + std::size_t bytesToSend = _writeBuffer.GetActiveSize(); + + if (bytesToSend == 0) + return HandleQueue(guard); + + boost::system::error_code error; + std::size_t bytesWritten = _socket.write_some(boost::asio::buffer(_writeBuffer.GetReadPointer(), bytesToSend), error); + if (error) { - CloseSocket(); - return; - } + if (error == boost::asio::error::would_block || error == boost::asio::error::try_again) + return AsyncProcessQueue(guard); - _readDataBuffer.WriteCompleted(transferredBytes); - if (!IsDataReady()) + return false; + } + else if (bytesWritten == 0) + return false; + else if (bytesWritten < bytesToSend) //now n > 0 { - // incomplete, read more - AsyncReadMissingData(); - return; + _writeBuffer.ReadCompleted(bytesWritten); + _writeBuffer.Normalize(); + return AsyncProcessQueue(guard); } - ReadDataHandler(); + // now bytesWritten == bytesToSend + _writeBuffer.Reset(); + + return HandleQueue(guard); } - void WriteHandler(boost::system::error_code error, size_t /*transferedBytes*/) + bool HandleQueue(std::unique_lock<std::mutex>& guard) { - if (!error) + if (_writeQueue.empty()) + { + _isWritingAsync = false; + return false; + } + + MessageBuffer& queuedMessage = _writeQueue.front(); + + std::size_t bytesToSend = queuedMessage.GetActiveSize(); + + boost::system::error_code error; + std::size_t bytesSent = _socket.write_some(boost::asio::buffer(queuedMessage.GetReadPointer(), bytesToSend), error); + + if (error) { - std::lock_guard<std::mutex> deleteGuard(_writeLock); + if (error == boost::asio::error::would_block || error == boost::asio::error::try_again) + return AsyncProcessQueue(guard); - DeletePacket(_writeQueue.front()); _writeQueue.pop(); + return false; + } + else if (bytesSent == 0) + { + _writeQueue.pop(); + return false; + } + else if (bytesSent < bytesToSend) // now n > 0 + { + queuedMessage.ReadCompleted(bytesSent); + return AsyncProcessQueue(guard); + } - if (!_writeQueue.empty()) - AsyncWrite(_writeQueue.front()); - else if (_closing) - CloseSocket(); + _writeQueue.pop(); + if (_writeQueue.empty()) + { + _isWritingAsync = false; + return false; } - else - CloseSocket(); - } - template<typename Q = PacketType> - typename std::enable_if<std::is_pointer<Q>::value>::type DeletePacket(PacketType& packet) { delete packet; } + return true; + } - template<typename Q = PacketType> - typename std::enable_if<!std::is_pointer<Q>::value>::type DeletePacket(PacketType const& /*packet*/) { } +#endif tcp::socket _socket; boost::asio::ip::address _remoteAddress; uint16 _remotePort; - MessageBuffer _readHeaderBuffer; - MessageBuffer _readDataBuffer; + MessageBuffer _readBuffer; std::atomic<bool> _closed; std::atomic<bool> _closing; + + bool _isWritingAsync; }; #endif // __SOCKET_H__ diff --git a/src/server/shared/Networking/SocketMgr.h b/src/server/shared/Networking/SocketMgr.h new file mode 100644 index 00000000000..47bd7794a4c --- /dev/null +++ b/src/server/shared/Networking/SocketMgr.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2008-2014 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/>. + */ + +#ifndef SocketMgr_h__ +#define SocketMgr_h__ + +#include "AsyncAcceptor.h" +#include "Config.h" +#include "Errors.h" +#include "NetworkThread.h" +#include <boost/asio/ip/tcp.hpp> +#include <memory> + +using boost::asio::ip::tcp; + +template<class SocketType> +class SocketMgr +{ +public: + virtual ~SocketMgr() + { + delete[] _threads; + } + + virtual bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) + { + _threadCount = sConfigMgr->GetIntDefault("Network.Threads", 1); + + if (_threadCount <= 0) + { + TC_LOG_ERROR("misc", "Network.Threads is wrong in your config file"); + return false; + } + + _acceptor = new AsyncAcceptor(service, bindIp, port); + _threads = CreateThreads(); + + ASSERT(_threads); + + for (int32 i = 0; i < _threadCount; ++i) + _threads[i].Start(); + + return true; + } + + virtual void StopNetwork() + { + if (_threadCount != 0) + for (int32 i = 0; i < _threadCount; ++i) + _threads[i].Stop(); + + Wait(); + } + + void Wait() + { + if (_threadCount != 0) + for (int32 i = 0; i < _threadCount; ++i) + _threads[i].Wait(); + } + + virtual void OnSocketOpen(tcp::socket&& sock) + { + size_t min = 0; + + for (int32 i = 1; i < _threadCount; ++i) + if (_threads[i].GetConnectionCount() < _threads[min].GetConnectionCount()) + min = i; + + try + { + std::shared_ptr<SocketType> newSocket = std::make_shared<SocketType>(std::move(sock)); + newSocket->Start(); + + _threads[min].AddSocket(newSocket); + } + catch (boost::system::system_error const& err) + { + TC_LOG_INFO("network", "Failed to retrieve client's remote address %s", err.what()); + } + } + + int32 GetNetworkThreadCount() const { return _threadCount; } + +protected: + SocketMgr() : _threads(nullptr), _threadCount(1) + { + } + + virtual NetworkThread<SocketType>* CreateThreads() const = 0; + + AsyncAcceptor* _acceptor; + NetworkThread<SocketType>* _threads; + int32 _threadCount; +}; + +#endif // SocketMgr_h__ diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index e149902af02..191e44b3e93 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -46,6 +46,7 @@ #include "CliRunnable.h" #include "SystemConfig.h" #include "WorldSocket.h" +#include "WorldSocketMgr.h" using namespace boost::program_options; @@ -82,7 +83,7 @@ uint32 realmID; ///< Id of the realm void SignalHandler(const boost::system::error_code& error, int signalNumber); void FreezeDetectorHandler(const boost::system::error_code& error); -AsyncAcceptor<RASession>* StartRaSocketAcceptor(boost::asio::io_service& ioService); +AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService); bool StartDB(); void StopDB(); void WorldUpdateLoop(); @@ -203,7 +204,7 @@ extern int main(int argc, char** argv) } // Start the Remote Access port (acceptor) if enabled - AsyncAcceptor<RASession>* raAcceptor = nullptr; + AsyncAcceptor* raAcceptor = nullptr; if (sConfigMgr->GetBoolDefault("Ra.Enable", false)) raAcceptor = StartRaSocketAcceptor(_ioService); @@ -217,11 +218,8 @@ extern int main(int argc, char** argv) // Launch the worldserver listener socket uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD)); std::string worldListener = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - bool tcpNoDelay = sConfigMgr->GetBoolDefault("Network.TcpNodelay", true); - AsyncAcceptor<WorldSocket> worldAcceptor(_ioService, worldListener, worldPort, tcpNoDelay); - - sScriptMgr->OnNetworkStart(); + sWorldSocketMgr.StartNetwork(_ioService, worldListener, worldPort); // Set server online (allow connecting now) LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); @@ -252,6 +250,8 @@ extern int main(int argc, char** argv) // unload battleground templates before different singletons destroyed sBattlegroundMgr->DeleteAllBattlegrounds(); + sWorldSocketMgr.StopNetwork(); + sInstanceSaveMgr->Unload(); sMapMgr->UnloadAll(); // unload all grids (including locked in memory) sObjectAccessor->UnloadAll(); // unload 'i_player2corpse' storage and remove from world @@ -379,12 +379,14 @@ void FreezeDetectorHandler(const boost::system::error_code& error) } } -AsyncAcceptor<RASession>* StartRaSocketAcceptor(boost::asio::io_service& ioService) +AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService) { uint16 raPort = uint16(sConfigMgr->GetIntDefault("Ra.Port", 3443)); std::string raListener = sConfigMgr->GetStringDefault("Ra.IP", "0.0.0.0"); - return new AsyncAcceptor<RASession>(ioService, raListener, raPort); + AsyncAcceptor* acceptor = new AsyncAcceptor(ioService, raListener, raPort); + acceptor->AsyncAccept<RASession>(); + return acceptor; } /// Initialize connection to the databases |