diff options
Diffstat (limited to 'src')
28 files changed, 1113 insertions, 591 deletions
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index f26c0342654..01dbaa6aa9a 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -118,7 +118,8 @@ int main(int argc, char** argv) } std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - AsyncAcceptor<AuthSession> authServer(_ioService, bindIp, port); + AsyncAcceptor authServer(_ioService, bindIp, port); + authServer.AsyncAccept<AuthSession>(); // Set signal handlers boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM); diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 8276183fb10..e8241ac49b1 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) - Base::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); @@ -393,7 +405,7 @@ bool AuthSession::HandleLogonChallenge() pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); } - AsyncWrite(pkt); + SendPacket(pkt); return true; } @@ -403,7 +415,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) @@ -515,10 +527,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) @@ -528,7 +539,7 @@ bool AuthSession::HandleLogonProof() packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); packet << uint8(3); packet << uint8(0); - AsyncWrite(packet); + SendPacket(packet); return false; } } @@ -559,7 +570,7 @@ bool AuthSession::HandleLogonProof() std::memcpy(packet.contents(), &proof, sizeof(proof)); } - AsyncWrite(packet); + SendPacket(packet); _isAuthenticated = true; } else @@ -569,7 +580,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()); @@ -638,7 +649,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); @@ -682,14 +693,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; @@ -710,7 +721,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; } @@ -870,7 +881,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 3497e3a030c..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> Base; - 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,15 +44,13 @@ public: void Start() override { - AsyncReadHeader(); + AsyncRead(); } - using Base::AsyncWrite; - 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/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index ba94a99c8dd..fa1f866120c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -810,6 +810,9 @@ bool Creature::Create(uint32 guidlow, Map* map, uint32 phaseMask, uint32 entry, m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_GHOST); } + if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING) + AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + return true; } diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 9b332bb5de4..9be71be1eed 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -39,22 +39,23 @@ class WorldSession; enum CreatureFlagsExtra { - CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group - CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility) - CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry - CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry - CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block - CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks - CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP - CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature - CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me - CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) - CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard - CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes - CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills - CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre - CREATURE_FLAG_EXTRA_ALL_DIMINISH = 0x00100000, // Creature is subject to all diminishing returns as player are - CREATURE_FLAG_EXTRA_DUNGEON_BOSS = 0x10000000 // creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB) + CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group + CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility) + CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry + CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry + CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block + CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks + CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP + CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature + CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me + CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) + CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard + CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes + CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills + CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre + CREATURE_FLAG_EXTRA_ALL_DIMINISH = 0x00100000, // Creature is subject to all diminishing returns as player are + CREATURE_FLAG_EXTRA_DUNGEON_BOSS = 0x10000000, // creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB) + CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING = 0x20000000 // creature ignore pathfinding }; #define CREATURE_FLAG_EXTRA_DB_ALLOWED (CREATURE_FLAG_EXTRA_INSTANCE_BIND | CREATURE_FLAG_EXTRA_CIVILIAN | \ @@ -62,7 +63,7 @@ enum CreatureFlagsExtra CREATURE_FLAG_EXTRA_NO_CRUSH | CREATURE_FLAG_EXTRA_NO_XP_AT_KILL | CREATURE_FLAG_EXTRA_TRIGGER | \ CREATURE_FLAG_EXTRA_NO_TAUNT | CREATURE_FLAG_EXTRA_WORLDEVENT | CREATURE_FLAG_EXTRA_NO_CRIT | \ CREATURE_FLAG_EXTRA_NO_SKILLGAIN | CREATURE_FLAG_EXTRA_TAUNT_DIMINISH | CREATURE_FLAG_EXTRA_ALL_DIMINISH | \ - CREATURE_FLAG_EXTRA_GUARD) + CREATURE_FLAG_EXTRA_GUARD | CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING) #define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index eceb0a8e7de..c1de3aae7cb 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1054,7 +1054,7 @@ enum TrinityStrings LANG_COMMAND_UNFREEZE = 5003, LANG_COMMAND_NO_FROZEN_PLAYERS = 5004, LANG_COMMAND_LIST_FREEZE = 5005, - LANG_COMMAND_FROZEN_PLAYERS = 5006, + LANG_COMMAND_PERMA_FROZEN_PLAYER = 5006, LANG_INSTANCE_RAID_GROUP_ONLY = 5007, LANG_INSTANCE_CLOSED = 5008, LANG_COMMAND_PLAYED_TO_ALL = 5009, @@ -1068,7 +1068,7 @@ enum TrinityStrings LANG_ARENA = 5016, LANG_RAID = 5017, //= 5018, - //= 5019, + LANG_COMMAND_TEMP_FROZEN_PLAYER = 5019, LANG_NPCINFO_PHASEMASK = 5020, LANG_NPCINFO_ARMOR = 5021, LANG_CHANNEL_ENABLE_OWNERSHIP = 5022, diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 445e42a7f08..321bc707879 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -227,7 +227,7 @@ void WorldSession::SendPacket(WorldPacket* packet) 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 ca8e2cd5a34..f8673e5d5b7 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -28,14 +28,14 @@ using boost::asio::ip::tcp; WorldSocket::WorldSocket(tcp::socket&& socket) - : Socket(std::move(socket), sizeof(ClientPktHeader)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr) + : Socket(std::move(socket)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr) { + _headerBuffer.Resize(sizeof(ClientPktHeader)); } void WorldSocket::Start() { - sScriptMgr->OnSocketOpen(shared_from_this()); - AsyncReadHeader(); + AsyncRead(); HandleSendAuthSession(); } @@ -53,14 +53,69 @@ void WorldSocket::HandleSendAuthSession() seed2.SetRand(16 * 8); packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds - AsyncWrite(packet); + SendPacket(packet); } -void WorldSocket::ReadHeaderHandler() +void WorldSocket::ReadHandler() { - _authCrypt.DecryptRecv(GetHeaderBuffer(), sizeof(ClientPktHeader)); + 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; + } + } - ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(GetHeaderBuffer()); + // just received fresh new payload + if (!ReadDataHandler()) + return; + + _headerBuffer.Reset(); + } + + AsyncRead(); +} + +bool WorldSocket::ReadHeaderHandler() +{ + ASSERT(_headerBuffer.GetActiveSize() == sizeof(ClientPktHeader)); + + _authCrypt.DecryptRecv(_headerBuffer.GetReadPointer(), sizeof(ClientPktHeader)); + + ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(_headerBuffer.GetReadPointer()); EndianConvertReverse(header->size); EndianConvert(header->cmd); @@ -74,24 +129,26 @@ 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() { - ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(GetHeaderBuffer()); + ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(_headerBuffer.GetReadPointer()); uint16 opcode = uint16(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()); @@ -122,7 +179,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. @@ -135,10 +192,10 @@ void WorldSocket::ReadDataHandler() } } - AsyncReadHeader(); + return true; } -void WorldSocket::AsyncWrite(WorldPacket& packet) +void WorldSocket::SendPacket(WorldPacket& packet) { if (!IsOpen()) return; @@ -150,15 +207,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) @@ -410,7 +479,7 @@ void WorldSocket::SendAuthResponseError(uint8 code) WorldPacket packet(SMSG_AUTH_RESPONSE, 1); packet << uint8(code); - AsyncWrite(packet); + SendPacket(packet); } void WorldSocket::HandlePing(WorldPacket& recvPacket) @@ -471,12 +540,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 0667c1b090a..d301e239340 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 "AuthCrypt.h" #include "ServerPktHeader.h" @@ -54,50 +44,8 @@ struct ClientPktHeader #pragma pack(pop) -struct WorldPacketBuffer +class WorldSocket : public Socket<WorldSocket> { - 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) - { - _buffers[0] = boost::asio::const_buffer(_header.header, _header.getHeaderLength()); - if (!_packet.empty()) - _buffers[1] = boost::asio::const_buffer(_packet.contents(), _packet.size()); - } - - const_iterator begin() const - { - return _buffers; - } - - const_iterator end() const - { - return _buffers + (_packet.empty() ? 1 : 2); - } - -private: - boost::asio::const_buffer _buffers[2]; - ServerPktHeader _header; - WorldPacket _packet; -}; - -namespace boost -{ - namespace asio - { - inline WorldPacketBuffer const& buffer(WorldPacketBuffer const& buf) - { - return buf; - } - } -} - -class WorldSocket : public Socket<WorldSocket, WorldPacketBuffer> -{ - typedef Socket<WorldSocket, WorldPacketBuffer> Base; - public: WorldSocket(tcp::socket&& socket); @@ -106,14 +54,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(); @@ -129,6 +75,9 @@ private: uint32 _OverSpeedPings; WorldSession* _worldSession; + + MessageBuffer _headerBuffer; + MessageBuffer _packetBuffer; }; #endif 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/game/World/World.cpp b/src/server/game/World/World.cpp index 5c65055f3c0..58e3cffa8de 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -856,6 +856,7 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_GM_VISIBLE_STATE] = sConfigMgr->GetIntDefault("GM.Visible", 2); m_int_configs[CONFIG_GM_CHAT] = sConfigMgr->GetIntDefault("GM.Chat", 2); m_int_configs[CONFIG_GM_WHISPERING_TO] = sConfigMgr->GetIntDefault("GM.WhisperingTo", 2); + m_int_configs[CONFIG_GM_FREEZE_DURATION] = sConfigMgr->GetIntDefault("GM.FreezeAuraDuration", 0); m_int_configs[CONFIG_GM_LEVEL_IN_GM_LIST] = sConfigMgr->GetIntDefault("GM.InGMList.Level", SEC_ADMINISTRATOR); m_int_configs[CONFIG_GM_LEVEL_IN_WHO_LIST] = sConfigMgr->GetIntDefault("GM.InWhoList.Level", SEC_ADMINISTRATOR); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 05bfcc21462..cc2078bc736 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -227,6 +227,7 @@ enum WorldIntConfigs CONFIG_GM_ACCEPT_TICKETS, CONFIG_GM_CHAT, CONFIG_GM_WHISPERING_TO, + CONFIG_GM_FREEZE_DURATION, CONFIG_GM_LEVEL_IN_GM_LIST, CONFIG_GM_LEVEL_IN_WHO_LIST, CONFIG_START_GM_LEVEL, diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 80b7710cd21..3e7b242f3cc 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -2241,69 +2241,100 @@ public: static bool HandleFreezeCommand(ChatHandler* handler, char const* args) { - std::string name; - Player* player; - char const* TargetName = strtok((char*)args, " "); // get entered name - if (!TargetName) // if no name entered use target - { - player = handler->getSelectedPlayer(); - if (player) //prevent crash with creature as target - { - name = player->GetName(); - normalizePlayerName(name); - } - } - else // if name entered + Player* player = handler->getSelectedPlayer(); // Selected player, if any. Might be null. + uint32 freezeDuration = 0; // Freeze Duration (in seconds) + bool canApplyFreeze = false; // Determines if every possible argument is set so Freeze can be applied + bool getDurationFromConfig = false; // If there's no given duration, we'll retrieve the world cfg value later + + /* + Possible Freeze Command Scenarios: + case 1 - .freeze (without args and a selected player) + case 2 - .freeze duration (with a selected player) + case 3 - .freeze player duration + case 4 - .freeze player (without specifying duration) + */ + + // case 1: .freeze + if (!*args) { - name = TargetName; - normalizePlayerName(name); - player = sObjectAccessor->FindPlayerByName(name); + // Might have a selected player. We'll check it later + // Get the duration from world cfg + getDurationFromConfig = true; } - - if (!player) + else { - handler->SendSysMessage(LANG_COMMAND_FREEZE_WRONG); - return true; + // Get the args that we might have (up to 2) + char const* arg1 = strtok((char*)args, " "); + char const* arg2 = strtok(NULL, " "); + + // Analyze them to see if we got either a playerName or duration or both + if (arg1) + { + if (isNumeric(arg1)) + { + // case 2: .freeze duration + // We have a selected player. We'll check him later + freezeDuration = uint32(atoi(arg1)); + canApplyFreeze = true; + } + else + { + // case 3 or 4: .freeze player duration | .freeze player + // find the player + std::string name = arg1; + normalizePlayerName(name); + player = sObjectAccessor->FindPlayerByName(name); + // Check if we have duration set + if (arg2 && isNumeric(arg2)) + { + freezeDuration = uint32(atoi(arg2)); + canApplyFreeze = true; + } + else + getDurationFromConfig = true; + } + } } - if (player == handler->GetSession()->GetPlayer()) + // Check if duration needs to be retrieved from config + if (getDurationFromConfig) { - handler->SendSysMessage(LANG_COMMAND_FREEZE_ERROR); - return true; + freezeDuration = sWorld->getIntConfig(CONFIG_GM_FREEZE_DURATION); + canApplyFreeze = true; } - // effect - if (player && (player != handler->GetSession()->GetPlayer())) + // Player and duration retrieval is over + if (canApplyFreeze) { - handler->PSendSysMessage(LANG_COMMAND_FREEZE, name.c_str()); - - // stop combat + make player unattackable + duel stop + stop some spells - player->setFaction(35); - player->CombatStop(); - if (player->IsNonMeleeSpellCast(true)) - player->InterruptNonMeleeSpells(true); - player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - - // if player class = hunter || warlock remove pet if alive - if ((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK)) + if (!player) // can be null if some previous selection failed + { + handler->SendSysMessage(LANG_COMMAND_FREEZE_WRONG); + return true; + } + else if (player == handler->GetSession()->GetPlayer()) + { + // Can't freeze himself + handler->SendSysMessage(LANG_COMMAND_FREEZE_ERROR); + return true; + } + else // Apply the effect { - if (Pet* pet = player->GetPet()) + // Add the freeze aura and set the proper duration + // Player combat status and flags are now handled + // in Freeze Spell AuraScript (OnApply) + Aura* freeze = player->AddAura(9454, player); + if (freeze) { - pet->SavePetToDB(PET_SAVE_AS_CURRENT); - // not let dismiss dead pet - if (pet->IsAlive()) - player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT); + if (freezeDuration) + freeze->SetDuration(freezeDuration * IN_MILLISECONDS); + handler->PSendSysMessage(LANG_COMMAND_FREEZE, player->GetName().c_str()); + // save player + player->SaveToDB(); + return true; } } - - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(9454)) - Aura::TryRefreshStackOrCreate(spellInfo, MAX_EFFECT_MASK, player, player); - - // save player - player->SaveToDB(); } - - return true; + return false; } static bool HandleUnFreezeCommand(ChatHandler* handler, char const*args) @@ -2329,15 +2360,10 @@ public: { handler->PSendSysMessage(LANG_COMMAND_UNFREEZE, name.c_str()); - // Reset player faction + allow combat + allow duels - player->setFactionForRace(player->getRace()); - player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - // Remove Freeze spell (allowing movement and spells) + // Player Flags + Neutral faction removal is now + // handled on the Freeze Spell AuraScript (OnRemove) player->RemoveAurasDueToSpell(9454); - - // Save player - player->SaveToDB(); } else { @@ -2394,7 +2420,17 @@ public: { Field* fields = result->Fetch(); std::string player = fields[0].GetString(); - handler->PSendSysMessage(LANG_COMMAND_FROZEN_PLAYERS, player.c_str()); + int32 remaintime = fields[1].GetInt32(); + // Save the frozen player to update remaining time in case of future .listfreeze uses + // before the frozen state expires + if (Player* frozen = sObjectAccessor->FindPlayerByName(player)) + frozen->SaveToDB(); + // Notify the freeze duration + if (remaintime == -1) // Permanent duration + handler->PSendSysMessage(LANG_COMMAND_PERMA_FROZEN_PLAYER, player.c_str()); + else + // show time left (seconds) + handler->PSendSysMessage(LANG_COMMAND_TEMP_FROZEN_PLAYER, player.c_str(), remaintime / IN_MILLISECONDS); } while (result->NextRow()); diff --git a/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp b/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp index 347c87b160b..62c85491a20 100644 --- a/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp +++ b/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp @@ -131,21 +131,28 @@ public: { boss_onyxiaAI(Creature* creature) : BossAI(creature, DATA_ONYXIA) { - Reset(); + Initialize(); } - void Reset() override + void Initialize() { - if (!IsCombatMovementAllowed()) - SetCombatMovement(true); - - _Reset(); - Phase = PHASE_START; MovePoint = urand(0, 5); PointData = GetMoveData(); SummonWhelpCount = 0; + triggerGUID = 0; + tankGUID = 0; IsMoving = false; + } + + void Reset() override + { + Initialize(); + + if (!IsCombatMovementAllowed()) + SetCombatMovement(true); + + _Reset(); instance->SetData(DATA_ONYXIA_PHASE, Phase); instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); @@ -153,17 +160,14 @@ public: void EnterCombat(Unit* /*who*/) override { + _EnterCombat(); Talk(SAY_AGGRO); - me->SetInCombatWithZone(); - - events.Reset(); events.ScheduleEvent(EVENT_FLAME_BREATH, urand(10000, 20000)); events.ScheduleEvent(EVENT_TAIL_SWEEP, urand(15000, 20000)); events.ScheduleEvent(EVENT_CLEAVE, urand(2000, 5000)); events.ScheduleEvent(EVENT_WING_BUFFET, urand(10000, 20000)); - instance->SetBossState(DATA_ONYXIA, IN_PROGRESS); instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); } @@ -226,11 +230,11 @@ public: me->SetCanFly(false); me->SetDisableGravity(false); me->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); - if (Creature* trigger = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_TRIGGER_GUID))) + if (Creature* trigger = ObjectAccessor::GetCreature(*me, triggerGUID)) me->Kill(trigger); me->SetReactState(REACT_AGGRESSIVE); // tank selection based on phase one. If tank is not there i take nearest one - if (Unit* tank = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_TANK_GUID))) + if (Unit* tank = ObjectAccessor::GetUnit(*me, tankGUID)) me->GetMotionMaster()->MoveChase(tank); else if (Unit* newtarget = SelectTarget(SELECT_TARGET_NEAREST, 0)) me->GetMotionMaster()->MoveChase(newtarget); @@ -246,7 +250,7 @@ public: me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); me->SetFacingTo(me->GetOrientation() + float(M_PI)); if (Creature * trigger = me->SummonCreature(NPC_TRIGGER, MiddleRoomLocation, TEMPSUMMON_CORPSE_DESPAWN)) - instance->SetData64(DATA_TRIGGER_GUID, trigger->GetGUID()); + triggerGUID = trigger->GetGUID(); me->GetMotionMaster()->MoveTakeoff(11, Phase2Floating); me->SetSpeed(MOVE_FLIGHT, 1.0f); Talk(SAY_PHASE_2_TRANS); @@ -329,7 +333,7 @@ public: { SetCombatMovement(false); Phase = PHASE_BREATH; - instance->SetData64(DATA_TANK_GUID, me->GetVictim()->GetGUID()); + tankGUID = me->GetVictim()->GetGUID(); me->SetReactState(REACT_PASSIVE); me->AttackStop(); me->GetMotionMaster()->MovePoint(10, Phase2Location); @@ -394,7 +398,7 @@ public: } if (!me->isMoving()) - if (Creature* trigger = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_TRIGGER_GUID))) + if (Creature* trigger = ObjectAccessor::GetCreature(*me, triggerGUID)) me->SetFacingToObject(trigger); events.Update(diff); @@ -470,6 +474,8 @@ public: uint8 Phase; uint8 MovePoint; uint8 SummonWhelpCount; + uint64 triggerGUID; + uint64 tankGUID; bool IsMoving; }; diff --git a/src/server/scripts/Kalimdor/OnyxiasLair/instance_onyxias_lair.cpp b/src/server/scripts/Kalimdor/OnyxiasLair/instance_onyxias_lair.cpp index 61b57d181a3..6aa50e71b17 100644 --- a/src/server/scripts/Kalimdor/OnyxiasLair/instance_onyxias_lair.cpp +++ b/src/server/scripts/Kalimdor/OnyxiasLair/instance_onyxias_lair.cpp @@ -54,8 +54,6 @@ public: SetBossNumber(EncounterCount); onyxiaGUID = 0; - triggerGUID = 0; - tankGUID = 0; onyxiaLiftoffTimer = 0; manyWhelpsCounter = 0; eruptTimer = 0; @@ -184,12 +182,6 @@ public: FloorEruptionGUIDQueue.push(data); eruptTimer = 2500; break; - case DATA_TRIGGER_GUID: - triggerGUID = data; - break; - case DATA_TANK_GUID: - tankGUID = data; - break; } } @@ -199,13 +191,6 @@ public: { case NPC_ONYXIA: return onyxiaGUID; - break; - case DATA_TRIGGER_GUID: - return triggerGUID; - break; - case DATA_TANK_GUID: - return tankGUID; - break; } return 0; diff --git a/src/server/scripts/Kalimdor/OnyxiasLair/onyxias_lair.h b/src/server/scripts/Kalimdor/OnyxiasLair/onyxias_lair.h index 0c423f35de6..c31988843fe 100644 --- a/src/server/scripts/Kalimdor/OnyxiasLair/onyxias_lair.h +++ b/src/server/scripts/Kalimdor/OnyxiasLair/onyxias_lair.h @@ -37,9 +37,7 @@ enum Data32 enum Data64 { DATA_ONYXIA_GUID = 0, - DATA_FLOOR_ERUPTION_GUID = 1, - DATA_TRIGGER_GUID = 2, - DATA_TANK_GUID = 3 + DATA_FLOOR_ERUPTION_GUID = 1 }; enum OnyxiaPhases diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index ced964048c2..03507c77efb 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -492,6 +492,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/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 45b640f27be..f02edbb5584 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/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 84e36b55c2c..e198a6a0c61 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -3609,6 +3609,79 @@ class spell_gen_eject_all_passengers : public SpellScriptLoader } }; +enum GMFreeze +{ + SPELL_GM_FREEZE = 9454 +}; + +class spell_gen_gm_freeze : public SpellScriptLoader +{ + public: + spell_gen_gm_freeze() : SpellScriptLoader("spell_gen_gm_freeze") { } + + class spell_gen_gm_freeze_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_gm_freeze_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_GM_FREEZE)) + return false; + return true; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // Do what was done before to the target in HandleFreezeCommand + if (Player* player = GetTarget()->ToPlayer()) + { + // stop combat + make player unattackable + duel stop + stop some spells + player->setFaction(35); + player->CombatStop(); + if (player->IsNonMeleeSpellCast(true)) + player->InterruptNonMeleeSpells(true); + player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + // if player class = hunter || warlock remove pet if alive + if ((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK)) + { + if (Pet* pet = player->GetPet()) + { + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + // not let dismiss dead pet + if (pet->IsAlive()) + player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT); + } + } + } + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // Do what was done before to the target in HandleUnfreezeCommand + if (Player* player = GetTarget()->ToPlayer()) + { + // Reset player faction + allow combat + allow duels + player->setFactionForRace(player->getRace()); + player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + // save player + player->SaveToDB(); + } + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_gen_gm_freeze_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_gm_freeze_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gen_gm_freeze_AuraScript(); + } +}; + void AddSC_generic_spell_scripts() { new spell_gen_absorb0_hitlimit1(); @@ -3689,4 +3762,5 @@ void AddSC_generic_spell_scripts() new spell_gen_wg_water(); new spell_gen_whisper_gulch_yogg_saron_whisper(); new spell_gen_eject_all_passengers(); + new spell_gen_gm_freeze(); } diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 0669d0b84f9..aa584d026fb 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -417,7 +417,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHARACTER_SOCIAL, "DELETE FROM character_social WHERE guid = ? AND friend = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER_SOCIAL_NOTE, "UPDATE character_social SET note = ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER_POSITION, "UPDATE characters SET position_x = ?, position_y = ?, position_z = ?, orientation = ?, map = ?, zone = ?, trans_x = 0, trans_y = 0, trans_z = 0, transguid = 0, taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name, character_aura.remaintime FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_ONLINE, "SELECT name, account, map, zone FROM characters WHERE online > 0", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH); diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h index 64665c2b198..a8b688e6b26 100644 --- a/src/server/shared/Networking/AsyncAcceptor.h +++ b/src/server/shared/Networking/AsyncAcceptor.h @@ -23,37 +23,32 @@ 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)); + boost::system::error_code error; + _acceptor.non_blocking(true, error); + } - 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(); + mgrHandler(std::move(this->_socket)); } catch (boost::system::system_error const& err) { @@ -61,13 +56,36 @@ private: } } - // 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..5ed7f25ed77 --- /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..ed638ab89f3 --- /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 (size_t i = 0; i < _threadCount; ++i) + _threads[i].Stop(); + + Wait(); + } + + void Wait() + { + if (_threadCount != 0) + for (size_t i = 0; i < _threadCount; ++i) + _threads[i].Wait(); + } + + virtual void OnSocketOpen(tcp::socket&& sock) + { + size_t min = 0; + + for (size_t 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 diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index b49f9ebd9ec..3fec13684da 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1548,6 +1548,14 @@ GM.Chat = 2 GM.WhisperingTo = 2 # +# GM.FreezeAuraDuration +# Description: Allows to set a default duration to the Freeze Aura +# applied on players when using the .freeze command +# Default: 0 - (Original aura duration. Lasts until the .unfreeze command is used) +# N - (Aura duration if unspecified in .freeze command, in seconds) +GM.FreezeAuraDuration = 0 + +# # GM.InGMList.Level # Description: Maximum GM level shown in GM list (if enabled) in non-GM state (.gm off). # Default: 3 - (Anyone) |