feat(Core): replace ACE network with Boost.Asio (#6574)
This commit is contained in:
parent
7449496bb5
commit
8568c4fb33
64 changed files with 3242 additions and 4712 deletions
|
|
@ -28,4 +28,6 @@ void Acore::Banner::Show(char const* applicationName, void(*log)(char const* tex
|
|||
{
|
||||
logExtraInfo();
|
||||
}
|
||||
|
||||
log(" ");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ target_link_libraries(common
|
|||
PRIVATE
|
||||
acore-core-interface
|
||||
PUBLIC
|
||||
ace
|
||||
boost
|
||||
argon2
|
||||
g3dlib
|
||||
|
|
|
|||
|
|
@ -14,13 +14,12 @@
|
|||
#include <utility>
|
||||
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
#include <ace/config-all.h>
|
||||
#include <ws2tcpip.h>
|
||||
#if AC_COMPILER == AC_COMPILER_INTEL
|
||||
# if !defined(BOOST_ASIO_HAS_MOVE)
|
||||
# define BOOST_ASIO_HAS_MOVE
|
||||
# endif // !defined(BOOST_ASIO_HAS_MOVE)
|
||||
# endif // if WARHEAD_COMPILER == WARHEAD_COMPILER_INTEL
|
||||
# endif // if AC_COMPILER == AC_COMPILER_INTEL
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#include <netdb.h>
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@
|
|||
#define ACORE_BIGENDIAN 1
|
||||
|
||||
#if !defined(ACORE_ENDIAN)
|
||||
# if defined (ACE_BIG_ENDIAN)
|
||||
# if defined (BOOST_BIG_ENDIAN)
|
||||
# define ACORE_ENDIAN ACORE_BIGENDIAN
|
||||
# else //ACE_BYTE_ORDER != ACE_BIG_ENDIAN
|
||||
# else
|
||||
# define ACORE_ENDIAN ACORE_LITTLEENDIAN
|
||||
# endif //ACE_BYTE_ORDER
|
||||
#endif //ACORE_ENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
# define ACORE_PATH_MAX MAX_PATH
|
||||
|
|
|
|||
|
|
@ -12,11 +12,6 @@
|
|||
#include <windows.h>
|
||||
#include <winsvc.h>
|
||||
|
||||
// stupid ACE define
|
||||
#ifdef main
|
||||
#undef main
|
||||
#endif //main
|
||||
|
||||
#if !defined(WINADVAPI)
|
||||
#if !defined(_ADVAPI32_)
|
||||
#define WINADVAPI DECLSPEC_IMPORT
|
||||
|
|
|
|||
154
src/common/Threading/MPSCQueue.h
Normal file
154
src/common/Threading/MPSCQueue.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef MPSCQueue_h__
|
||||
#define MPSCQueue_h__
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
namespace Acore::Impl
|
||||
{
|
||||
// C++ implementation of Dmitry Vyukov's lock free MPSC queue
|
||||
// http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue
|
||||
template<typename T>
|
||||
class MPSCQueueNonIntrusive
|
||||
{
|
||||
public:
|
||||
MPSCQueueNonIntrusive() : _head(new Node()), _tail(_head.load(std::memory_order_relaxed))
|
||||
{
|
||||
Node* front = _head.load(std::memory_order_relaxed);
|
||||
front->Next.store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
~MPSCQueueNonIntrusive()
|
||||
{
|
||||
T* output;
|
||||
while (Dequeue(output))
|
||||
delete output;
|
||||
|
||||
Node* front = _head.load(std::memory_order_relaxed);
|
||||
delete front;
|
||||
}
|
||||
|
||||
void Enqueue(T* input)
|
||||
{
|
||||
Node* node = new Node(input);
|
||||
Node* prevHead = _head.exchange(node, std::memory_order_acq_rel);
|
||||
prevHead->Next.store(node, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool Dequeue(T*& result)
|
||||
{
|
||||
Node* tail = _tail.load(std::memory_order_relaxed);
|
||||
Node* next = tail->Next.load(std::memory_order_acquire);
|
||||
if (!next)
|
||||
return false;
|
||||
|
||||
result = next->Data;
|
||||
_tail.store(next, std::memory_order_release);
|
||||
delete tail;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node
|
||||
{
|
||||
Node() = default;
|
||||
explicit Node(T* data) : Data(data)
|
||||
{
|
||||
Next.store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
T* Data;
|
||||
std::atomic<Node*> Next;
|
||||
};
|
||||
|
||||
std::atomic<Node*> _head;
|
||||
std::atomic<Node*> _tail;
|
||||
|
||||
MPSCQueueNonIntrusive(MPSCQueueNonIntrusive const&) = delete;
|
||||
MPSCQueueNonIntrusive& operator=(MPSCQueueNonIntrusive const&) = delete;
|
||||
};
|
||||
|
||||
// C++ implementation of Dmitry Vyukov's lock free MPSC queue
|
||||
// http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue
|
||||
template<typename T, std::atomic<T*> T::* IntrusiveLink>
|
||||
class MPSCQueueIntrusive
|
||||
{
|
||||
public:
|
||||
MPSCQueueIntrusive() : _dummyPtr(reinterpret_cast<T*>(std::addressof(_dummy))), _head(_dummyPtr), _tail(_dummyPtr)
|
||||
{
|
||||
// _dummy is constructed from aligned_storage and is intentionally left uninitialized (it might not be default constructible)
|
||||
// so we init only its IntrusiveLink here
|
||||
std::atomic<T*>* dummyNext = new (&(_dummyPtr->*IntrusiveLink)) std::atomic<T*>();
|
||||
dummyNext->store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
~MPSCQueueIntrusive()
|
||||
{
|
||||
T* output;
|
||||
while (Dequeue(output))
|
||||
delete output;
|
||||
}
|
||||
|
||||
void Enqueue(T* input)
|
||||
{
|
||||
(input->*IntrusiveLink).store(nullptr, std::memory_order_release);
|
||||
T* prevHead = _head.exchange(input, std::memory_order_acq_rel);
|
||||
(prevHead->*IntrusiveLink).store(input, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool Dequeue(T*& result)
|
||||
{
|
||||
T* tail = _tail.load(std::memory_order_relaxed);
|
||||
T* next = (tail->*IntrusiveLink).load(std::memory_order_acquire);
|
||||
if (tail == _dummyPtr)
|
||||
{
|
||||
if (!next)
|
||||
return false;
|
||||
|
||||
_tail.store(next, std::memory_order_release);
|
||||
tail = next;
|
||||
next = (next->*IntrusiveLink).load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
if (next)
|
||||
{
|
||||
_tail.store(next, std::memory_order_release);
|
||||
result = tail;
|
||||
return true;
|
||||
}
|
||||
|
||||
T* head = _head.load(std::memory_order_acquire);
|
||||
if (tail != head)
|
||||
return false;
|
||||
|
||||
Enqueue(_dummyPtr);
|
||||
next = (tail->*IntrusiveLink).load(std::memory_order_acquire);
|
||||
if (next)
|
||||
{
|
||||
_tail.store(next, std::memory_order_release);
|
||||
result = tail;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::aligned_storage_t<sizeof(T), alignof(T)> _dummy;
|
||||
T* _dummyPtr;
|
||||
std::atomic<T*> _head;
|
||||
std::atomic<T*> _tail;
|
||||
|
||||
MPSCQueueIntrusive(MPSCQueueIntrusive const&) = delete;
|
||||
MPSCQueueIntrusive& operator=(MPSCQueueIntrusive const&) = delete;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T, std::atomic<T*> T::* IntrusiveLink = nullptr>
|
||||
using MPSCQueue = std::conditional_t<IntrusiveLink != nullptr, Acore::Impl::MPSCQueueIntrusive<T, IntrusiveLink>, Acore::Impl::MPSCQueueNonIntrusive<T>>;
|
||||
|
||||
#endif // MPSCQueue_h__
|
||||
|
|
@ -100,7 +100,7 @@ private:
|
|||
typename std::enable_if<std::is_pointer<E>::value>::type DeleteQueuedObject(E& obj) { delete obj; }
|
||||
|
||||
template<typename E = T>
|
||||
typename std::enable_if < !std::is_pointer<E>::value >::type DeleteQueuedObject(E const& /*packet*/) { }
|
||||
typename std::enable_if<!std::is_pointer<E>::value>::type DeleteQueuedObject(E const& /*packet*/) { }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef _PCQ_H
|
||||
#define _PCQ_H
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename T>
|
||||
class ProducerConsumerQueue
|
||||
{
|
||||
private:
|
||||
std::mutex _queueLock;
|
||||
std::queue<T> _queue;
|
||||
std::condition_variable _condition;
|
||||
std::atomic<bool> _shutdown;
|
||||
|
||||
public:
|
||||
|
||||
ProducerConsumerQueue<T>() : _shutdown(false) { }
|
||||
|
||||
void Push(const T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_queueLock);
|
||||
_queue.push(std::move(value));
|
||||
|
||||
_condition.notify_one();
|
||||
}
|
||||
|
||||
bool Empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_queueLock);
|
||||
|
||||
return _queue.empty();
|
||||
}
|
||||
|
||||
bool Pop(T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_queueLock);
|
||||
|
||||
if (_queue.empty() || _shutdown)
|
||||
return false;
|
||||
|
||||
value = _queue.front();
|
||||
|
||||
_queue.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaitAndPop(T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_queueLock);
|
||||
|
||||
// we could be using .wait(lock, predicate) overload here but it is broken
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/1098841
|
||||
while (_queue.empty() && !_shutdown)
|
||||
_condition.wait(lock);
|
||||
|
||||
if (_queue.empty() || _shutdown)
|
||||
return;
|
||||
|
||||
value = _queue.front();
|
||||
|
||||
_queue.pop();
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_queueLock);
|
||||
|
||||
while (!_queue.empty())
|
||||
{
|
||||
T& value = _queue.front();
|
||||
|
||||
DeleteQueuedObject(value);
|
||||
|
||||
_queue.pop();
|
||||
}
|
||||
|
||||
_shutdown = true;
|
||||
|
||||
_condition.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename E = T>
|
||||
typename std::enable_if<std::is_pointer<E>::value>::type DeleteQueuedObject(E& obj) { delete obj; }
|
||||
|
||||
template<typename E = T>
|
||||
typename std::enable_if<!std::is_pointer<E>::value>::type DeleteQueuedObject(E const& /*packet*/) { }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -7,9 +7,9 @@
|
|||
#include "Util.h"
|
||||
#include "Common.h"
|
||||
#include "Containers.h"
|
||||
#include "IpAddress.h"
|
||||
#include "StringConvert.h"
|
||||
#include "StringFormat.h"
|
||||
#include <ace/Default_Constants.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdarg>
|
||||
|
|
@ -17,8 +17,10 @@
|
|||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <cstdarg>
|
||||
#include <ctime>
|
||||
#include <utf8.h>
|
||||
// #include "IpAddress.h"
|
||||
|
||||
Tokenizer::Tokenizer(const std::string& src, const char sep, uint32 vectorReserve)
|
||||
{
|
||||
|
|
@ -295,26 +297,9 @@ bool IsIPAddress(char const* ipaddress)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Let the big boys do it.
|
||||
// Drawback: all valid ip address formats are recognized e.g.: 12.23, 121234, 0xABCD)
|
||||
return inet_addr(ipaddress) != INADDR_NONE;
|
||||
}
|
||||
|
||||
std::string GetAddressString(ACE_INET_Addr const& addr)
|
||||
{
|
||||
char buf[ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16];
|
||||
addr.addr_to_string(buf, ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16);
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool IsIPAddrInNetwork(ACE_INET_Addr const& net, ACE_INET_Addr const& addr, ACE_INET_Addr const& subnetMask)
|
||||
{
|
||||
uint32 mask = subnetMask.get_ip_address();
|
||||
if ((net.get_ip_address() & mask) == (addr.get_ip_address() & mask))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
boost::system::error_code error;
|
||||
Acore::Net::make_address(ipaddress, error);
|
||||
return !error;
|
||||
}
|
||||
|
||||
/// create PID file
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include "Containers.h"
|
||||
#include "Define.h"
|
||||
#include "Errors.h"
|
||||
#include <ace/INET_Addr.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
|
|
@ -18,6 +17,9 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
// Searcher for map of structs
|
||||
template<typename T, class S> struct Finder
|
||||
|
|
@ -383,12 +385,6 @@ bool Utf8ToUpperOnlyLatin(std::string& utf8String);
|
|||
|
||||
bool IsIPAddress(char const* ipaddress);
|
||||
|
||||
/// Checks if address belongs to the a network with specified submask
|
||||
bool IsIPAddrInNetwork(ACE_INET_Addr const& net, ACE_INET_Addr const& addr, ACE_INET_Addr const& subnetMask);
|
||||
|
||||
/// Transforms ACE_INET_Addr address into string format "dotted_ip:port"
|
||||
std::string GetAddressString(ACE_INET_Addr const& addr);
|
||||
|
||||
uint32 CreatePIDFile(const std::string& filename);
|
||||
uint32 GetPID();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,28 +13,26 @@
|
|||
*/
|
||||
|
||||
#include "AppenderDB.h"
|
||||
#include "AuthSocketMgr.h"
|
||||
#include "Banner.h"
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "DeadlineTimer.h"
|
||||
#include "GitRevision.h"
|
||||
#include "IPLocation.h"
|
||||
#include "Log.h"
|
||||
#include "RealmAcceptor.h"
|
||||
#include "RealmList.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "IoContext.h"
|
||||
#include "MySQLThreading.h"
|
||||
#include "ProcessPriority.h"
|
||||
#include "RealmList.h"
|
||||
#include "SecretMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SignalHandler.h"
|
||||
#include "Util.h"
|
||||
#include "ProcessPriority.h"
|
||||
#include <ace/ACE.h>
|
||||
#include <ace/Dev_Poll_Reactor.h>
|
||||
#include <ace/Sig_Handler.h>
|
||||
#include <ace/TP_Reactor.h>
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <csignal>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
|
|
@ -42,23 +40,26 @@
|
|||
#define _ACORE_REALM_CONFIG "authserver.conf"
|
||||
#endif
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
bool StartDB();
|
||||
void StopDB();
|
||||
|
||||
bool stopEvent = false; // Setting it to true stops the server
|
||||
void SignalHandler(std::weak_ptr<Acore::Asio::IoContext> ioContextRef, boost::system::error_code const& error, int signalNumber);
|
||||
void KeepDatabaseAliveHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error);
|
||||
void BanExpiryHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error);
|
||||
|
||||
/// Print out the usage string for this program on the console.
|
||||
void usage(const char* prog)
|
||||
{
|
||||
LOG_INFO("server.authserver", "Usage: \n %s [<options>]\n"
|
||||
" -c config_file use config_file as configuration file\n\r",
|
||||
prog);
|
||||
" -c config_file use config_file as configuration file\n\r", prog);
|
||||
}
|
||||
|
||||
/// Launch the auth server
|
||||
extern int main(int argc, char** argv)
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Acore::Impl::CurrentServerProcessHolder::_type = SERVER_PROCESS_AUTHSERVER;
|
||||
signal(SIGABRT, &Acore::AbortHandler);
|
||||
|
||||
// Command line parsing to get the configuration file name
|
||||
std::string configFile = sConfigMgr->GetConfigPath() + std::string(_ACORE_REALM_CONFIG);
|
||||
|
|
@ -99,18 +100,9 @@ extern int main(int argc, char** argv)
|
|||
LOG_INFO("server.authserver", "> Using configuration file %s.", sConfigMgr->GetFilename().c_str());
|
||||
LOG_INFO("server.authserver", "> Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||
LOG_INFO("server.authserver", "> Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
|
||||
LOG_INFO("server.authserver", "> Using ACE version: %s\n", ACE_VERSION);
|
||||
}
|
||||
);
|
||||
|
||||
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
|
||||
ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true);
|
||||
#else
|
||||
ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true);
|
||||
#endif
|
||||
|
||||
LOG_INFO("server.authserver", "Max allowed open files is %d", ACE::max_handles());
|
||||
|
||||
// authserver PID file creation
|
||||
std::string pidFile = sConfigMgr->GetOption<std::string>("PidFile", "");
|
||||
if (!pidFile.empty())
|
||||
|
|
@ -133,75 +125,70 @@ extern int main(int argc, char** argv)
|
|||
// Load IP Location Database
|
||||
sIPLocation->Load();
|
||||
|
||||
std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
|
||||
|
||||
std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
|
||||
|
||||
// Get the list of realms for the server
|
||||
sRealmList->Initialize(sConfigMgr->GetOption<int32>("RealmsStateUpdateDelay", 20));
|
||||
sRealmList->Initialize(*ioContext, sConfigMgr->GetOption<int32>("RealmsStateUpdateDelay", 20));
|
||||
|
||||
std::shared_ptr<void> sRealmListHandle(nullptr, [](void*) { sRealmList->Close(); });
|
||||
|
||||
if (sRealmList->GetRealms().empty())
|
||||
{
|
||||
LOG_ERROR("server.authserver", "No valid realms specified.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Launch the listening network socket
|
||||
RealmAcceptor acceptor;
|
||||
|
||||
int32 rmport = sConfigMgr->GetOption<int32>("RealmServerPort", 3724);
|
||||
if (rmport < 0 || rmport > 0xFFFF)
|
||||
// Start the listening port (acceptor) for auth connections
|
||||
int32 port = sConfigMgr->GetOption<int32>("RealmServerPort", 3724);
|
||||
if (port < 0 || port > 0xFFFF)
|
||||
{
|
||||
LOG_ERROR("server.authserver", "The specified RealmServerPort (%d) is out of the allowed range (1-65535)", rmport);
|
||||
LOG_ERROR("server.authserver", "Specified port out of allowed range (1-65535)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string bind_ip = sConfigMgr->GetOption<std::string>("BindIP", "0.0.0.0");
|
||||
std::string bindIp = sConfigMgr->GetOption<std::string>("BindIP", "0.0.0.0");
|
||||
|
||||
ACE_INET_Addr bind_addr(uint16(rmport), bind_ip.c_str());
|
||||
|
||||
if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1)
|
||||
if (!sAuthSocketMgr.StartNetwork(*ioContext, bindIp, port))
|
||||
{
|
||||
LOG_ERROR("server.authserver", "Auth server can not bind to %s:%d (possible error: port already in use)", bind_ip.c_str(), rmport);
|
||||
LOG_ERROR("server.authserver", "Failed to initialize network");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG_INFO("server.authserver", "Authserver listening to %s:%d", bind_ip.c_str(), rmport);
|
||||
std::shared_ptr<void> sAuthSocketMgrHandle(nullptr, [](void*) { sAuthSocketMgr.StopNetwork(); });
|
||||
|
||||
// Initialize the signal handlers
|
||||
Acore::SignalHandler signalHandler;
|
||||
auto const _handler = [](int) { stopEvent = true; };
|
||||
|
||||
// Register authservers's signal handlers
|
||||
signalHandler.handle_signal(SIGINT, _handler);
|
||||
signalHandler.handle_signal(SIGTERM, _handler);
|
||||
// Set signal handlers
|
||||
boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM);
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
signalHandler.handle_signal(SIGBREAK, _handler);
|
||||
signals.add(SIGBREAK);
|
||||
#endif
|
||||
signals.async_wait(std::bind(&SignalHandler, std::weak_ptr<Acore::Asio::IoContext>(ioContext), std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
// Set process priority according to configuration settings
|
||||
SetProcessPriority("server.authserver", sConfigMgr->GetOption<int32>(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetOption<bool>(CONFIG_HIGH_PRIORITY, false));
|
||||
|
||||
// maximum counter for next ping
|
||||
uint32 numLoops = (sConfigMgr->GetOption<int32>("MaxPingTime", 30) * (MINUTE * 1000000 / 100000));
|
||||
uint32 loopCounter = 0;
|
||||
// Enabled a timed callback for handling the database keep alive ping
|
||||
int32 dbPingInterval = sConfigMgr->GetOption<int32>("MaxPingTime", 30);
|
||||
std::shared_ptr<Acore::Asio::DeadlineTimer> dbPingTimer = std::make_shared<Acore::Asio::DeadlineTimer>(*ioContext);
|
||||
dbPingTimer->expires_from_now(boost::posix_time::minutes(dbPingInterval));
|
||||
dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, std::weak_ptr<Acore::Asio::DeadlineTimer>(dbPingTimer), dbPingInterval, std::placeholders::_1));
|
||||
|
||||
// Wait for termination signal
|
||||
while (!stopEvent)
|
||||
{
|
||||
// dont move this outside the loop, the reactor will modify it
|
||||
ACE_Time_Value interval(0, 100000);
|
||||
int32 banExpiryCheckInterval = sConfigMgr->GetOption<int32>("BanExpiryCheckInterval", 60);
|
||||
std::shared_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimer = std::make_shared<Acore::Asio::DeadlineTimer>(*ioContext);
|
||||
banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval));
|
||||
banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, std::weak_ptr<Acore::Asio::DeadlineTimer>(banExpiryCheckTimer), banExpiryCheckInterval, std::placeholders::_1));
|
||||
|
||||
if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1)
|
||||
break;
|
||||
// Start the io service worker loop
|
||||
ioContext->run();
|
||||
|
||||
if ((++loopCounter) == numLoops)
|
||||
{
|
||||
loopCounter = 0;
|
||||
LOG_INFO("server.authserver", "Ping MySQL to keep connection alive");
|
||||
LoginDatabase.KeepAlive();
|
||||
}
|
||||
}
|
||||
|
||||
// Close the Database Pool and library
|
||||
StopDB();
|
||||
banExpiryCheckTimer->cancel();
|
||||
dbPingTimer->cancel();
|
||||
|
||||
LOG_INFO("server.authserver", "Halting process...");
|
||||
|
||||
signals.cancel();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -231,3 +218,44 @@ void StopDB()
|
|||
LoginDatabase.Close();
|
||||
MySQL::Library_End();
|
||||
}
|
||||
|
||||
void SignalHandler(std::weak_ptr<Acore::Asio::IoContext> ioContextRef, boost::system::error_code const& error, int /*signalNumber*/)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
if (std::shared_ptr<Acore::Asio::IoContext> ioContext = ioContextRef.lock())
|
||||
{
|
||||
ioContext->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeepDatabaseAliveHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
if (std::shared_ptr<Acore::Asio::DeadlineTimer> dbPingTimer = dbPingTimerRef.lock())
|
||||
{
|
||||
LOG_INFO("server.authserver", "Ping MySQL to keep connection alive");
|
||||
LoginDatabase.KeepAlive();
|
||||
|
||||
dbPingTimer->expires_from_now(boost::posix_time::minutes(dbPingInterval));
|
||||
dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, dbPingTimerRef, dbPingInterval, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BanExpiryHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
if (std::shared_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimer = banExpiryCheckTimerRef.lock())
|
||||
{
|
||||
LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
|
||||
LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
|
||||
|
||||
banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval));
|
||||
banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, banExpiryCheckTimerRef, banExpiryCheckInterval, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include "Configuration/Config.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Config.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "RealmList.h"
|
||||
#include "RealmSocket.h"
|
||||
#include "Common.h"
|
||||
|
|
|
|||
866
src/server/authserver/Server/AuthSession.cpp
Normal file
866
src/server/authserver/Server/AuthSession.cpp
Normal file
|
|
@ -0,0 +1,866 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#include "AuthSession.h"
|
||||
#include "AES.h"
|
||||
#include "AuthCodes.h"
|
||||
#include "Config.h"
|
||||
#include "CryptoGenerics.h"
|
||||
#include "CryptoRandom.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Errors.h"
|
||||
#include "CryptoHash.h"
|
||||
#include "IPLocation.h"
|
||||
#include "Log.h"
|
||||
#include "RealmList.h"
|
||||
#include "SecretMgr.h"
|
||||
#include "Timer.h"
|
||||
#include "TOTP.h"
|
||||
#include "Util.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
enum eAuthCmd
|
||||
{
|
||||
AUTH_LOGON_CHALLENGE = 0x00,
|
||||
AUTH_LOGON_PROOF = 0x01,
|
||||
AUTH_RECONNECT_CHALLENGE = 0x02,
|
||||
AUTH_RECONNECT_PROOF = 0x03,
|
||||
REALM_LIST = 0x10,
|
||||
XFER_INITIATE = 0x30,
|
||||
XFER_DATA = 0x31,
|
||||
XFER_ACCEPT = 0x32,
|
||||
XFER_RESUME = 0x33,
|
||||
XFER_CANCEL = 0x34
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct AUTH_LOGON_CHALLENGE_C
|
||||
{
|
||||
uint8 cmd;
|
||||
uint8 error;
|
||||
uint16 size;
|
||||
uint8 gamename[4];
|
||||
uint8 version1;
|
||||
uint8 version2;
|
||||
uint8 version3;
|
||||
uint16 build;
|
||||
uint8 platform[4];
|
||||
uint8 os[4];
|
||||
uint8 country[4];
|
||||
uint32 timezone_bias;
|
||||
uint32 ip;
|
||||
uint8 I_len;
|
||||
uint8 I[1];
|
||||
} sAuthLogonChallenge_C;
|
||||
static_assert(sizeof(sAuthLogonChallenge_C) == (1 + 1 + 2 + 4 + 1 + 1 + 1 + 2 + 4 + 4 + 4 + 4 + 4 + 1 + 1));
|
||||
|
||||
typedef struct AUTH_LOGON_PROOF_C
|
||||
{
|
||||
uint8 cmd;
|
||||
Acore::Crypto::SRP6::EphemeralKey A;
|
||||
Acore::Crypto::SHA1::Digest clientM;
|
||||
Acore::Crypto::SHA1::Digest crc_hash;
|
||||
uint8 number_of_keys;
|
||||
uint8 securityFlags;
|
||||
} sAuthLogonProof_C;
|
||||
static_assert(sizeof(sAuthLogonProof_C) == (1 + 32 + 20 + 20 + 1 + 1));
|
||||
|
||||
typedef struct AUTH_LOGON_PROOF_S
|
||||
{
|
||||
uint8 cmd;
|
||||
uint8 error;
|
||||
Acore::Crypto::SHA1::Digest M2;
|
||||
uint32 AccountFlags;
|
||||
uint32 SurveyId;
|
||||
uint16 LoginFlags;
|
||||
} sAuthLogonProof_S;
|
||||
static_assert(sizeof(sAuthLogonProof_S) == (1 + 1 + 20 + 4 + 4 + 2));
|
||||
|
||||
typedef struct AUTH_LOGON_PROOF_S_OLD
|
||||
{
|
||||
uint8 cmd;
|
||||
uint8 error;
|
||||
Acore::Crypto::SHA1::Digest M2;
|
||||
uint32 unk2;
|
||||
} sAuthLogonProof_S_Old;
|
||||
static_assert(sizeof(sAuthLogonProof_S_Old) == (1 + 1 + 20 + 4));
|
||||
|
||||
typedef struct AUTH_RECONNECT_PROOF_C
|
||||
{
|
||||
uint8 cmd;
|
||||
uint8 R1[16];
|
||||
Acore::Crypto::SHA1::Digest R2, R3;
|
||||
uint8 number_of_keys;
|
||||
} sAuthReconnectProof_C;
|
||||
static_assert(sizeof(sAuthReconnectProof_C) == (1 + 16 + 20 + 20 + 1));
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 } };
|
||||
|
||||
#define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 16)
|
||||
|
||||
#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
|
||||
#define REALM_LIST_PACKET_SIZE 5
|
||||
|
||||
std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers()
|
||||
{
|
||||
std::unordered_map<uint8, AuthHandler> handlers;
|
||||
|
||||
handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge };
|
||||
handlers[AUTH_LOGON_PROOF] = { STATUS_LOGON_PROOF, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof };
|
||||
handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge };
|
||||
handlers[AUTH_RECONNECT_PROOF] = { STATUS_RECONNECT_PROOF, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof };
|
||||
handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList };
|
||||
|
||||
return handlers;
|
||||
}
|
||||
|
||||
std::unordered_map<uint8, AuthHandler> const Handlers = AuthSession::InitHandlers();
|
||||
|
||||
void AccountInfo::LoadResult(Field* fields)
|
||||
{
|
||||
// 0 1 2 3 4 5
|
||||
// SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins,
|
||||
// 6 7
|
||||
// ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate,
|
||||
// 8 9
|
||||
// ipb.unbandate > UNIX_TIMESTAMP() OR ipb.unbandate = ipb.bandate, ipb.unbandate = ipb.bandate,
|
||||
// 10
|
||||
// aa.gmlevel (, more query-specific fields)
|
||||
// FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 LEFT JOIN ip_banned ipb ON ipb.ip = ? WHERE a.username = ?
|
||||
|
||||
Id = fields[0].GetUInt32();
|
||||
Login = fields[1].GetString();
|
||||
IsLockedToIP = fields[2].GetBool();
|
||||
LockCountry = fields[3].GetString();
|
||||
LastIP = fields[4].GetString();
|
||||
FailedLogins = fields[5].GetUInt32();
|
||||
IsBanned = fields[6].GetBool() || fields[8].GetBool();
|
||||
IsPermanentlyBanned = fields[7].GetBool() || fields[9].GetBool();
|
||||
SecurityLevel = static_cast<AccountTypes>(fields[10].GetUInt8()) > SEC_CONSOLE ? SEC_CONSOLE : static_cast<AccountTypes>(fields[10].GetUInt8());
|
||||
|
||||
// Use our own uppercasing of the account name instead of using UPPER() in mysql query
|
||||
// This is how the account was created in the first place and changing it now would result in breaking
|
||||
// login for all accounts having accented characters in their name
|
||||
Utf8ToUpperOnlyLatin(Login);
|
||||
}
|
||||
|
||||
AuthSession::AuthSession(tcp::socket&& socket) :
|
||||
Socket(std::move(socket)), _status(STATUS_CHALLENGE), _build(0), _expversion(0) { }
|
||||
|
||||
void AuthSession::Start()
|
||||
{
|
||||
std::string ip_address = GetRemoteIpAddress().to_string();
|
||||
LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str());
|
||||
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);
|
||||
stmt->setString(0, ip_address);
|
||||
|
||||
_queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1)));
|
||||
}
|
||||
|
||||
bool AuthSession::Update()
|
||||
{
|
||||
if (!AuthSocket::Update())
|
||||
return false;
|
||||
|
||||
_queryProcessor.ProcessReadyCallbacks();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuthSession::CheckIpCallback(PreparedQueryResult result)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
bool banned = false;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
if (fields[0].GetUInt64() != 0)
|
||||
banned = true;
|
||||
|
||||
} while (result->NextRow());
|
||||
|
||||
if (banned)
|
||||
{
|
||||
ByteBuffer pkt;
|
||||
pkt << uint8(AUTH_LOGON_CHALLENGE);
|
||||
pkt << uint8(0x00);
|
||||
pkt << uint8(WOW_FAIL_BANNED);
|
||||
SendPacket(pkt);
|
||||
LOG_DEBUG("session", "[AuthSession::CheckIpCallback] Banned ip '%s:%d' tries to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncRead();
|
||||
}
|
||||
|
||||
void AuthSession::ReadHandler()
|
||||
{
|
||||
MessageBuffer& packet = GetReadBuffer();
|
||||
|
||||
while (packet.GetActiveSize())
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (_status != itr->second.status)
|
||||
{
|
||||
CloseSocket();
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 size = uint16(itr->second.packetSize);
|
||||
if (packet.GetActiveSize() < size)
|
||||
break;
|
||||
|
||||
if (cmd == AUTH_LOGON_CHALLENGE || cmd == AUTH_RECONNECT_CHALLENGE)
|
||||
{
|
||||
sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(packet.GetReadPointer());
|
||||
size += challenge->size;
|
||||
if (size > MAX_ACCEPTED_CHALLENGE_SIZE)
|
||||
{
|
||||
CloseSocket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.GetActiveSize() < size)
|
||||
break;
|
||||
|
||||
if (!(*this.*itr->second.handler)())
|
||||
{
|
||||
CloseSocket();
|
||||
return;
|
||||
}
|
||||
|
||||
packet.ReadCompleted(size);
|
||||
}
|
||||
|
||||
AsyncRead();
|
||||
}
|
||||
|
||||
void AuthSession::SendPacket(ByteBuffer& packet)
|
||||
{
|
||||
if (!IsOpen())
|
||||
return;
|
||||
|
||||
if (!packet.empty())
|
||||
{
|
||||
MessageBuffer buffer(packet.size());
|
||||
buffer.Write(packet.contents(), packet.size());
|
||||
QueuePacket(std::move(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthSession::HandleLogonChallenge()
|
||||
{
|
||||
_status = STATUS_CLOSED;
|
||||
|
||||
sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
|
||||
if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
|
||||
return false;
|
||||
|
||||
std::string login((char const*)challenge->I, challenge->I_len);
|
||||
LOG_DEBUG("server.authserver", "[AuthChallenge] '%s'", login.c_str());
|
||||
|
||||
_build = challenge->build;
|
||||
_expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
|
||||
std::array<char, 5> os;
|
||||
os.fill('\0');
|
||||
memcpy(os.data(), challenge->os, sizeof(challenge->os));
|
||||
_os = os.data();
|
||||
|
||||
// Restore string order as its byte order is reversed
|
||||
std::reverse(_os.begin(), _os.end());
|
||||
|
||||
_localizationName.resize(4);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
_localizationName[i] = challenge->country[4 - i - 1];
|
||||
|
||||
// Get the account details from the account table
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE);
|
||||
stmt->setString(0, GetRemoteIpAddress().to_string());
|
||||
stmt->setString(1, login);
|
||||
|
||||
_queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuthSession::LogonChallengeCallback(PreparedQueryResult result)
|
||||
{
|
||||
ByteBuffer pkt;
|
||||
pkt << uint8(AUTH_LOGON_CHALLENGE);
|
||||
pkt << uint8(0x00);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
|
||||
SendPacket(pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
_accountInfo.LoadResult(fields);
|
||||
|
||||
std::string ipAddress = GetRemoteIpAddress().to_string();
|
||||
uint16 port = GetRemotePort();
|
||||
|
||||
// If the IP is 'locked', check that the player comes indeed from the correct IP address
|
||||
if (_accountInfo.IsLockedToIP)
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountInfo.Login.c_str(), _accountInfo.LastIP.c_str(), ipAddress.c_str());
|
||||
if (_accountInfo.LastIP != ipAddress)
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_LOCKED_ENFORCED);
|
||||
SendPacket(pkt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(ipAddress))
|
||||
_ipCountry = location->CountryCode;
|
||||
|
||||
LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _accountInfo.Login.c_str());
|
||||
if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00")
|
||||
LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _accountInfo.Login.c_str());
|
||||
else if (!_ipCountry.empty())
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _accountInfo.Login.c_str(), _accountInfo.LockCountry.c_str(), _ipCountry.c_str());
|
||||
if (_ipCountry != _accountInfo.LockCountry)
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK);
|
||||
SendPacket(pkt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the account is banned, reject the logon attempt
|
||||
if (_accountInfo.IsBanned)
|
||||
{
|
||||
if (_accountInfo.IsPermanentlyBanned)
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_BANNED);
|
||||
SendPacket(pkt);
|
||||
LOG_INFO("server.authserver.banned", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_SUSPENDED);
|
||||
SendPacket(pkt);
|
||||
LOG_INFO("server.authserver.banned", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 securityFlags = 0;
|
||||
|
||||
// Check if a TOTP token is needed
|
||||
if (!fields[11].IsNull())
|
||||
{
|
||||
securityFlags = 4;
|
||||
_totpSecret = fields[11].GetBinary();
|
||||
|
||||
if (auto const& secret = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY))
|
||||
{
|
||||
bool success = Acore::Crypto::AEDecrypt<Acore::Crypto::AES>(*_totpSecret, *secret);
|
||||
if (!success)
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_DB_BUSY);
|
||||
LOG_ERROR("server.authserver", "[AuthChallenge] Account '%s' has invalid ciphertext for TOTP token key stored", _accountInfo.Login.c_str());
|
||||
SendPacket(pkt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_srp6.emplace(
|
||||
_accountInfo.Login,
|
||||
fields[12].GetBinary<Acore::Crypto::SRP6::SALT_LENGTH>(),
|
||||
fields[13].GetBinary<Acore::Crypto::SRP6::VERIFIER_LENGTH>());
|
||||
|
||||
// Fill the response packet with the result
|
||||
if (AuthHelper::IsAcceptedClientBuild(_build))
|
||||
{
|
||||
pkt << uint8(WOW_SUCCESS);
|
||||
|
||||
pkt.append(_srp6->B);
|
||||
pkt << uint8(1);
|
||||
pkt.append(_srp6->g);
|
||||
pkt << uint8(32);
|
||||
pkt.append(_srp6->N);
|
||||
pkt.append(_srp6->s);
|
||||
pkt.append(VersionChallenge.data(), VersionChallenge.size());
|
||||
pkt << uint8(securityFlags); // security flags (0x0...0x04)
|
||||
|
||||
if (securityFlags & 0x01) // PIN input
|
||||
{
|
||||
pkt << uint32(0);
|
||||
pkt << uint64(0) << uint64(0); // 16 bytes hash?
|
||||
}
|
||||
|
||||
if (securityFlags & 0x02) // Matrix input
|
||||
{
|
||||
pkt << uint8(0);
|
||||
pkt << uint8(0);
|
||||
pkt << uint8(0);
|
||||
pkt << uint8(0);
|
||||
pkt << uint64(0);
|
||||
}
|
||||
|
||||
if (securityFlags & 0x04) // Security token input
|
||||
pkt << uint8(1);
|
||||
|
||||
LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%s' locale (%u)",
|
||||
ipAddress.c_str(), port, _accountInfo.Login.c_str(), _localizationName.c_str(), GetLocaleByName(_localizationName));
|
||||
|
||||
_status = STATUS_LOGON_PROOF;
|
||||
}
|
||||
else
|
||||
pkt << uint8(WOW_FAIL_VERSION_INVALID);
|
||||
|
||||
SendPacket(pkt);
|
||||
}
|
||||
|
||||
// Logon Proof command handler
|
||||
bool AuthSession::HandleLogonProof()
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "Entering _HandleLogonProof");
|
||||
_status = STATUS_CLOSED;
|
||||
|
||||
// Read the packet
|
||||
sAuthLogonProof_C* logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetReadBuffer().GetReadPointer());
|
||||
|
||||
// If the client has no valid version
|
||||
if (_expversion == NO_VALID_EXP_FLAG)
|
||||
{
|
||||
// Check if we have the appropriate patch on the disk
|
||||
LOG_DEBUG("network", "Client with invalid version, patching is not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if SRP6 results match (password is correct), else send an error
|
||||
if (Optional<SessionKey> K = _srp6->VerifyChallengeResponse(logonProof->A, logonProof->clientM))
|
||||
{
|
||||
_sessionKey = *K;
|
||||
// Check auth token
|
||||
bool tokenSuccess = false;
|
||||
bool sentToken = (logonProof->securityFlags & 0x04);
|
||||
if (sentToken && _totpSecret)
|
||||
{
|
||||
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 incomingToken = atoi(token.c_str());
|
||||
tokenSuccess = Acore::Crypto::TOTP::ValidateToken(*_totpSecret, incomingToken);
|
||||
memset(_totpSecret->data(), 0, _totpSecret->size());
|
||||
}
|
||||
else if (!sentToken && !_totpSecret)
|
||||
tokenSuccess = true;
|
||||
|
||||
if (!tokenSuccess)
|
||||
{
|
||||
ByteBuffer packet;
|
||||
packet << uint8(AUTH_LOGON_PROOF);
|
||||
packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
|
||||
packet << uint16(0); // LoginFlags, 1 has account message
|
||||
SendPacket(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!VerifyVersion(logonProof->A.data(), logonProof->A.size(), logonProof->crc_hash, false))
|
||||
{
|
||||
ByteBuffer packet;
|
||||
packet << uint8(AUTH_LOGON_PROOF);
|
||||
packet << uint8(WOW_FAIL_VERSION_INVALID);
|
||||
SendPacket(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str());
|
||||
|
||||
// Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
|
||||
// No SQL injection (escaped user name) and IP address as received by socket
|
||||
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
|
||||
stmt->setBinary(0, _sessionKey);
|
||||
stmt->setString(1, GetRemoteIpAddress().to_string());
|
||||
stmt->setUInt32(2, GetLocaleByName(_localizationName));
|
||||
stmt->setString(3, _os);
|
||||
stmt->setString(4, _accountInfo.Login);
|
||||
LoginDatabase.DirectExecute(stmt);
|
||||
|
||||
// Finish SRP6 and send the final result to the client
|
||||
Acore::Crypto::SHA1::Digest M2 = Acore::Crypto::SRP6::GetSessionVerifier(logonProof->A, logonProof->clientM, _sessionKey);
|
||||
|
||||
ByteBuffer packet;
|
||||
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
|
||||
{
|
||||
sAuthLogonProof_S proof;
|
||||
proof.M2 = M2;
|
||||
proof.cmd = AUTH_LOGON_PROOF;
|
||||
proof.error = 0;
|
||||
proof.AccountFlags = 0x00800000; // 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament)
|
||||
proof.SurveyId = 0;
|
||||
proof.LoginFlags = 0; // 0x1 = has account message
|
||||
|
||||
packet.resize(sizeof(proof));
|
||||
std::memcpy(packet.contents(), &proof, sizeof(proof));
|
||||
}
|
||||
else
|
||||
{
|
||||
sAuthLogonProof_S_Old proof;
|
||||
proof.M2 = M2;
|
||||
proof.cmd = AUTH_LOGON_PROOF;
|
||||
proof.error = 0;
|
||||
proof.unk2 = 0x00;
|
||||
|
||||
packet.resize(sizeof(proof));
|
||||
std::memcpy(packet.contents(), &proof, sizeof(proof));
|
||||
}
|
||||
|
||||
SendPacket(packet);
|
||||
_status = STATUS_AUTHED;
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteBuffer packet;
|
||||
packet << uint8(AUTH_LOGON_PROOF);
|
||||
packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
|
||||
packet << uint16(0); // LoginFlags, 1 has account message
|
||||
SendPacket(packet);
|
||||
|
||||
LOG_INFO("server.authserver.hack", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!",
|
||||
GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str());
|
||||
|
||||
uint32 MaxWrongPassCount = sConfigMgr->GetOption<int32>("WrongPass.MaxCount", 0);
|
||||
|
||||
// We can not include the failed account login hook. However, this is a workaround to still log this.
|
||||
if (sConfigMgr->GetOption<bool>("WrongPass.Logging", false))
|
||||
{
|
||||
LoginDatabasePreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING);
|
||||
logstmt->setUInt32(0, _accountInfo.Id);
|
||||
logstmt->setString(1, GetRemoteIpAddress().to_string());
|
||||
logstmt->setString(2, "Login to WoW Failed - Incorrect Password");
|
||||
|
||||
LoginDatabase.Execute(logstmt);
|
||||
}
|
||||
|
||||
if (MaxWrongPassCount > 0)
|
||||
{
|
||||
//Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
|
||||
stmt->setString(0, _accountInfo.Login);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
if (++_accountInfo.FailedLogins >= MaxWrongPassCount)
|
||||
{
|
||||
uint32 WrongPassBanTime = sConfigMgr->GetOption<int32>("WrongPass.BanTime", 600);
|
||||
bool WrongPassBanType = sConfigMgr->GetOption<bool>("WrongPass.BanType", false);
|
||||
|
||||
if (WrongPassBanType)
|
||||
{
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED);
|
||||
stmt->setUInt32(0, _accountInfo.Id);
|
||||
stmt->setUInt32(1, WrongPassBanTime);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
|
||||
GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str(), WrongPassBanTime, _accountInfo.FailedLogins);
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
|
||||
stmt->setString(0, GetRemoteIpAddress().to_string());
|
||||
stmt->setUInt32(1, WrongPassBanTime);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP got banned for '%u' seconds because account %s failed to authenticate '%u' times",
|
||||
GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), WrongPassBanTime, _accountInfo.Login.c_str(), _accountInfo.FailedLogins);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuthSession::HandleReconnectChallenge()
|
||||
{
|
||||
_status = STATUS_CLOSED;
|
||||
|
||||
sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
|
||||
if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
|
||||
return false;
|
||||
|
||||
std::string login((char const*)challenge->I, challenge->I_len);
|
||||
LOG_DEBUG("server.authserver", "[ReconnectChallenge] '%s'", login.c_str());
|
||||
|
||||
_build = challenge->build;
|
||||
_expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
|
||||
|
||||
std::array<char, 5> os;
|
||||
os.fill('\0');
|
||||
memcpy(os.data(), challenge->os, sizeof(challenge->os));
|
||||
_os = os.data();
|
||||
|
||||
// Restore string order as its byte order is reversed
|
||||
std::reverse(_os.begin(), _os.end());
|
||||
|
||||
_localizationName.resize(4);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
_localizationName[i] = challenge->country[4 - i - 1];
|
||||
|
||||
// Get the account details from the account table
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE);
|
||||
stmt->setString(0, GetRemoteIpAddress().to_string());
|
||||
stmt->setString(1, login);
|
||||
|
||||
_queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result)
|
||||
{
|
||||
ByteBuffer pkt;
|
||||
pkt << uint8(AUTH_RECONNECT_CHALLENGE);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
|
||||
SendPacket(pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
_accountInfo.LoadResult(fields);
|
||||
_sessionKey = fields[11].GetBinary<SESSION_KEY_LENGTH>();
|
||||
Acore::Crypto::GetRandomBytes(_reconnectProof);
|
||||
_status = STATUS_RECONNECT_PROOF;
|
||||
|
||||
pkt << uint8(WOW_SUCCESS);
|
||||
pkt.append(_reconnectProof);
|
||||
pkt.append(VersionChallenge.data(), VersionChallenge.size());
|
||||
|
||||
SendPacket(pkt);
|
||||
}
|
||||
|
||||
bool AuthSession::HandleReconnectProof()
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof");
|
||||
_status = STATUS_CLOSED;
|
||||
|
||||
sAuthReconnectProof_C* reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetReadBuffer().GetReadPointer());
|
||||
|
||||
if (_accountInfo.Login.empty())
|
||||
return false;
|
||||
|
||||
BigNumber t1;
|
||||
t1.SetBinary(reconnectProof->R1, 16);
|
||||
|
||||
Acore::Crypto::SHA1 sha;
|
||||
sha.UpdateData(_accountInfo.Login);
|
||||
sha.UpdateData(t1.ToByteArray<16>());
|
||||
sha.UpdateData(_reconnectProof);
|
||||
sha.UpdateData(_sessionKey);
|
||||
sha.Finalize();
|
||||
|
||||
if (sha.GetDigest() == reconnectProof->R2)
|
||||
{
|
||||
if (!VerifyVersion(reconnectProof->R1, sizeof(reconnectProof->R1), reconnectProof->R3, true))
|
||||
{
|
||||
ByteBuffer packet;
|
||||
packet << uint8(AUTH_RECONNECT_PROOF);
|
||||
packet << uint8(WOW_FAIL_VERSION_INVALID);
|
||||
SendPacket(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sending response
|
||||
ByteBuffer pkt;
|
||||
pkt << uint8(AUTH_RECONNECT_PROOF);
|
||||
pkt << uint8(WOW_SUCCESS);
|
||||
pkt << uint16(0); // LoginFlags, 1 has account message
|
||||
SendPacket(pkt);
|
||||
_status = STATUS_AUTHED;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("server.authserver.hack", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", GetRemoteIpAddress().to_string().c_str(),
|
||||
GetRemotePort(), _accountInfo.Login.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthSession::HandleRealmList()
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "Entering _HandleRealmList");
|
||||
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS);
|
||||
stmt->setUInt32(0, _accountInfo.Id);
|
||||
|
||||
_queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1)));
|
||||
_status = STATUS_WAITING_FOR_REALM_LIST;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuthSession::RealmListCallback(PreparedQueryResult result)
|
||||
{
|
||||
std::map<uint32, uint8> characterCounts;
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
characterCounts[fields[0].GetUInt32()] = fields[1].GetUInt8();
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
// Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
|
||||
ByteBuffer pkt;
|
||||
|
||||
size_t RealmListSize = 0;
|
||||
for (auto const& [realmHandle, realm] : sRealmList->GetRealms())
|
||||
{
|
||||
// don't work with realms which not compatible with the client
|
||||
bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.Build == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.Build));
|
||||
|
||||
// No SQL injection. id of realm is controlled by the database.
|
||||
uint32 flag = realm.Flags;
|
||||
RealmBuildInfo const* buildInfo = sRealmList->GetBuildInfo(realm.Build);
|
||||
if (!okBuild)
|
||||
{
|
||||
if (!buildInfo)
|
||||
continue;
|
||||
|
||||
flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for
|
||||
}
|
||||
|
||||
if (!buildInfo)
|
||||
flag &= ~REALM_FLAG_SPECIFYBUILD;
|
||||
|
||||
std::string name = realm.Name;
|
||||
if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << name << " (" << buildInfo->MajorVersion << '.' << buildInfo->MinorVersion << '.' << buildInfo->BugfixVersion << ')';
|
||||
name = ss.str();
|
||||
}
|
||||
|
||||
uint8 lock = (realm.AllowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0;
|
||||
|
||||
pkt << uint8(realm.Type); // realm type
|
||||
if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
|
||||
pkt << uint8(lock); // if 1, then realm locked
|
||||
|
||||
pkt << uint8(flag); // RealmFlags
|
||||
pkt << name;
|
||||
pkt << boost::lexical_cast<std::string>(realm.GetAddressForClient(GetRemoteIpAddress()));
|
||||
pkt << float(realm.PopulationLevel);
|
||||
pkt << uint8(characterCounts[realm.Id.Realm]);
|
||||
pkt << uint8(realm.Timezone); // realm category
|
||||
|
||||
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
|
||||
pkt << uint8(realm.Id.Realm);
|
||||
else
|
||||
pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients
|
||||
|
||||
if (_expversion & POST_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD)
|
||||
{
|
||||
pkt << uint8(buildInfo->MajorVersion);
|
||||
pkt << uint8(buildInfo->MinorVersion);
|
||||
pkt << uint8(buildInfo->BugfixVersion);
|
||||
pkt << uint16(buildInfo->Build);
|
||||
}
|
||||
|
||||
++RealmListSize;
|
||||
}
|
||||
|
||||
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
|
||||
{
|
||||
pkt << uint8(0x10);
|
||||
pkt << uint8(0x00);
|
||||
}
|
||||
else // 1.12.1 and 1.12.2 clients
|
||||
{
|
||||
pkt << uint8(0x00);
|
||||
pkt << uint8(0x02);
|
||||
}
|
||||
|
||||
// make a ByteBuffer which stores the RealmList's size
|
||||
ByteBuffer RealmListSizeBuffer;
|
||||
RealmListSizeBuffer << uint32(0);
|
||||
|
||||
if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
|
||||
RealmListSizeBuffer << uint16(RealmListSize);
|
||||
else
|
||||
RealmListSizeBuffer << uint32(RealmListSize);
|
||||
|
||||
ByteBuffer hdr;
|
||||
hdr << uint8(REALM_LIST);
|
||||
hdr << uint16(pkt.size() + RealmListSizeBuffer.size());
|
||||
hdr.append(RealmListSizeBuffer); // append RealmList's size buffer
|
||||
hdr.append(pkt); // append realms in the realmlist
|
||||
SendPacket(hdr);
|
||||
|
||||
_status = STATUS_AUTHED;
|
||||
}
|
||||
|
||||
bool AuthSession::VerifyVersion(uint8 const* a, int32 aLength, Acore::Crypto::SHA1::Digest const& versionProof, bool isReconnect)
|
||||
{
|
||||
if (!sConfigMgr->GetOption<bool>("StrictVersionCheck", false))
|
||||
return true;
|
||||
|
||||
Acore::Crypto::SHA1::Digest zeros;
|
||||
Acore::Crypto::SHA1::Digest const* versionHash = nullptr;
|
||||
|
||||
if (!isReconnect)
|
||||
{
|
||||
RealmBuildInfo const* buildInfo = sRealmList->GetBuildInfo(_build);
|
||||
if (!buildInfo)
|
||||
return false;
|
||||
|
||||
if (_os == "Win")
|
||||
versionHash = &buildInfo->WindowsHash;
|
||||
else if (_os == "OSX")
|
||||
versionHash = &buildInfo->MacHash;
|
||||
|
||||
if (!versionHash)
|
||||
return false;
|
||||
|
||||
if (zeros == *versionHash)
|
||||
return true; // not filled serverside
|
||||
}
|
||||
else
|
||||
versionHash = &zeros;
|
||||
|
||||
Acore::Crypto::SHA1 version;
|
||||
version.UpdateData(a, aLength);
|
||||
version.UpdateData(*versionHash);
|
||||
version.Finalize();
|
||||
|
||||
return (versionProof == version.GetDigest());
|
||||
}
|
||||
109
src/server/authserver/Server/AuthSession.h
Normal file
109
src/server/authserver/Server/AuthSession.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef __AUTHSESSION_H__
|
||||
#define __AUTHSESSION_H__
|
||||
|
||||
#include "AsyncCallbackProcessor.h"
|
||||
#include "BigNumber.h"
|
||||
#include "ByteBuffer.h"
|
||||
#include "Common.h"
|
||||
#include "CryptoHash.h"
|
||||
#include "Optional.h"
|
||||
#include "Socket.h"
|
||||
#include "SRP6.h"
|
||||
#include "QueryResult.h"
|
||||
#include <memory>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
class Field;
|
||||
struct AuthHandler;
|
||||
|
||||
enum AuthStatus
|
||||
{
|
||||
STATUS_CHALLENGE = 0,
|
||||
STATUS_LOGON_PROOF,
|
||||
STATUS_RECONNECT_PROOF,
|
||||
STATUS_AUTHED,
|
||||
STATUS_WAITING_FOR_REALM_LIST,
|
||||
STATUS_CLOSED
|
||||
};
|
||||
|
||||
struct AccountInfo
|
||||
{
|
||||
void LoadResult(Field* fields);
|
||||
|
||||
uint32 Id = 0;
|
||||
std::string Login;
|
||||
bool IsLockedToIP = false;
|
||||
std::string LockCountry;
|
||||
std::string LastIP;
|
||||
uint32 FailedLogins = 0;
|
||||
bool IsBanned = false;
|
||||
bool IsPermanentlyBanned = false;
|
||||
AccountTypes SecurityLevel = SEC_PLAYER;
|
||||
};
|
||||
|
||||
class AuthSession : public Socket<AuthSession>
|
||||
{
|
||||
typedef Socket<AuthSession> AuthSocket;
|
||||
|
||||
public:
|
||||
static std::unordered_map<uint8, AuthHandler> InitHandlers();
|
||||
|
||||
AuthSession(tcp::socket&& socket);
|
||||
|
||||
void Start() override;
|
||||
bool Update() override;
|
||||
|
||||
void SendPacket(ByteBuffer& packet);
|
||||
|
||||
protected:
|
||||
void ReadHandler() override;
|
||||
|
||||
private:
|
||||
bool HandleLogonChallenge();
|
||||
bool HandleLogonProof();
|
||||
bool HandleReconnectChallenge();
|
||||
bool HandleReconnectProof();
|
||||
bool HandleRealmList();
|
||||
|
||||
void CheckIpCallback(PreparedQueryResult result);
|
||||
void LogonChallengeCallback(PreparedQueryResult result);
|
||||
void ReconnectChallengeCallback(PreparedQueryResult result);
|
||||
void RealmListCallback(PreparedQueryResult result);
|
||||
|
||||
bool VerifyVersion(uint8 const* a, int32 aLength, Acore::Crypto::SHA1::Digest const& versionProof, bool isReconnect);
|
||||
|
||||
Optional<Acore::Crypto::SRP6> _srp6;
|
||||
SessionKey _sessionKey = {};
|
||||
std::array<uint8, 16> _reconnectProof = {};
|
||||
|
||||
AuthStatus _status;
|
||||
AccountInfo _accountInfo;
|
||||
Optional<std::vector<uint8>> _totpSecret;
|
||||
std::string _localizationName;
|
||||
std::string _os;
|
||||
std::string _ipCountry;
|
||||
uint16 _build;
|
||||
uint8 _expversion;
|
||||
|
||||
QueryCallbackProcessor _queryProcessor;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct AuthHandler
|
||||
{
|
||||
AuthStatus status;
|
||||
size_t packetSize;
|
||||
bool (AuthSession::* handler)();
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef _AUTHSOCKET_H
|
||||
#define _AUTHSOCKET_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "CryptoHash.h"
|
||||
#include "Optional.h"
|
||||
#include "RealmSocket.h"
|
||||
#include "SRP6.h"
|
||||
#include <mutex>
|
||||
|
||||
class ACE_INET_Addr;
|
||||
class Field;
|
||||
struct Realm;
|
||||
|
||||
enum eStatus
|
||||
{
|
||||
STATUS_CHALLENGE,
|
||||
STATUS_LOGON_PROOF,
|
||||
STATUS_RECON_PROOF,
|
||||
STATUS_PATCH, // unused
|
||||
STATUS_AUTHED,
|
||||
STATUS_CLOSED
|
||||
};
|
||||
|
||||
struct AccountInfo
|
||||
{
|
||||
void LoadResult(Field* fields);
|
||||
|
||||
uint32 Id = 0;
|
||||
std::string Login;
|
||||
bool IsLockedToIP = false;
|
||||
std::string LockCountry;
|
||||
std::string LastIP;
|
||||
uint32 FailedLogins = 0;
|
||||
bool IsBanned = false;
|
||||
bool IsPermanentlyBanned = false;
|
||||
AccountTypes SecurityLevel = SEC_PLAYER;
|
||||
};
|
||||
|
||||
// Handle login commands
|
||||
class AuthSocket: public RealmSocket::Session
|
||||
{
|
||||
public:
|
||||
const static int s_BYTE_SIZE = 32;
|
||||
|
||||
AuthSocket(RealmSocket& socket);
|
||||
~AuthSocket() override;
|
||||
|
||||
void OnRead() override;
|
||||
void OnAccept() override;
|
||||
void OnClose() override;
|
||||
|
||||
static ACE_INET_Addr const& GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr);
|
||||
|
||||
bool _HandleLogonChallenge();
|
||||
bool _HandleLogonProof();
|
||||
bool _HandleReconnectChallenge();
|
||||
bool _HandleReconnectProof();
|
||||
bool _HandleRealmList();
|
||||
|
||||
//data transfer handle for patch
|
||||
bool _HandleXferResume();
|
||||
bool _HandleXferCancel();
|
||||
bool _HandleXferAccept();
|
||||
|
||||
FILE* pPatch;
|
||||
std::mutex patcherLock;
|
||||
|
||||
private:
|
||||
RealmSocket& socket_;
|
||||
RealmSocket& socket() { return socket_; }
|
||||
|
||||
std::optional<Acore::Crypto::SRP6> _srp6;
|
||||
SessionKey _sessionKey = {};
|
||||
std::array<uint8, 16> _reconnectProof = {};
|
||||
|
||||
eStatus _status;
|
||||
|
||||
AccountInfo _accountInfo;
|
||||
Optional<std::vector<uint8>> _totpSecret;
|
||||
|
||||
// Since GetLocaleByName() is _NOT_ bijective, we have to store the locale as a string. Otherwise we can't differ
|
||||
// between enUS and enGB, which is important for the patch system
|
||||
std::string _localizationName;
|
||||
std::string _os;
|
||||
std::string _ipCountry;
|
||||
uint16 _build;
|
||||
uint8 _expversion;
|
||||
};
|
||||
|
||||
#endif
|
||||
46
src/server/authserver/Server/AuthSocketMgr.h
Normal file
46
src/server/authserver/Server/AuthSocketMgr.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef AuthSocketMgr_h__
|
||||
#define AuthSocketMgr_h__
|
||||
|
||||
#include "SocketMgr.h"
|
||||
#include "AuthSession.h"
|
||||
|
||||
class AuthSocketMgr : public SocketMgr<AuthSession>
|
||||
{
|
||||
typedef SocketMgr<AuthSession> BaseSocketMgr;
|
||||
|
||||
public:
|
||||
static AuthSocketMgr& Instance()
|
||||
{
|
||||
static AuthSocketMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool StartNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int threadCount = 1) override
|
||||
{
|
||||
if (!BaseSocketMgr::StartNetwork(ioContext, bindIp, port, threadCount))
|
||||
return false;
|
||||
|
||||
_acceptor->AsyncAcceptWithCallback<&AuthSocketMgr::OnSocketAccept>();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
NetworkThread<AuthSession>* CreateThreads() const override
|
||||
{
|
||||
return new NetworkThread<AuthSession>[1];
|
||||
}
|
||||
|
||||
static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
|
||||
{
|
||||
Instance().OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
|
||||
}
|
||||
};
|
||||
|
||||
#define sAuthSocketMgr AuthSocketMgr::Instance()
|
||||
|
||||
#endif // AuthSocketMgr_h__
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef __REALMACCEPTOR_H__
|
||||
#define __REALMACCEPTOR_H__
|
||||
|
||||
#include <ace/Acceptor.h>
|
||||
#include <ace/SOCK_Acceptor.h>
|
||||
|
||||
#include "RealmSocket.h"
|
||||
#include "AuthSocket.h"
|
||||
|
||||
class RealmAcceptor : public ACE_Acceptor<RealmSocket, ACE_SOCK_Acceptor>
|
||||
{
|
||||
public:
|
||||
RealmAcceptor() = default;
|
||||
virtual ~RealmAcceptor()
|
||||
{
|
||||
if (reactor())
|
||||
reactor()->cancel_timer(this, 1);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int make_svc_handler(RealmSocket*& sh)
|
||||
{
|
||||
if (sh == nullptr)
|
||||
ACE_NEW_RETURN(sh, RealmSocket, -1);
|
||||
|
||||
sh->reactor(reactor());
|
||||
sh->set_session(new AuthSocket(*sh));
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/)
|
||||
{
|
||||
LOG_INFO("network", "Resuming acceptor");
|
||||
reactor()->cancel_timer(this, 1);
|
||||
return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);
|
||||
}
|
||||
|
||||
virtual int handle_accept_error()
|
||||
{
|
||||
#if defined(ENFILE) && defined(EMFILE)
|
||||
if (errno == ENFILE || errno == EMFILE)
|
||||
{
|
||||
LOG_ERROR("network", "Out of file descriptors, suspending incoming connections for 10 seconds");
|
||||
reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL);
|
||||
reactor()->schedule_timer(this, nullptr, ACE_Time_Value(10));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -167,6 +167,23 @@ MySQLExecutable = ""
|
|||
|
||||
IPLocationFile = ""
|
||||
|
||||
#
|
||||
# BanExpiryCheckInterval
|
||||
# Description: Time (in seconds) between checks for expired bans
|
||||
# Default: 60
|
||||
#
|
||||
|
||||
BanExpiryCheckInterval = 60
|
||||
|
||||
#
|
||||
# StrictVersionCheck
|
||||
# Description: Prevent modified clients from connecting
|
||||
# Default: 0 - (Disabled)
|
||||
# 1 - (Enabled)
|
||||
#
|
||||
|
||||
StrictVersionCheck = 0
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
|
|
|
|||
|
|
@ -1,299 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef _CALLBACK_H
|
||||
#define _CALLBACK_H
|
||||
|
||||
#include <ace/Future.h>
|
||||
#include <ace/Future_Set.h>
|
||||
#include "QueryResult.h"
|
||||
|
||||
typedef ACE_Future<QueryResult> QueryResultFuture;
|
||||
typedef ACE_Future<PreparedQueryResult> PreparedQueryResultFuture;
|
||||
|
||||
/*! A simple template using ACE_Future to manage callbacks from the thread and object that
|
||||
issued the request. <ParamType> is variable type of parameter that is used as parameter
|
||||
for the callback function.
|
||||
*/
|
||||
#define CALLBACK_STAGE_INVALID uint8(-1)
|
||||
|
||||
template <typename Result, typename ParamType, bool chain = false>
|
||||
class QueryCallback
|
||||
{
|
||||
public:
|
||||
QueryCallback() : _param(), _stage(chain ? 0 : CALLBACK_STAGE_INVALID) {}
|
||||
|
||||
//! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery
|
||||
void SetFutureResult(ACE_Future<Result> value)
|
||||
{
|
||||
_result = value;
|
||||
}
|
||||
|
||||
ACE_Future<Result> GetFutureResult()
|
||||
{
|
||||
return _result;
|
||||
}
|
||||
|
||||
int IsReady()
|
||||
{
|
||||
return _result.ready();
|
||||
}
|
||||
|
||||
void GetResult(Result& res)
|
||||
{
|
||||
_result.get(res);
|
||||
}
|
||||
|
||||
void FreeResult()
|
||||
{
|
||||
_result.cancel();
|
||||
}
|
||||
|
||||
void SetParam(ParamType value)
|
||||
{
|
||||
_param = value;
|
||||
}
|
||||
|
||||
ParamType GetParam()
|
||||
{
|
||||
return _param;
|
||||
}
|
||||
|
||||
//! Resets the stage of the callback chain
|
||||
void ResetStage()
|
||||
{
|
||||
if (!chain)
|
||||
return;
|
||||
|
||||
_stage = 0;
|
||||
}
|
||||
|
||||
//! Advances the callback chain to the next stage, so upper level code can act on its results accordingly
|
||||
void NextStage()
|
||||
{
|
||||
if (!chain)
|
||||
return;
|
||||
|
||||
++_stage;
|
||||
}
|
||||
|
||||
//! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid)
|
||||
uint8 GetStage()
|
||||
{
|
||||
return _stage;
|
||||
}
|
||||
|
||||
//! Resets all underlying variables (param, result and stage)
|
||||
void Reset()
|
||||
{
|
||||
SetParam(nullptr);
|
||||
FreeResult();
|
||||
ResetStage();
|
||||
}
|
||||
|
||||
private:
|
||||
ACE_Future<Result> _result;
|
||||
ParamType _param;
|
||||
uint8 _stage;
|
||||
};
|
||||
|
||||
template <typename Result, typename ParamType1, typename ParamType2, bool chain = false>
|
||||
class QueryCallback_2
|
||||
{
|
||||
public:
|
||||
QueryCallback_2() : _stage(chain ? 0 : CALLBACK_STAGE_INVALID) {}
|
||||
|
||||
//! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery
|
||||
void SetFutureResult(ACE_Future<Result> value)
|
||||
{
|
||||
_result = value;
|
||||
}
|
||||
|
||||
ACE_Future<Result> GetFutureResult()
|
||||
{
|
||||
return _result;
|
||||
}
|
||||
|
||||
int IsReady()
|
||||
{
|
||||
return _result.ready();
|
||||
}
|
||||
|
||||
void GetResult(Result& res)
|
||||
{
|
||||
_result.get(res);
|
||||
}
|
||||
|
||||
void FreeResult()
|
||||
{
|
||||
_result.cancel();
|
||||
}
|
||||
|
||||
void SetFirstParam(ParamType1 value)
|
||||
{
|
||||
_param_1 = value;
|
||||
}
|
||||
|
||||
void SetSecondParam(ParamType2 value)
|
||||
{
|
||||
_param_2 = value;
|
||||
}
|
||||
|
||||
ParamType1 GetFirstParam()
|
||||
{
|
||||
return _param_1;
|
||||
}
|
||||
|
||||
ParamType2 GetSecondParam()
|
||||
{
|
||||
return _param_2;
|
||||
}
|
||||
|
||||
//! Resets the stage of the callback chain
|
||||
void ResetStage()
|
||||
{
|
||||
if (!chain)
|
||||
return;
|
||||
|
||||
_stage = 0;
|
||||
}
|
||||
|
||||
//! Advances the callback chain to the next stage, so upper level code can act on its results accordingly
|
||||
void NextStage()
|
||||
{
|
||||
if (!chain)
|
||||
return;
|
||||
|
||||
++_stage;
|
||||
}
|
||||
|
||||
//! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid)
|
||||
uint8 GetStage()
|
||||
{
|
||||
return _stage;
|
||||
}
|
||||
|
||||
//! Resets all underlying variables (param, result and stage)
|
||||
void Reset()
|
||||
{
|
||||
SetFirstParam(0);
|
||||
SetSecondParam(nullptr);
|
||||
FreeResult();
|
||||
ResetStage();
|
||||
}
|
||||
|
||||
private:
|
||||
ACE_Future<Result> _result;
|
||||
ParamType1 _param_1;
|
||||
ParamType2 _param_2;
|
||||
uint8 _stage;
|
||||
};
|
||||
|
||||
template <typename Result, typename ParamType1, typename ParamType2, typename ParamType3, bool chain = false>
|
||||
class QueryCallback_3
|
||||
{
|
||||
public:
|
||||
QueryCallback_3() : _stage(chain ? 0 : CALLBACK_STAGE_INVALID) {}
|
||||
|
||||
//! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery
|
||||
void SetFutureResult(ACE_Future<Result> value)
|
||||
{
|
||||
_result = value;
|
||||
}
|
||||
|
||||
ACE_Future<Result> GetFutureResult()
|
||||
{
|
||||
return _result;
|
||||
}
|
||||
|
||||
int IsReady()
|
||||
{
|
||||
return _result.ready();
|
||||
}
|
||||
|
||||
void GetResult(Result& res)
|
||||
{
|
||||
_result.get(res);
|
||||
}
|
||||
|
||||
void FreeResult()
|
||||
{
|
||||
_result.cancel();
|
||||
}
|
||||
|
||||
void SetFirstParam(ParamType1 value)
|
||||
{
|
||||
_param_1 = value;
|
||||
}
|
||||
|
||||
void SetSecondParam(ParamType2 value)
|
||||
{
|
||||
_param_2 = value;
|
||||
}
|
||||
|
||||
void SetThirdParam(ParamType3 value)
|
||||
{
|
||||
_param_3 = value;
|
||||
}
|
||||
|
||||
ParamType1 GetFirstParam()
|
||||
{
|
||||
return _param_1;
|
||||
}
|
||||
|
||||
ParamType2 GetSecondParam()
|
||||
{
|
||||
return _param_2;
|
||||
}
|
||||
|
||||
ParamType3 GetThirdParam()
|
||||
{
|
||||
return _param_3;
|
||||
}
|
||||
|
||||
//! Resets the stage of the callback chain
|
||||
void ResetStage()
|
||||
{
|
||||
if (!chain)
|
||||
return;
|
||||
|
||||
_stage = 0;
|
||||
}
|
||||
|
||||
//! Advances the callback chain to the next stage, so upper level code can act on its results accordingly
|
||||
void NextStage()
|
||||
{
|
||||
if (!chain)
|
||||
return;
|
||||
|
||||
++_stage;
|
||||
}
|
||||
|
||||
//! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid)
|
||||
uint8 GetStage()
|
||||
{
|
||||
return _stage;
|
||||
}
|
||||
|
||||
//! Resets all underlying variables (param, result and stage)
|
||||
void Reset()
|
||||
{
|
||||
SetFirstParam(nullptr);
|
||||
SetSecondParam(nullptr);
|
||||
SetThirdParam(nullptr);
|
||||
FreeResult();
|
||||
ResetStage();
|
||||
}
|
||||
|
||||
private:
|
||||
ACE_Future<Result> _result;
|
||||
ParamType1 _param_1;
|
||||
ParamType2 _param_2;
|
||||
ParamType3 _param_3;
|
||||
uint8 _stage;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
#include "DatabaseWorker.h"
|
||||
#include "ProducerConsumerQueue.h"
|
||||
#include "PCQueue.h"
|
||||
#include "SQLOperation.h"
|
||||
|
||||
DatabaseWorker::DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include "MySQLPreparedStatement.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "ProducerConsumerQueue.h"
|
||||
#include "PCQueue.h"
|
||||
#include "QueryCallback.h"
|
||||
#include "QueryHolder.h"
|
||||
#include "QueryResult.h"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
|||
"LEFT JOIN account_access aa ON a.id = aa.id "
|
||||
"LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 "
|
||||
"LEFT JOIN ip_banned ipb ON ipb.ip = ? "
|
||||
"WHERE a.username = ?", CONNECTION_SYNCH);
|
||||
"WHERE a.username = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_RECONNECTCHALLENGE,
|
||||
"SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, "
|
||||
"ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, "
|
||||
|
|
@ -30,14 +30,15 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
|||
"LEFT JOIN account_access aa ON a.id = aa.id "
|
||||
"LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 "
|
||||
"LEFT JOIN ip_banned ipb ON ipb.ip = ? "
|
||||
"WHERE a.username = ? AND a.session_key IS NOT NULL", CONNECTION_SYNCH);
|
||||
"WHERE a.username = ? AND a.session_key IS NOT NULL", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.session_key, a.last_ip, a.locked, a.lock_country, a.expansion, a.mutetime, a.locale, a.recruiter, a.os, a.totaltime, "
|
||||
"aa.gmlevel, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id FROM account a LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) "
|
||||
"LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account r ON a.id = r.recruiter WHERE a.username = ? "
|
||||
"AND a.session_key IS NOT NULL ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_SYNCH);
|
||||
"AND a.session_key IS NOT NULL ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_IP_INFO, "SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity realmd', 'Failed login autoban')", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_IP_BANNED_ALL, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) ORDER BY unbandate", CONNECTION_SYNCH);
|
||||
|
|
@ -55,6 +56,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
|||
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS, "SELECT realmid, numchars FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ enum LoginDatabaseStatements : uint32
|
|||
LOGIN_SEL_REALMLIST,
|
||||
LOGIN_DEL_EXPIRED_IP_BANS,
|
||||
LOGIN_UPD_EXPIRED_ACCOUNT_BANS,
|
||||
LOGIN_SEL_IP_INFO,
|
||||
LOGIN_SEL_IP_BANNED,
|
||||
LOGIN_INS_IP_AUTO_BANNED,
|
||||
LOGIN_SEL_ACCOUNT_BANNED,
|
||||
|
|
@ -37,6 +38,7 @@ enum LoginDatabaseStatements : uint32
|
|||
LOGIN_SEL_ACCOUNT_INFO_BY_NAME,
|
||||
LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL,
|
||||
LOGIN_SEL_NUM_CHARS_ON_REALM,
|
||||
LOGIN_SEL_REALM_CHARACTER_COUNTS,
|
||||
LOGIN_SEL_ACCOUNT_BY_IP,
|
||||
LOGIN_INS_IP_BANNED,
|
||||
LOGIN_DEL_IP_NOT_BANNED,
|
||||
|
|
|
|||
|
|
@ -4,188 +4,319 @@
|
|||
|
||||
#include "ArenaSpectator.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "Chat.h"
|
||||
#include "LFGMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Map.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "World.h"
|
||||
|
||||
namespace ArenaSpectator
|
||||
bool ArenaSpectator::HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args)
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
std::list<std::string> errors;
|
||||
|
||||
if (!*args)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
std::list<std::string> errors;
|
||||
if (!*args)
|
||||
handler->SendSysMessage("Missing player name.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player->IsSpectator())
|
||||
{
|
||||
if (player->FindMap() && player->FindMap()->IsBattleArena())
|
||||
{
|
||||
handler->SendSysMessage("Missing player name.");
|
||||
HandleSpectatorWatchCommand(handler, args);
|
||||
return true;
|
||||
}
|
||||
if (player->IsSpectator())
|
||||
{
|
||||
if (player->FindMap() && player->FindMap()->IsBattleArena())
|
||||
handler->PSendSysMessage("You are already spectacting arena.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player->getClass() == CLASS_DEATH_KNIGHT && player->GetMapId() == 609)
|
||||
{
|
||||
handler->PSendSysMessage("Death Knights can't spectate before finishing questline.");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string name = std::string(args);
|
||||
Player* spectate = ObjectAccessor::FindPlayerByName(name);
|
||||
if (!spectate)
|
||||
{
|
||||
handler->SendSysMessage("Requested player not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (spectate->IsSpectator())
|
||||
{
|
||||
handler->SendSysMessage("Requested player is a spectator.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!spectate->FindMap() || !spectate->FindMap()->IsBattleArena())
|
||||
{
|
||||
handler->SendSysMessage("Requested player is not in arena.");
|
||||
return true;
|
||||
}
|
||||
|
||||
BattlegroundMap* bgmap = ((BattlegroundMap*)spectate->FindMap());
|
||||
if (!bgmap->GetBG() || bgmap->GetBG()->GetStatus() == STATUS_WAIT_LEAVE)
|
||||
{
|
||||
handler->SendSysMessage("This arena battle has finished.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player->IsBeingTeleported() || !player->IsInWorld())
|
||||
errors.push_back("Can't use while being teleported.");
|
||||
|
||||
if (!player->FindMap() || player->FindMap()->Instanceable())
|
||||
errors.push_back("Can't use while in instance, bg or arena.");
|
||||
|
||||
if (player->GetVehicle())
|
||||
errors.push_back("Can't be on a vehicle.");
|
||||
|
||||
if (player->IsInCombat())
|
||||
errors.push_back("Can't be in combat.");
|
||||
|
||||
if (player->isUsingLfg())
|
||||
errors.push_back("Can't spectate while using LFG system.");
|
||||
|
||||
if (player->InBattlegroundQueue())
|
||||
errors.push_back("Can't be queued for arena or bg.");
|
||||
|
||||
if (player->GetGroup())
|
||||
errors.push_back("Can't be in a group.");
|
||||
|
||||
if (player->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
errors.push_back("Can't be isolated.");
|
||||
|
||||
if (player->m_mover != player)
|
||||
errors.push_back("You must control yourself.");
|
||||
|
||||
if (player->IsInFlight())
|
||||
errors.push_back("Can't be in flight.");
|
||||
|
||||
if (player->IsMounted())
|
||||
errors.push_back("Dismount before spectating.");
|
||||
|
||||
if (!player->IsAlive())
|
||||
errors.push_back("Must be alive.");
|
||||
|
||||
if (!player->m_Controlled.empty())
|
||||
errors.push_back("Can't be controlling creatures.");
|
||||
|
||||
const Unit::VisibleAuraMap* va = player->GetVisibleAuras();
|
||||
for (auto itr = va->begin(); itr != va->end(); ++itr)
|
||||
if (Aura* aura = itr->second->GetBase())
|
||||
if (!itr->second->IsPositive() && !aura->IsPermanent() && aura->GetDuration() < HOUR * IN_MILLISECONDS)
|
||||
{
|
||||
HandleSpectatorWatchCommand(handler, args);
|
||||
return true;
|
||||
}
|
||||
handler->PSendSysMessage("You are already spectacting arena.");
|
||||
return true;
|
||||
}
|
||||
if (player->getClass() == CLASS_DEATH_KNIGHT && player->GetMapId() == 609)
|
||||
{
|
||||
handler->PSendSysMessage("Death Knights can't spectate before finishing questline.");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string name = std::string(args);
|
||||
Player* spectate = ObjectAccessor::FindPlayerByName(name);
|
||||
if (!spectate)
|
||||
{
|
||||
handler->SendSysMessage("Requested player not found.");
|
||||
return true;
|
||||
}
|
||||
if (spectate->IsSpectator())
|
||||
{
|
||||
handler->SendSysMessage("Requested player is a spectator.");
|
||||
return true;
|
||||
}
|
||||
if (!spectate->FindMap() || !spectate->FindMap()->IsBattleArena())
|
||||
{
|
||||
handler->SendSysMessage("Requested player is not in arena.");
|
||||
return true;
|
||||
}
|
||||
BattlegroundMap* bgmap = ((BattlegroundMap*)spectate->FindMap());
|
||||
if (!bgmap->GetBG() || bgmap->GetBG()->GetStatus() == STATUS_WAIT_LEAVE)
|
||||
{
|
||||
handler->SendSysMessage("This arena battle has finished.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player->IsBeingTeleported() || !player->IsInWorld())
|
||||
errors.push_back("Can't use while being teleported.");
|
||||
if (!player->FindMap() || player->FindMap()->Instanceable())
|
||||
errors.push_back("Can't use while in instance, bg or arena.");
|
||||
if (player->GetVehicle())
|
||||
errors.push_back("Can't be on a vehicle.");
|
||||
if (player->IsInCombat())
|
||||
errors.push_back("Can't be in combat.");
|
||||
if (player->isUsingLfg())
|
||||
errors.push_back("Can't spectate while using LFG system.");
|
||||
if (player->InBattlegroundQueue())
|
||||
errors.push_back("Can't be queued for arena or bg.");
|
||||
if (player->GetGroup())
|
||||
errors.push_back("Can't be in a group.");
|
||||
if (player->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
errors.push_back("Can't be isolated.");
|
||||
if (player->m_mover != player)
|
||||
errors.push_back("You must control yourself.");
|
||||
if (player->IsInFlight())
|
||||
errors.push_back("Can't be in flight.");
|
||||
if (player->IsMounted())
|
||||
errors.push_back("Dismount before spectating.");
|
||||
if (!player->IsAlive())
|
||||
errors.push_back("Must be alive.");
|
||||
if (!player->m_Controlled.empty())
|
||||
errors.push_back("Can't be controlling creatures.");
|
||||
|
||||
const Unit::VisibleAuraMap* va = player->GetVisibleAuras();
|
||||
for (Unit::VisibleAuraMap::const_iterator itr = va->begin(); itr != va->end(); ++itr)
|
||||
if (Aura* aura = itr->second->GetBase())
|
||||
if (!itr->second->IsPositive() && !aura->IsPermanent() && aura->GetDuration() < HOUR * IN_MILLISECONDS)
|
||||
switch (aura->GetSpellInfo()->Id)
|
||||
{
|
||||
switch (aura->GetSpellInfo()->Id)
|
||||
{
|
||||
case lfg::LFG_SPELL_DUNGEON_DESERTER:
|
||||
case lfg::LFG_SPELL_DUNGEON_COOLDOWN:
|
||||
case 26013: // bg deserter
|
||||
case 57724: // sated
|
||||
case 57723: // exhaustion
|
||||
case 25771: // forbearance
|
||||
case 15007: // resurrection sickness
|
||||
case 24755: // Tricked or Treated (z eventu)
|
||||
continue;
|
||||
}
|
||||
|
||||
errors.push_back("Can't have negative auras.");
|
||||
break;
|
||||
case lfg::LFG_SPELL_DUNGEON_DESERTER:
|
||||
case lfg::LFG_SPELL_DUNGEON_COOLDOWN:
|
||||
case 26013: // bg deserter
|
||||
case 57724: // sated
|
||||
case 57723: // exhaustion
|
||||
case 25771: // forbearance
|
||||
case 15007: // resurrection sickness
|
||||
case 24755: // Tricked or Treated (z eventu)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uint32 inviteInstanceId = player->GetPendingSpectatorInviteInstanceId())
|
||||
{
|
||||
if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId))
|
||||
tbg->RemoveToBeTeleported(player->GetGUID());
|
||||
player->SetPendingSpectatorInviteInstanceId(0);
|
||||
}
|
||||
|
||||
bool bgPreparation = false;
|
||||
if ((!handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS) ||
|
||||
(handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_WAIT_JOIN && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS))
|
||||
{
|
||||
bgPreparation = true;
|
||||
handler->SendSysMessage("Arena is not in progress yet. You will be invited as soon as it starts.");
|
||||
bgmap->GetBG()->AddToBeTeleported(player->GetGUID(), spectate->GetGUID());
|
||||
player->SetPendingSpectatorInviteInstanceId(spectate->GetBattlegroundId());
|
||||
}
|
||||
|
||||
if (!errors.empty())
|
||||
{
|
||||
handler->PSendSysMessage("To spectate, please fix the following:");
|
||||
for (std::list<std::string>::const_iterator itr = errors.begin(); itr != errors.end(); ++itr)
|
||||
handler->PSendSysMessage("- %s", (*itr).c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bgPreparation)
|
||||
return true;
|
||||
|
||||
player->SetPendingSpectatorForBG(spectate->GetBattlegroundId());
|
||||
player->SetBattlegroundId(spectate->GetBattlegroundId(), spectate->GetBattlegroundTypeId(), PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL);
|
||||
player->SetEntryPoint();
|
||||
float z = spectate->GetMapId() == 618 ? std::max(28.27f, spectate->GetPositionZ() + 0.25f) : spectate->GetPositionZ() + 0.25f;
|
||||
player->TeleportTo(spectate->GetMapId(), spectate->GetPositionX(), spectate->GetPositionY(), z, spectate->GetOrientation(), TELE_TO_GM_MODE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!*args)
|
||||
return true;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
if (!player->IsSpectator())
|
||||
return true;
|
||||
|
||||
if (!player->FindMap() || !player->FindMap()->IsBattleArena())
|
||||
return true;
|
||||
|
||||
Battleground* bg = ((BattlegroundMap*)player->FindMap())->GetBG();
|
||||
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
|
||||
return true;
|
||||
|
||||
std::string name = std::string(args);
|
||||
Player* spectate = ObjectAccessor::FindPlayerByName(name);
|
||||
if (!spectate || !spectate->IsAlive() || spectate->IsSpectator() || spectate->GetGUID() == player->GetGUID() || !spectate->IsInWorld() || !spectate->FindMap() || spectate->IsBeingTeleported() || spectate->FindMap() != player->FindMap() || !bg->IsPlayerInBattleground(spectate->GetGUID()))
|
||||
return true;
|
||||
|
||||
if (WorldObject* o = player->GetViewpoint())
|
||||
if (Unit* u = o->ToUnit())
|
||||
{
|
||||
u->RemoveAurasByType(SPELL_AURA_BIND_SIGHT, player->GetGUID());
|
||||
player->RemoveAurasDueToSpell(SPECTATOR_SPELL_BINDSIGHT, player->GetGUID(), (1 << EFFECT_1));
|
||||
|
||||
if (u->GetGUID() == spectate->GetGUID())
|
||||
return true;
|
||||
errors.push_back("Can't have negative auras.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (player->GetGuidValue(PLAYER_FARSIGHT) || player->m_seer != player) // pussywizard: below this point we must not have a viewpoint!
|
||||
return true;
|
||||
if (uint32 inviteInstanceId = player->GetPendingSpectatorInviteInstanceId())
|
||||
{
|
||||
if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId))
|
||||
tbg->RemoveToBeTeleported(player->GetGUID());
|
||||
player->SetPendingSpectatorInviteInstanceId(0);
|
||||
}
|
||||
|
||||
if (player->HaveAtClient(spectate))
|
||||
player->CastSpell(spectate, SPECTATOR_SPELL_BINDSIGHT, true);
|
||||
bool bgPreparation = false;
|
||||
if ((!handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS) ||
|
||||
(handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_WAIT_JOIN && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS))
|
||||
{
|
||||
bgPreparation = true;
|
||||
handler->SendSysMessage("Arena is not in progress yet. You will be invited as soon as it starts.");
|
||||
bgmap->GetBG()->AddToBeTeleported(player->GetGUID(), spectate->GetGUID());
|
||||
player->SetPendingSpectatorInviteInstanceId(spectate->GetBattlegroundId());
|
||||
}
|
||||
|
||||
if (!errors.empty())
|
||||
{
|
||||
handler->PSendSysMessage("To spectate, please fix the following:");
|
||||
for (std::list<std::string>::const_iterator itr = errors.begin(); itr != errors.end(); ++itr)
|
||||
handler->PSendSysMessage("- %s", (*itr).c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bgPreparation)
|
||||
return true;
|
||||
|
||||
float z = spectate->GetMapId() == 618 ? std::max(28.27f, spectate->GetPositionZ() + 0.25f) : spectate->GetPositionZ() + 0.25f;
|
||||
|
||||
player->SetPendingSpectatorForBG(spectate->GetBattlegroundId());
|
||||
player->SetBattlegroundId(spectate->GetBattlegroundId(), spectate->GetBattlegroundTypeId(), PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL);
|
||||
player->SetEntryPoint();
|
||||
player->TeleportTo(spectate->GetMapId(), spectate->GetPositionX(), spectate->GetPositionY(), z, spectate->GetOrientation(), TELE_TO_GM_MODE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArenaSpectator::HandleSpectatorWatchCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!*args)
|
||||
return true;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
if (!player->IsSpectator())
|
||||
return true;
|
||||
|
||||
if (!player->FindMap() || !player->FindMap()->IsBattleArena())
|
||||
return true;
|
||||
|
||||
Battleground* bg = ((BattlegroundMap*)player->FindMap())->GetBG();
|
||||
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
|
||||
return true;
|
||||
|
||||
std::string name = std::string(args);
|
||||
Player* spectate = ObjectAccessor::FindPlayerByName(name);
|
||||
if (!spectate || !spectate->IsAlive() || spectate->IsSpectator() || spectate->GetGUID() == player->GetGUID() || !spectate->IsInWorld() || !spectate->FindMap() || spectate->IsBeingTeleported() || spectate->FindMap() != player->FindMap() || !bg->IsPlayerInBattleground(spectate->GetGUID()))
|
||||
return true;
|
||||
|
||||
if (WorldObject* o = player->GetViewpoint())
|
||||
if (Unit* u = o->ToUnit())
|
||||
{
|
||||
u->RemoveAurasByType(SPELL_AURA_BIND_SIGHT, player->GetGUID());
|
||||
player->RemoveAurasDueToSpell(SPECTATOR_SPELL_BINDSIGHT, player->GetGUID(), (1 << EFFECT_1));
|
||||
|
||||
if (u->GetGUID() == spectate->GetGUID())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player->GetUInt64Value(PLAYER_FARSIGHT) || player->m_seer != player) // pussywizard: below this point we must not have a viewpoint!
|
||||
return true;
|
||||
|
||||
if (player->HaveAtClient(spectate))
|
||||
player->CastSpell(spectate, SPECTATOR_SPELL_BINDSIGHT, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ArenaSpectator::CreatePacket(WorldPacket& data, std::string const& message)
|
||||
{
|
||||
size_t len = message.length();
|
||||
data.Initialize(SMSG_MESSAGECHAT, 1 + 4 + 8 + 4 + 8 + 4 + 1 + len + 1);
|
||||
data << uint8(CHAT_MSG_WHISPER);
|
||||
data << uint32(LANG_ADDON);
|
||||
data << uint64(0);
|
||||
data << uint32(0);
|
||||
data << uint64(0);
|
||||
data << uint32(len + 1);
|
||||
data << message;
|
||||
data << uint8(0);
|
||||
}
|
||||
|
||||
void ArenaSpectator::HandleResetCommand(Player* player)
|
||||
{
|
||||
if (!player->FindMap() || !player->IsInWorld() || !player->FindMap()->IsBattleArena())
|
||||
return;
|
||||
|
||||
Battleground* bg = ((BattlegroundMap*)player->FindMap())->GetBG();
|
||||
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
|
||||
return;
|
||||
|
||||
Battleground::BattlegroundPlayerMap const& pl = bg->GetPlayers();
|
||||
for (Battleground::BattlegroundPlayerMap::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
|
||||
{
|
||||
if (player->HasReceivedSpectatorResetFor(itr->first))
|
||||
continue;
|
||||
|
||||
Player* plr = itr->second;
|
||||
player->AddReceivedSpectatorResetFor(itr->first);
|
||||
|
||||
SendCommand_String(player, itr->first, "NME", plr->GetName().c_str());
|
||||
// Xinef: addon compatibility
|
||||
SendCommand_UInt32Value(player, itr->first, "TEM", plr->GetBgTeamId() == TEAM_ALLIANCE ? ALLIANCE : HORDE);
|
||||
SendCommand_UInt32Value(player, itr->first, "CLA", plr->getClass());
|
||||
SendCommand_UInt32Value(player, itr->first, "MHP", plr->GetMaxHealth());
|
||||
SendCommand_UInt32Value(player, itr->first, "CHP", plr->GetHealth());
|
||||
SendCommand_UInt32Value(player, itr->first, "STA", plr->IsAlive() ? 1 : 0);
|
||||
Powers ptype = plr->getPowerType();
|
||||
SendCommand_UInt32Value(player, itr->first, "PWT", ptype);
|
||||
SendCommand_UInt32Value(player, itr->first, "MPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetMaxPower(ptype) / 10 : plr->GetMaxPower(ptype));
|
||||
SendCommand_UInt32Value(player, itr->first, "CPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetPower(ptype) / 10 : plr->GetPower(ptype));
|
||||
Pet* pet = plr->GetPet();
|
||||
SendCommand_UInt32Value(player, itr->first, "PHP", pet && pet->GetCreatureTemplate()->family ? (uint32)pet->GetHealthPct() : 0);
|
||||
SendCommand_UInt32Value(player, itr->first, "PET", pet ? pet->GetCreatureTemplate()->family : 0);
|
||||
SendCommand_GUID(player, itr->first, "TRG", plr->GetTarget());
|
||||
SendCommand_UInt32Value(player, itr->first, "RES", 1);
|
||||
SendCommand_UInt32Value(player, itr->first, "CDC", 1);
|
||||
SendCommand_UInt32Value(player, itr->first, "TIM", (bg->GetStartTime() < 46 * MINUTE * IN_MILLISECONDS) ? (46 * MINUTE * IN_MILLISECONDS - bg->GetStartTime()) / IN_MILLISECONDS : 0);
|
||||
// "SPE" not here (only possible to send starting a new cast)
|
||||
|
||||
// send all "CD"
|
||||
SpellCooldowns const& sc = plr->GetSpellCooldownMap();
|
||||
for (SpellCooldowns::const_iterator itrc = sc.begin(); itrc != sc.end(); ++itrc)
|
||||
if (itrc->second.sendToSpectator && itrc->second.maxduration >= SPECTATOR_COOLDOWN_MIN * IN_MILLISECONDS && itrc->second.maxduration <= SPECTATOR_COOLDOWN_MAX * IN_MILLISECONDS)
|
||||
if (uint32 cd = (getMSTimeDiff(getMSTime(), itrc->second.end) / 1000))
|
||||
SendCommand_Cooldown(player, itr->first, "ACD", itrc->first, cd, itrc->second.maxduration / 1000);
|
||||
|
||||
// send all visible "AUR"
|
||||
Unit::VisibleAuraMap const* visibleAuras = plr->GetVisibleAuras();
|
||||
for (Unit::VisibleAuraMap::const_iterator aitr = visibleAuras->begin(); aitr != visibleAuras->end(); ++aitr)
|
||||
{
|
||||
Aura* aura = aitr->second->GetBase();
|
||||
if (ShouldSendAura(aura, aitr->second->GetEffectMask(), plr->GetGUID(), false))
|
||||
SendCommand_Aura(player, itr->first, "AUR", aura->GetCasterGUID(), aura->GetSpellInfo()->Id, aura->GetSpellInfo()->IsPositive(), aura->GetSpellInfo()->Dispel, aura->GetDuration(), aura->GetMaxDuration(), (aura->GetCharges() > 1 ? aura->GetCharges() : aura->GetStackAmount()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ArenaSpectator::ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove)
|
||||
{
|
||||
if (aura->GetSpellInfo()->SpellIconID == 1 || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR1_NO_AURA_ICON))
|
||||
return false;
|
||||
|
||||
if (remove || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH) || aura->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_GENERIC)
|
||||
return true;
|
||||
|
||||
for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (effMask & (1 << i))
|
||||
{
|
||||
AuraType at = aura->GetEffect(i)->GetAuraType();
|
||||
if ((aura->GetEffect(i)->GetAmount() && (aura->GetSpellInfo()->IsPositive() || targetGUID != aura->GetCasterGUID())) ||
|
||||
at == SPELL_AURA_MECHANIC_IMMUNITY || at == SPELL_AURA_EFFECT_IMMUNITY || at == SPELL_AURA_STATE_IMMUNITY || at == SPELL_AURA_SCHOOL_IMMUNITY || at == SPELL_AURA_DISPEL_IMMUNITY)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
AC_GAME_API void ArenaSpectator::SendPacketTo(const Player* player, std::string&& message)
|
||||
{
|
||||
WorldPacket data;
|
||||
CreatePacket(data, message);
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
template<>
|
||||
AC_GAME_API void ArenaSpectator::SendPacketTo(const Map* map, std::string&& message)
|
||||
{
|
||||
if (!map->IsBattleArena())
|
||||
return;
|
||||
|
||||
Battleground* bg = ((BattlegroundMap*)map)->GetBG();
|
||||
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
|
||||
return;
|
||||
|
||||
WorldPacket data;
|
||||
CreatePacket(data, message);
|
||||
bg->SpectatorsSendPacket(data);
|
||||
}
|
||||
|
|
@ -5,15 +5,17 @@
|
|||
#ifndef AZEROTHCORE_ARENASPECTATOR_H
|
||||
#define AZEROTHCORE_ARENASPECTATOR_H
|
||||
|
||||
#include "Battleground.h"
|
||||
#include "Chat.h"
|
||||
#include "LFGMgr.h"
|
||||
#include "Map.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "World.h"
|
||||
#include "Common.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "StringFormat.h"
|
||||
|
||||
class Aura;
|
||||
class Player;
|
||||
class Map;
|
||||
class WorldPacket;
|
||||
|
||||
#define SPECTATOR_ADDON_VERSION 27
|
||||
#define SPECTATOR_BUFFER_LEN 150
|
||||
|
|
@ -25,196 +27,78 @@
|
|||
|
||||
namespace ArenaSpectator
|
||||
{
|
||||
template<class T> inline void SendCommand(T* o, const char* format, ...) ATTR_PRINTF(2, 3);
|
||||
inline void CreatePacket(WorldPacket& data, const char* m);
|
||||
inline void SendPacketTo(const Player* p, const char* m);
|
||||
inline void SendPacketTo(const Map* map, const char* m);
|
||||
inline void HandleResetCommand(Player* p);
|
||||
inline bool ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove);
|
||||
|
||||
template<class T> inline void SendCommand_String(T* p, ObjectGuid targetGUID, const char* prefix, const std::string& c);
|
||||
template<class T> inline void SendCommand_UInt32Value(T* o, ObjectGuid targetGUID, const char* prefix, uint32 t);
|
||||
template<class T> inline void SendCommand_GUID(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid t);
|
||||
template<class T> inline void SendCommand_Spell(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, int32 casttime);
|
||||
template<class T> inline void SendCommand_Cooldown(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur);
|
||||
template<class T> inline void SendCommand_Aura(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove);
|
||||
|
||||
bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args);
|
||||
bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args);
|
||||
|
||||
// definitions below:
|
||||
|
||||
template<class T>
|
||||
void SendCommand(T* o, const char* format, ...)
|
||||
{
|
||||
if (!format)
|
||||
return;
|
||||
char buffer[SPECTATOR_BUFFER_LEN];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vsnprintf(buffer, SPECTATOR_BUFFER_LEN, format, ap);
|
||||
va_end(ap);
|
||||
SendPacketTo(o, buffer);
|
||||
}
|
||||
AC_GAME_API void SendPacketTo(const T* object, std::string&& message);
|
||||
|
||||
void CreatePacket(WorldPacket& data, const char* m)
|
||||
template<class T, typename Format, typename... Args>
|
||||
inline void SendCommand(T* o, Format&& fmt, Args&& ... args)
|
||||
{
|
||||
size_t len = strlen(m);
|
||||
data.Initialize(SMSG_MESSAGECHAT, 1 + 4 + 8 + 4 + 8 + 4 + 1 + len + 1);
|
||||
data << uint8(CHAT_MSG_WHISPER);
|
||||
data << uint32(LANG_ADDON);
|
||||
data << uint64(0);
|
||||
data << uint32(0);
|
||||
data << uint64(0);
|
||||
data << uint32(len + 1);
|
||||
data << m;
|
||||
data << uint8(0);
|
||||
}
|
||||
|
||||
void SendPacketTo(const Player* p, const char* m)
|
||||
{
|
||||
WorldPacket data;
|
||||
CreatePacket(data, m);
|
||||
p->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void SendPacketTo(const Map* map, const char* m)
|
||||
{
|
||||
if (!map->IsBattleArena())
|
||||
return;
|
||||
Battleground* bg = ((BattlegroundMap*)map)->GetBG();
|
||||
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
|
||||
return;
|
||||
WorldPacket data;
|
||||
CreatePacket(data, m);
|
||||
bg->SpectatorsSendPacket(data);
|
||||
SendPacketTo(o, Acore::StringFormat(std::forward<Format>(fmt), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SendCommand_String(T* o, ObjectGuid targetGUID, const char* prefix, const char* c)
|
||||
inline void SendCommand_String(T* o, ObjectGuid targetGUID, const char* prefix, const char* c)
|
||||
{
|
||||
if (!targetGUID.IsPlayer())
|
||||
return;
|
||||
SendCommand(o, "%s0x%016llX;%s=%s;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, c);
|
||||
|
||||
SendCommand(o, "%s0x%016llX;%s=%s;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, c);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SendCommand_UInt32Value(T* o, ObjectGuid targetGUID, const char* prefix, uint32 t)
|
||||
inline void SendCommand_UInt32Value(T* o, ObjectGuid targetGUID, const char* prefix, uint32 t)
|
||||
{
|
||||
if (!targetGUID.IsPlayer())
|
||||
return;
|
||||
SendCommand(o, "%s0x%016llX;%s=%u;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, t);
|
||||
|
||||
SendCommand(o, "%s0x%016llX;%s=%u;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, t);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SendCommand_GUID(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid t)
|
||||
inline void SendCommand_GUID(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid t)
|
||||
{
|
||||
if (!targetGUID.IsPlayer())
|
||||
return;
|
||||
SendCommand(o, "%s0x%016llX;%s=0x%016llX;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, (unsigned long long)t.GetRawValue());
|
||||
|
||||
SendCommand(o, "%s0x%016llX;%s=0x%016llX;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, t.GetRawValue());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SendCommand_Spell(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, int32 casttime)
|
||||
inline void SendCommand_Spell(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, int32 casttime)
|
||||
{
|
||||
if (!targetGUID.IsPlayer())
|
||||
return;
|
||||
SendCommand(o, "%s0x%016llX;%s=%u,%i;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, id, casttime);
|
||||
|
||||
SendCommand(o, "%s0x%016llX;%s=%u,%i;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, id, casttime);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SendCommand_Cooldown(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur)
|
||||
inline void SendCommand_Cooldown(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur)
|
||||
{
|
||||
if (!targetGUID.IsPlayer())
|
||||
return;
|
||||
|
||||
if (const SpellInfo* si = sSpellMgr->GetSpellInfo(id))
|
||||
if (si->SpellIconID == 1)
|
||||
return;
|
||||
SendCommand(o, "%s0x%016llX;%s=%u,%u,%u;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, id, dur, maxdur);
|
||||
|
||||
SendCommand(o, "%s0x%016llX;%s=%u,%u,%u;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, id, dur, maxdur);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SendCommand_Aura(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove)
|
||||
inline void SendCommand_Aura(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove)
|
||||
{
|
||||
if (!targetGUID.IsPlayer())
|
||||
return;
|
||||
SendCommand(o, "%s0x%016llX;%s=%u,%u,%i,%i,%u,%u,%u,0x%016llX;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, remove ? 1 : 0, stack, dur, maxdur, id, dispel, isDebuff ? 1 : 0, (unsigned long long)caster.GetRawValue());
|
||||
|
||||
SendCommand(o, "%s0x%016llX;%s=%u,%u,%i,%i,%u,%u,%u,0x%016llX;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, remove ? 1 : 0, stack, dur, maxdur, id, dispel, isDebuff ? 1 : 0, caster.GetRawValue());
|
||||
}
|
||||
|
||||
void HandleResetCommand(Player* p)
|
||||
{
|
||||
if (!p->FindMap() || !p->IsInWorld() || !p->FindMap()->IsBattleArena())
|
||||
return;
|
||||
Battleground* bg = ((BattlegroundMap*)p->FindMap())->GetBG();
|
||||
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
|
||||
return;
|
||||
Battleground::BattlegroundPlayerMap const& pl = bg->GetPlayers();
|
||||
for (Battleground::BattlegroundPlayerMap::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
|
||||
{
|
||||
if (p->HasReceivedSpectatorResetFor(itr->first))
|
||||
continue;
|
||||
|
||||
Player* plr = itr->second;
|
||||
p->AddReceivedSpectatorResetFor(itr->first);
|
||||
|
||||
SendCommand_String(p, itr->first, "NME", plr->GetName().c_str());
|
||||
// Xinef: addon compatibility
|
||||
SendCommand_UInt32Value(p, itr->first, "TEM", plr->GetBgTeamId() == TEAM_ALLIANCE ? ALLIANCE : HORDE);
|
||||
SendCommand_UInt32Value(p, itr->first, "CLA", plr->getClass());
|
||||
SendCommand_UInt32Value(p, itr->first, "MHP", plr->GetMaxHealth());
|
||||
SendCommand_UInt32Value(p, itr->first, "CHP", plr->GetHealth());
|
||||
SendCommand_UInt32Value(p, itr->first, "STA", plr->IsAlive() ? 1 : 0);
|
||||
Powers ptype = plr->getPowerType();
|
||||
SendCommand_UInt32Value(p, itr->first, "PWT", ptype);
|
||||
SendCommand_UInt32Value(p, itr->first, "MPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetMaxPower(ptype) / 10 : plr->GetMaxPower(ptype));
|
||||
SendCommand_UInt32Value(p, itr->first, "CPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetPower(ptype) / 10 : plr->GetPower(ptype));
|
||||
Pet* pet = plr->GetPet();
|
||||
SendCommand_UInt32Value(p, itr->first, "PHP", pet && pet->GetCreatureTemplate()->family ? (uint32)pet->GetHealthPct() : 0);
|
||||
SendCommand_UInt32Value(p, itr->first, "PET", pet ? pet->GetCreatureTemplate()->family : 0);
|
||||
SendCommand_GUID(p, itr->first, "TRG", plr->GetTarget());
|
||||
SendCommand_UInt32Value(p, itr->first, "RES", 1);
|
||||
SendCommand_UInt32Value(p, itr->first, "CDC", 1);
|
||||
SendCommand_UInt32Value(p, itr->first, "TIM", (bg->GetStartTime() < 46 * MINUTE * IN_MILLISECONDS) ? (46 * MINUTE * IN_MILLISECONDS - bg->GetStartTime()) / IN_MILLISECONDS : 0);
|
||||
// "SPE" not here (only possible to send starting a new cast)
|
||||
|
||||
// send all "CD"
|
||||
SpellCooldowns const& sc = plr->GetSpellCooldownMap();
|
||||
for (SpellCooldowns::const_iterator itrc = sc.begin(); itrc != sc.end(); ++itrc)
|
||||
if (itrc->second.sendToSpectator && itrc->second.maxduration >= SPECTATOR_COOLDOWN_MIN * IN_MILLISECONDS && itrc->second.maxduration <= SPECTATOR_COOLDOWN_MAX * IN_MILLISECONDS)
|
||||
if (uint32 cd = (getMSTimeDiff(World::GetGameTimeMS(), itrc->second.end) / 1000))
|
||||
SendCommand_Cooldown(p, itr->first, "ACD", itrc->first, cd, itrc->second.maxduration / 1000);
|
||||
|
||||
// send all visible "AUR"
|
||||
Unit::VisibleAuraMap const* visibleAuras = plr->GetVisibleAuras();
|
||||
for (Unit::VisibleAuraMap::const_iterator aitr = visibleAuras->begin(); aitr != visibleAuras->end(); ++aitr)
|
||||
{
|
||||
Aura* aura = aitr->second->GetBase();
|
||||
if (ShouldSendAura(aura, aitr->second->GetEffectMask(), plr->GetGUID(), false))
|
||||
SendCommand_Aura(p, itr->first, "AUR", aura->GetCasterGUID(), aura->GetSpellInfo()->Id, aura->GetSpellInfo()->IsPositive(), aura->GetSpellInfo()->Dispel, aura->GetDuration(), aura->GetMaxDuration(), (aura->GetCharges() > 1 ? aura->GetCharges() : aura->GetStackAmount()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove)
|
||||
{
|
||||
if (aura->GetSpellInfo()->SpellIconID == 1 || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR1_NO_AURA_ICON))
|
||||
return false;
|
||||
|
||||
if (remove || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_AURA_CC) || aura->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_GENERIC)
|
||||
return true;
|
||||
|
||||
for(uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (effMask & (1 << i))
|
||||
{
|
||||
AuraType at = aura->GetEffect(i)->GetAuraType();
|
||||
if ((aura->GetEffect(i)->GetAmount() && (aura->GetSpellInfo()->IsPositive() || targetGUID != aura->GetCasterGUID())) ||
|
||||
at == SPELL_AURA_MECHANIC_IMMUNITY || at == SPELL_AURA_EFFECT_IMMUNITY || at == SPELL_AURA_STATE_IMMUNITY || at == SPELL_AURA_SCHOOL_IMMUNITY || at == SPELL_AURA_DISPEL_IMMUNITY)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
AC_GAME_API bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args);
|
||||
AC_GAME_API bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args);
|
||||
AC_GAME_API void CreatePacket(WorldPacket& data, std::string const& message);
|
||||
AC_GAME_API void HandleResetCommand(Player* player);
|
||||
AC_GAME_API bool ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "Pet.h"
|
||||
#include "ArenaSpectator.h"
|
||||
#include "Battleground.h"
|
||||
#include "Common.h"
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
#include "InstanceScript.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellAuras.h"
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@
|
|||
#include "QuestDef.h"
|
||||
#include "QueryHolder.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "revision.h"
|
||||
#include "Realm.h"
|
||||
#include "SavingSystem.h"
|
||||
#include "ScriptMgr.h"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include "Player.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "revision.h"
|
||||
#include "GitRevision.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
|
||||
|
|
@ -559,8 +559,8 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
|
|||
auto stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK);
|
||||
stmt->setUInt32(0, quest_id);
|
||||
stmt->setUInt32(1, GetGUID().GetCounter());
|
||||
stmt->setString(2, _HASH);
|
||||
stmt->setString(3, _DATE);
|
||||
stmt->setString(2, GitRevision::GetHash());
|
||||
stmt->setString(3, GitRevision::GetDate());
|
||||
|
||||
// add to Quest Tracker
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
|
|
|||
|
|
@ -3884,6 +3884,7 @@ void Unit::HandleSafeUnitPointersOnDelete(Unit* thisUnit)
|
|||
return;
|
||||
for (std::set<SafeUnitPointer*>::iterator itr = thisUnit->SafeUnitPointerSet.begin(); itr != thisUnit->SafeUnitPointerSet.end(); ++itr)
|
||||
(*itr)->UnitDeleted();
|
||||
|
||||
thisUnit->SafeUnitPointerSet.clear();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
//add here most rarely modified headers to speed up debug build compilation
|
||||
|
||||
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
|
||||
|
||||
#include "Common.h"
|
||||
#include "Log.h"
|
||||
#include "MapManager.h"
|
||||
|
|
@ -14,3 +12,4 @@
|
|||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "WorldSocket.h"
|
||||
|
|
|
|||
|
|
@ -345,18 +345,18 @@ void ScriptMgr::OnNetworkStop()
|
|||
FOREACH_SCRIPT(ServerScript)->OnNetworkStop();
|
||||
}
|
||||
|
||||
void ScriptMgr::OnSocketOpen(WorldSocket* socket)
|
||||
void ScriptMgr::OnSocketOpen(std::shared_ptr<WorldSocket> socket)
|
||||
{
|
||||
ASSERT(socket);
|
||||
|
||||
FOREACH_SCRIPT(ServerScript)->OnSocketOpen(socket);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnSocketClose(WorldSocket* socket, bool wasNew)
|
||||
void ScriptMgr::OnSocketClose(std::shared_ptr<WorldSocket> socket)
|
||||
{
|
||||
ASSERT(socket);
|
||||
|
||||
FOREACH_SCRIPT(ServerScript)->OnSocketClose(socket, wasNew);
|
||||
FOREACH_SCRIPT(ServerScript)->OnSocketClose(socket);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnPacketReceive(WorldSession* session, WorldPacket const& packet)
|
||||
|
|
|
|||
|
|
@ -144,11 +144,11 @@ public:
|
|||
virtual void OnNetworkStop() { }
|
||||
|
||||
// Called when a remote socket establishes a connection to the server. Do not store the socket object.
|
||||
virtual void OnSocketOpen(WorldSocket* /*socket*/) { }
|
||||
virtual void OnSocketOpen(std::shared_ptr<WorldSocket> /*socket*/) { }
|
||||
|
||||
// Called when a socket is closed. Do not store the socket object, and do not rely on the connection
|
||||
// being open; it is not.
|
||||
virtual void OnSocketClose(WorldSocket* /*socket*/, bool /*wasNew*/) { }
|
||||
virtual void OnSocketClose(std::shared_ptr<WorldSocket> /*socket*/) { }
|
||||
|
||||
// Called when a packet is sent to a client. The packet object is a copy of the original packet, so reading
|
||||
// and modifying it is safe.
|
||||
|
|
@ -1456,8 +1456,8 @@ public: /* SpellScriptLoader */
|
|||
public: /* ServerScript */
|
||||
void OnNetworkStart();
|
||||
void OnNetworkStop();
|
||||
void OnSocketOpen(WorldSocket* socket);
|
||||
void OnSocketClose(WorldSocket* socket, bool wasNew);
|
||||
void OnSocketOpen(std::shared_ptr<WorldSocket> socket);
|
||||
void OnSocketClose(std::shared_ptr<WorldSocket> socket);
|
||||
void OnPacketReceive(WorldSession* session, WorldPacket const& packet);
|
||||
void OnPacketSend(WorldSession* session, WorldPacket const& packet);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ bool WorldPackets::Strings::Utf8::Validate(std::string const& value)
|
|||
|
||||
//bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value)
|
||||
//{
|
||||
// if (!Warhead::Hyperlinks::CheckAllLinks(value))
|
||||
// if (!Acore::Hyperlinks::CheckAllLinks(value))
|
||||
// throw InvalidHyperlinkException(value);
|
||||
//
|
||||
// return true;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "PacketLog.h"
|
||||
#include "Config.h"
|
||||
#include "IpAddress.h"
|
||||
#include "Timer.h"
|
||||
#include "WorldPacket.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// Packet logging structures in PKT 3.1 format
|
||||
// Packet logging structures in PKT 3.1 format
|
||||
struct LogHeader
|
||||
{
|
||||
char Signature[3];
|
||||
|
|
@ -27,11 +28,19 @@ struct LogHeader
|
|||
|
||||
struct PacketHeader
|
||||
{
|
||||
char Direction[4];
|
||||
// used to uniquely identify a connection
|
||||
struct OptionalData
|
||||
{
|
||||
uint8 SocketIPBytes[16];
|
||||
uint32 SocketPort;
|
||||
};
|
||||
|
||||
uint32 Direction;
|
||||
uint32 ConnectionId;
|
||||
uint32 ArrivalTicks;
|
||||
uint32 OptionalDataSize;
|
||||
uint32 Length;
|
||||
OptionalData OptionalData;
|
||||
uint32 Opcode;
|
||||
};
|
||||
|
||||
|
|
@ -45,7 +54,9 @@ PacketLog::PacketLog() : _file(nullptr)
|
|||
PacketLog::~PacketLog()
|
||||
{
|
||||
if (_file)
|
||||
{
|
||||
fclose(_file);
|
||||
}
|
||||
|
||||
_file = nullptr;
|
||||
}
|
||||
|
|
@ -60,9 +71,10 @@ void PacketLog::Initialize()
|
|||
{
|
||||
std::string logsDir = sConfigMgr->GetOption<std::string>("LogsDir", "");
|
||||
|
||||
if (!logsDir.empty())
|
||||
if ((logsDir.at(logsDir.length() - 1) != '/') && (logsDir.at(logsDir.length() - 1) != '\\'))
|
||||
logsDir.push_back('/');
|
||||
if (!logsDir.empty() && (logsDir.at(logsDir.length() - 1) != '/') && (logsDir.at(logsDir.length() - 1) != '\\'))
|
||||
{
|
||||
logsDir.push_back('/');
|
||||
}
|
||||
|
||||
std::string logname = sConfigMgr->GetOption<std::string>("PacketLogFile", "");
|
||||
if (!logname.empty())
|
||||
|
|
@ -80,23 +92,42 @@ void PacketLog::Initialize()
|
|||
header.SniffStartTicks = getMSTime();
|
||||
header.OptionalDataSize = 0;
|
||||
|
||||
fwrite(&header, sizeof(header), 1, _file);
|
||||
if (CanLogPacket())
|
||||
{
|
||||
fwrite(&header, sizeof(header), 1, _file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PacketLog::LogPacket(WorldPacket const& packet, Direction direction)
|
||||
void PacketLog::LogPacket(WorldPacket const& packet, Direction direction, boost::asio::ip::address const& addr, uint16 port)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_logPacketLock);
|
||||
|
||||
PacketHeader header;
|
||||
*reinterpret_cast<uint32*>(header.Direction) = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
|
||||
header.Direction = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
|
||||
header.ConnectionId = 0;
|
||||
header.ArrivalTicks = getMSTime();
|
||||
header.OptionalDataSize = 0;
|
||||
header.Length = packet.size() + 4;
|
||||
|
||||
header.OptionalDataSize = sizeof(header.OptionalData);
|
||||
memset(header.OptionalData.SocketIPBytes, 0, sizeof(header.OptionalData.SocketIPBytes));
|
||||
|
||||
if (addr.is_v4())
|
||||
{
|
||||
auto bytes = addr.to_v4().to_bytes();
|
||||
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
|
||||
}
|
||||
else if (addr.is_v6())
|
||||
{
|
||||
auto bytes = addr.to_v6().to_bytes();
|
||||
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
header.OptionalData.SocketPort = port;
|
||||
header.Length = packet.size() + sizeof(header.Opcode);
|
||||
header.Opcode = packet.GetOpcode();
|
||||
|
||||
fwrite(&header, sizeof(header), 1, _file);
|
||||
|
||||
if (!packet.empty())
|
||||
{
|
||||
fwrite(packet.contents(), 1, packet.size(), _file);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define ACORE_PACKETLOG_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <mutex>
|
||||
|
||||
enum Direction
|
||||
|
|
@ -18,7 +19,7 @@ enum Direction
|
|||
|
||||
class WorldPacket;
|
||||
|
||||
class PacketLog
|
||||
class AC_GAME_API PacketLog
|
||||
{
|
||||
private:
|
||||
PacketLog();
|
||||
|
|
@ -31,7 +32,7 @@ public:
|
|||
|
||||
void Initialize();
|
||||
bool CanLogPacket() const { return (_file != nullptr); }
|
||||
void LogPacket(WorldPacket const& packet, Direction direction);
|
||||
void LogPacket(WorldPacket const& packet, Direction direction, boost::asio::ip::address const& addr, uint16 port);
|
||||
|
||||
private:
|
||||
FILE* _file;
|
||||
|
|
|
|||
50
src/server/game/Server/Protocol/ServerPktHeader.h
Normal file
50
src/server/game/Server/Protocol/ServerPktHeader.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#ifndef __SERVERPKTHDR_H__
|
||||
#define __SERVERPKTHDR_H__
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ServerPktHeader
|
||||
{
|
||||
/**
|
||||
* size is the length of the payload _plus_ the length of the opcode
|
||||
*/
|
||||
ServerPktHeader(uint32 size, uint16 cmd) : size(size)
|
||||
{
|
||||
uint8 headerIndex=0;
|
||||
if (isLargePacket())
|
||||
{
|
||||
LOG_DEBUG("network", "initializing large server to client packet. Size: %u, cmd: %u", size, cmd);
|
||||
header[headerIndex++] = 0x80 | (0xFF & (size >> 16));
|
||||
}
|
||||
header[headerIndex++] = 0xFF &(size >> 8);
|
||||
header[headerIndex++] = 0xFF & size;
|
||||
|
||||
header[headerIndex++] = 0xFF & cmd;
|
||||
header[headerIndex++] = 0xFF & (cmd >> 8);
|
||||
}
|
||||
|
||||
uint8 getHeaderLength()
|
||||
{
|
||||
// cmd = 2 bytes, size= 2||3bytes
|
||||
return 2 + (isLargePacket() ? 3 : 2);
|
||||
}
|
||||
|
||||
bool isLargePacket() const
|
||||
{
|
||||
return size > 0x7FFF;
|
||||
}
|
||||
|
||||
const uint32 size;
|
||||
uint8 header[5];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
\ingroup u2w
|
||||
*/
|
||||
|
||||
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
|
||||
#include "WorldSession.h"
|
||||
#include "AccountMgr.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "Common.h"
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
#include "PacketUtilities.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "QueryHolder.h"
|
||||
#include "SavingSystem.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SocialMgr.h"
|
||||
|
|
@ -34,9 +35,8 @@
|
|||
#include "WardenWin.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "QueryHolder.h"
|
||||
#include "zlib.h"
|
||||
#include "WorldSocket.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#ifdef ELUNA
|
||||
#include "LuaEngine.h"
|
||||
|
|
@ -91,7 +91,8 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
|
|||
}
|
||||
|
||||
/// WorldSession constructor
|
||||
WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
|
||||
WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion,
|
||||
time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
|
||||
m_muteTime(mute_time),
|
||||
m_timeOutTime(0),
|
||||
_lastAuctionListItemsMSTime(0),
|
||||
|
|
@ -103,6 +104,7 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
|
|||
_security(sec),
|
||||
_skipQueue(skipQueue),
|
||||
_accountId(id),
|
||||
_accountName(std::move(name)),
|
||||
m_expansion(expansion),
|
||||
m_total_time(TotalTime),
|
||||
_logoutTime(0),
|
||||
|
|
@ -125,7 +127,6 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
|
|||
{
|
||||
memset(m_Tutorials, 0, sizeof(m_Tutorials));
|
||||
|
||||
_warden = nullptr;
|
||||
_offlineTime = 0;
|
||||
_kicked = false;
|
||||
_shouldSetOfflineInDB = true;
|
||||
|
|
@ -135,10 +136,9 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
|
|||
|
||||
if (sock)
|
||||
{
|
||||
m_Address = sock->GetRemoteAddress();
|
||||
sock->AddReference();
|
||||
m_Address = sock->GetRemoteIpAddress().to_string();
|
||||
ResetTimeOutTime(false);
|
||||
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId());
|
||||
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -154,17 +154,10 @@ WorldSession::~WorldSession()
|
|||
/// - If have unclosed socket, close it
|
||||
if (m_Socket)
|
||||
{
|
||||
m_Socket->CloseSocket("WorldSession destructor");
|
||||
m_Socket->RemoveReference();
|
||||
m_Socket->CloseSocket();
|
||||
m_Socket = nullptr;
|
||||
}
|
||||
|
||||
if (_warden)
|
||||
{
|
||||
delete _warden;
|
||||
_warden = nullptr;
|
||||
}
|
||||
|
||||
///- empty incoming packet queue
|
||||
WorldPacket* packet = nullptr;
|
||||
while (_recvQueue.next(packet))
|
||||
|
|
@ -257,9 +250,7 @@ void WorldSession::SendPacket(WorldPacket const* packet)
|
|||
#endif
|
||||
|
||||
LOG_TRACE("network.opcode", "S->C: %s %s", GetPlayerInfo().c_str(), GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet->GetOpcode())).c_str());
|
||||
|
||||
if (m_Socket->SendPacket(*packet) == -1)
|
||||
m_Socket->CloseSocket("m_Socket->SendPacket(*packet) == -1");
|
||||
m_Socket->SendPacket(*packet);
|
||||
}
|
||||
|
||||
/// Add an incoming packet to the queue
|
||||
|
|
@ -290,26 +281,28 @@ void WorldSession::LogUnprocessedTail(WorldPacket* packet)
|
|||
/// Update the WorldSession (triggered by World update)
|
||||
bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
||||
{
|
||||
if (updater.ProcessUnsafe())
|
||||
{
|
||||
UpdateTimeOutTime(diff);
|
||||
///- Before we process anything:
|
||||
/// If necessary, kick the player because the client didn't send anything for too long
|
||||
/// (or they've been idling in character select)
|
||||
if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket)
|
||||
m_Socket->CloseSocket();
|
||||
|
||||
/// If necessary, kick the player because the client didn't send anything for too long
|
||||
/// (or they've been idling in character select)
|
||||
if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket)
|
||||
m_Socket->CloseSocket("Client didn't send anything for too long");
|
||||
}
|
||||
if (updater.ProcessUnsafe())
|
||||
UpdateTimeOutTime(diff);
|
||||
|
||||
HandleTeleportTimeout(updater.ProcessUnsafe());
|
||||
|
||||
uint32 _startMSTime = getMSTime();
|
||||
///- Retrieve packets from the receive queue and call the appropriate handlers
|
||||
/// not process packets if socket already closed
|
||||
WorldPacket* packet = nullptr;
|
||||
|
||||
//! Delete packet after processing by default
|
||||
bool deletePacket = true;
|
||||
WorldPacket* firstDelayedPacket = nullptr;
|
||||
std::vector<WorldPacket*> requeuePackets;
|
||||
uint32 processedPackets = 0;
|
||||
time_t currentTime = time(nullptr);
|
||||
|
||||
while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater))
|
||||
while (m_Socket && _recvQueue.next(packet, updater))
|
||||
{
|
||||
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
|
||||
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
|
||||
|
|
@ -412,38 +405,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
|||
//Any leftover will be processed in next update
|
||||
if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE)
|
||||
break;
|
||||
|
||||
if (getMSTimeDiff(_startMSTime, getMSTime()) >= 3) // limit (by time) packets processed in one update, prevent DDoS
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_Socket && !m_Socket->IsClosed())
|
||||
ProcessQueryCallbacks();
|
||||
|
||||
if (updater.ProcessUnsafe())
|
||||
{
|
||||
if (m_Socket && !m_Socket->IsClosed() && _warden)
|
||||
{
|
||||
_warden->Update(diff);
|
||||
}
|
||||
|
||||
time_t currTime = time(nullptr);
|
||||
if (ShouldLogOut(currTime) && !m_playerLoading)
|
||||
{
|
||||
LogoutPlayer(true);
|
||||
}
|
||||
|
||||
if (m_Socket && m_Socket->IsClosed())
|
||||
{
|
||||
m_Socket->RemoveReference();
|
||||
m_Socket = nullptr;
|
||||
}
|
||||
|
||||
if (!m_Socket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
|
||||
|
||||
if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter
|
||||
{
|
||||
|
|
@ -461,29 +425,60 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
|||
}
|
||||
}
|
||||
|
||||
ProcessQueryCallbacks();
|
||||
|
||||
//check if we are safe to proceed with logout
|
||||
//logout procedure should happen only in World::UpdateSessions() method!!!
|
||||
if (updater.ProcessUnsafe())
|
||||
{
|
||||
if (m_Socket && m_Socket->IsOpen() && _warden)
|
||||
{
|
||||
_warden->Update(diff);
|
||||
}
|
||||
|
||||
if (ShouldLogOut(currentTime) && !m_playerLoading)
|
||||
{
|
||||
LogoutPlayer(true);
|
||||
}
|
||||
|
||||
if (m_Socket && !m_Socket->IsOpen())
|
||||
{
|
||||
if (GetPlayer() && _warden)
|
||||
_warden->Update(diff);
|
||||
|
||||
m_Socket = nullptr;
|
||||
}
|
||||
|
||||
if (!m_Socket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldSession::HandleSocketClosed()
|
||||
{
|
||||
if (m_Socket && m_Socket->IsClosed() && !IsKicked() && GetPlayer() && !PlayerLogout() && GetPlayer()->m_taxi.empty() && GetPlayer()->IsInWorld() && !World::IsStopped())
|
||||
if (m_Socket && !m_Socket->IsOpen() && !IsKicked() && GetPlayer() && !PlayerLogout() && GetPlayer()->m_taxi.empty() && GetPlayer()->IsInWorld() && !World::IsStopped())
|
||||
{
|
||||
m_Socket->RemoveReference();
|
||||
m_Socket = nullptr;
|
||||
GetPlayer()->TradeCancel(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WorldSession::IsSocketClosed() const {
|
||||
return !m_Socket || m_Socket->IsClosed();
|
||||
bool WorldSession::IsSocketClosed() const
|
||||
{
|
||||
return !m_Socket || !m_Socket->IsOpen();
|
||||
}
|
||||
|
||||
void WorldSession::HandleTeleportTimeout(bool updateInSessions)
|
||||
{
|
||||
// pussywizard: handle teleport ack timeout
|
||||
if (m_Socket && !m_Socket->IsClosed() && GetPlayer() && GetPlayer()->IsBeingTeleported())
|
||||
if (m_Socket && m_Socket->IsOpen() && GetPlayer() && GetPlayer()->IsBeingTeleported())
|
||||
{
|
||||
time_t currTime = time(nullptr);
|
||||
if (updateInSessions) // session update from World::UpdateSessions
|
||||
|
|
@ -681,7 +676,12 @@ void WorldSession::LogoutPlayer(bool save)
|
|||
void WorldSession::KickPlayer(std::string const& reason, bool setKicked)
|
||||
{
|
||||
if (m_Socket)
|
||||
m_Socket->CloseSocket(reason);
|
||||
{
|
||||
LOG_INFO("network.kick", "Account: %u Character: '%s' %s kicked with reason: %s", GetAccountId(), _player ? _player->GetName().c_str() : "<none>",
|
||||
_player ? _player->GetGUID().ToString().c_str() : "", reason.c_str());
|
||||
|
||||
m_Socket->CloseSocket();
|
||||
}
|
||||
|
||||
if (setKicked)
|
||||
SetKicked(true); // pussywizard: the session won't be left ingame for 60 seconds and to also kick offline session
|
||||
|
|
@ -947,41 +947,41 @@ void WorldSession::ReadMovementInfo(WorldPacket& data, MovementInfo* mi)
|
|||
It will freeze clients that receive this player's movement info.
|
||||
*/
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
|
||||
MOVEMENTFLAG_ROOT);
|
||||
MOVEMENTFLAG_ROOT);
|
||||
|
||||
//! Cannot hover without SPELL_AURA_HOVER
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetPlayer()->HasAuraType(SPELL_AURA_HOVER),
|
||||
MOVEMENTFLAG_HOVER);
|
||||
MOVEMENTFLAG_HOVER);
|
||||
|
||||
//! Cannot ascend and descend at the same time
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ASCENDING) && mi->HasMovementFlag(MOVEMENTFLAG_DESCENDING),
|
||||
MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING);
|
||||
MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING);
|
||||
|
||||
//! Cannot move left and right at the same time
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_RIGHT),
|
||||
MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT);
|
||||
MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT);
|
||||
|
||||
//! Cannot strafe left and right at the same time
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_RIGHT),
|
||||
MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
|
||||
MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
|
||||
|
||||
//! Cannot pitch up and down at the same time
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_PITCH_UP) && mi->HasMovementFlag(MOVEMENTFLAG_PITCH_DOWN),
|
||||
MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN);
|
||||
MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN);
|
||||
|
||||
//! Cannot move forwards and backwards at the same time
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FORWARD) && mi->HasMovementFlag(MOVEMENTFLAG_BACKWARD),
|
||||
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
|
||||
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
|
||||
|
||||
//! Cannot walk on water without SPELL_AURA_WATER_WALK
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) &&
|
||||
!GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) &&
|
||||
!GetPlayer()->HasAuraType(SPELL_AURA_GHOST),
|
||||
MOVEMENTFLAG_WATERWALKING);
|
||||
!GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) &&
|
||||
!GetPlayer()->HasAuraType(SPELL_AURA_GHOST),
|
||||
MOVEMENTFLAG_WATERWALKING);
|
||||
|
||||
//! Cannot feather fall without SPELL_AURA_FEATHER_FALL
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FALLING_SLOW) && !GetPlayer()->HasAuraType(SPELL_AURA_FEATHER_FALL),
|
||||
MOVEMENTFLAG_FALLING_SLOW);
|
||||
MOVEMENTFLAG_FALLING_SLOW);
|
||||
|
||||
/*! Cannot fly if no fly auras present. Exception is being a GM.
|
||||
Note that we check for account level instead of Player::IsGameMaster() because in some
|
||||
|
|
@ -990,14 +990,14 @@ void WorldSession::ReadMovementInfo(WorldPacket& data, MovementInfo* mi)
|
|||
*/
|
||||
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY) && GetSecurity() == SEC_PLAYER && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_FLY) && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED),
|
||||
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY);
|
||||
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY);
|
||||
|
||||
//! Cannot fly and fall at the same time
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY) && mi->HasMovementFlag(MOVEMENTFLAG_FALLING),
|
||||
MOVEMENTFLAG_FALLING);
|
||||
MOVEMENTFLAG_FALLING);
|
||||
|
||||
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE_ENABLED) &&
|
||||
(!GetPlayer()->movespline->Initialized() || GetPlayer()->movespline->Finalized()), MOVEMENTFLAG_SPLINE_ENABLED);
|
||||
(!GetPlayer()->movespline->Initialized() || GetPlayer()->movespline->Finalized()), MOVEMENTFLAG_SPLINE_ENABLED);
|
||||
|
||||
#undef REMOVE_VIOLATING_FLAGS
|
||||
}
|
||||
|
|
@ -1040,7 +1040,7 @@ void WorldSession::WriteMovementInfo(WorldPacket* data, MovementInfo* mi)
|
|||
*data << mi->splineElevation;
|
||||
}
|
||||
|
||||
void WorldSession::ReadAddonsInfo(WorldPacket& data)
|
||||
void WorldSession::ReadAddonsInfo(ByteBuffer& data)
|
||||
{
|
||||
if (data.rpos() + 4 > data.size())
|
||||
return;
|
||||
|
|
@ -1218,7 +1218,7 @@ void WorldSession::InitWarden(SessionKey const& k, std::string const& os)
|
|||
{
|
||||
if (os == "Win")
|
||||
{
|
||||
_warden = new WardenWin();
|
||||
_warden = std::make_unique<WardenWin>();
|
||||
_warden->Init(this, k);
|
||||
}
|
||||
else if (os == "OSX")
|
||||
|
|
@ -1533,6 +1533,9 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co
|
|||
return maxPacketCounterAllowed;
|
||||
}
|
||||
|
||||
WorldSession::DosProtection::DosProtection(WorldSession* s) :
|
||||
Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { }
|
||||
|
||||
void WorldSession::ResetTimeSync()
|
||||
{
|
||||
_timeSyncNextCounter = 0;
|
||||
|
|
|
|||
|
|
@ -230,14 +230,14 @@ struct PacketCounter
|
|||
class WorldSession
|
||||
{
|
||||
public:
|
||||
WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime);
|
||||
WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime);
|
||||
~WorldSession();
|
||||
|
||||
bool PlayerLoading() const { return m_playerLoading; }
|
||||
bool PlayerLogout() const { return m_playerLogout; }
|
||||
bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
|
||||
|
||||
void ReadAddonsInfo(WorldPacket& data);
|
||||
void ReadAddonsInfo(ByteBuffer& data);
|
||||
void SendAddonsInfo();
|
||||
|
||||
void ReadMovementInfo(WorldPacket& data, MovementInfo* mi);
|
||||
|
|
@ -987,7 +987,7 @@ protected:
|
|||
{
|
||||
friend class World;
|
||||
public:
|
||||
DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { }
|
||||
DosProtection(WorldSession* s);
|
||||
bool EvaluateOpcode(WorldPacket& p, time_t time) const;
|
||||
protected:
|
||||
enum Policy
|
||||
|
|
@ -1035,20 +1035,20 @@ private:
|
|||
|
||||
ObjectGuid::LowType m_GUIDLow;
|
||||
Player* _player;
|
||||
WorldSocket* m_Socket;
|
||||
std::shared_ptr<WorldSocket> m_Socket;
|
||||
std::string m_Address;
|
||||
// std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session!
|
||||
|
||||
AccountTypes _security;
|
||||
bool _skipQueue;
|
||||
uint32 _accountId;
|
||||
std::string _accountName;
|
||||
uint8 m_expansion;
|
||||
uint32 m_total_time;
|
||||
|
||||
typedef std::list<AddonInfo> AddonsList;
|
||||
|
||||
// Warden
|
||||
Warden* _warden; // Remains nullptr if Warden system is not enabled by config
|
||||
std::unique_ptr<Warden> _warden; // Remains nullptr if Warden system is not enabled by config
|
||||
|
||||
time_t _logoutTime;
|
||||
bool m_inQueue; // session wait in auth.queue
|
||||
|
|
@ -1057,7 +1057,7 @@ private:
|
|||
bool m_playerSave;
|
||||
LocaleConstant m_sessionDbcLocale;
|
||||
LocaleConstant m_sessionDbLocaleIndex;
|
||||
uint32 m_latency;
|
||||
std::atomic<uint32> m_latency;
|
||||
AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES];
|
||||
uint32 m_Tutorials[MAX_ACCOUNT_TUTORIAL_VALUES];
|
||||
bool m_TutorialsChanged;
|
||||
|
|
@ -1081,6 +1081,9 @@ private:
|
|||
std::map<uint32, uint32> _pendingTimeSyncRequests; // key: counter. value: server time when packet with that counter was sent.
|
||||
uint32 _timeSyncNextCounter;
|
||||
uint32 _timeSyncTimer;
|
||||
|
||||
WorldSession(WorldSession const& right) = delete;
|
||||
WorldSession& operator=(WorldSession const& right) = delete;
|
||||
};
|
||||
#endif
|
||||
/// @}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,197 +4,121 @@
|
|||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/** \addtogroup u2w User to World Communication
|
||||
* @{
|
||||
* \file WorldSocket.h
|
||||
* \author Derex <derex101@gmail.com>
|
||||
*/
|
||||
#ifndef __WORLDSOCKET_H__
|
||||
#define __WORLDSOCKET_H__
|
||||
|
||||
#ifndef _WORLDSOCKET_H
|
||||
#define _WORLDSOCKET_H
|
||||
|
||||
#include "AuthCrypt.h"
|
||||
#include "Common.h"
|
||||
#include "Duration.h"
|
||||
#include <ace/Message_Block.h>
|
||||
#include <ace/SOCK_Stream.h>
|
||||
#include <ace/Svc_Handler.h>
|
||||
#include <ace/Synch_Traits.h>
|
||||
#include <ace/Unbounded_Queue.h>
|
||||
#include <mutex>
|
||||
#include "AuthCrypt.h"
|
||||
#include "ServerPktHeader.h"
|
||||
#include "Socket.h"
|
||||
#include "Util.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "MPSCQueue.h"
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#if !defined (ACE_LACKS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif /* ACE_LACKS_PRAGMA_ONCE */
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
class ACE_Message_Block;
|
||||
class WorldPacket;
|
||||
class WorldSession;
|
||||
class EncryptablePacket : public WorldPacket
|
||||
{
|
||||
public:
|
||||
EncryptablePacket(WorldPacket const& packet, bool encrypt) : WorldPacket(packet), _encrypt(encrypt)
|
||||
{
|
||||
SocketQueueLink.store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool NeedsEncryption() const { return _encrypt; }
|
||||
|
||||
std::atomic<EncryptablePacket*> SocketQueueLink;
|
||||
|
||||
private:
|
||||
bool _encrypt;
|
||||
};
|
||||
|
||||
namespace WorldPackets
|
||||
{
|
||||
class ServerPacket;
|
||||
}
|
||||
|
||||
/// Handler that can communicate over stream sockets.
|
||||
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
|
||||
|
||||
/**
|
||||
* WorldSocket.
|
||||
*
|
||||
* This class is responsible for the communication with
|
||||
* remote clients.
|
||||
* Most methods return -1 on failure.
|
||||
* The class uses reference counting.
|
||||
*
|
||||
* For output the class uses one buffer (64K usually) and
|
||||
* a queue where it stores packet if there is no place on
|
||||
* the queue. The reason this is done, is because the server
|
||||
* does really a lot of small-size writes to it, and it doesn't
|
||||
* scale well to allocate memory for every. When something is
|
||||
* written to the output buffer the socket is not immediately
|
||||
* activated for output (again for the same reason), there
|
||||
* is 10ms celling (thats why there is Update() method).
|
||||
* This concept is similar to TCP_CORK, but TCP_CORK
|
||||
* uses 200ms celling. As result overhead generated by
|
||||
* sending packets from "producer" threads is minimal,
|
||||
* and doing a lot of writes with small size is tolerated.
|
||||
*
|
||||
* The calls to Update() method are managed by WorldSocketMgr
|
||||
* and ReactorRunnable.
|
||||
*
|
||||
* For input, the class uses one 4096 bytes buffer on stack
|
||||
* to which it does recv() calls. And then received data is
|
||||
* distributed where its needed. 4096 matches pretty well the
|
||||
* traffic generated by client for now.
|
||||
*
|
||||
* The input/output do speculative reads/writes (AKA it tryes
|
||||
* to read all data available in the kernel buffer or tryes to
|
||||
* write everything available in userspace buffer),
|
||||
* which is ok for using with Level and Edge Triggered IO
|
||||
* notification.
|
||||
*
|
||||
*/
|
||||
class WorldSocket : public WorldHandler
|
||||
#pragma pack(push, 1)
|
||||
struct ClientPktHeader
|
||||
{
|
||||
uint16 size;
|
||||
uint32 cmd;
|
||||
|
||||
bool IsValidSize() const { return size >= 4 && size < 10240; }
|
||||
bool IsValidOpcode() const { return cmd < NUM_OPCODE_HANDLERS; }
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct AuthSession;
|
||||
|
||||
class AC_GAME_API WorldSocket : public Socket<WorldSocket>
|
||||
{
|
||||
typedef Socket<WorldSocket> BaseSocket;
|
||||
|
||||
public:
|
||||
WorldSocket (void);
|
||||
virtual ~WorldSocket (void);
|
||||
WorldSocket(tcp::socket&& socket);
|
||||
~WorldSocket();
|
||||
|
||||
friend class WorldSocketMgr;
|
||||
WorldSocket(WorldSocket const& right) = delete;
|
||||
WorldSocket& operator=(WorldSocket const& right) = delete;
|
||||
|
||||
/// Check if socket is closed.
|
||||
bool IsClosed (void) const;
|
||||
void Start() override;
|
||||
bool Update() override;
|
||||
|
||||
/// Close the socket.
|
||||
void CloseSocket(std::string const& reason);
|
||||
void SendPacket(WorldPacket const& packet);
|
||||
|
||||
/// Get address of connected peer.
|
||||
const std::string& GetRemoteAddress (void) const;
|
||||
void SetSendBufferSize(std::size_t sendBufferSize) { _sendBufferSize = sendBufferSize; }
|
||||
|
||||
/// Send A packet on the socket, this function is reentrant.
|
||||
/// @param pct packet to send
|
||||
/// @return -1 of failure
|
||||
int SendPacket(const WorldPacket& pct);
|
||||
protected:
|
||||
void OnClose() override;
|
||||
void ReadHandler() override;
|
||||
bool ReadHeaderHandler();
|
||||
|
||||
/// Add reference to this object.
|
||||
long AddReference (void);
|
||||
enum class ReadDataHandlerResult
|
||||
{
|
||||
Ok = 0,
|
||||
Error = 1,
|
||||
WaitingForQuery = 2
|
||||
};
|
||||
|
||||
/// Remove reference to this object.
|
||||
long RemoveReference (void);
|
||||
|
||||
/// things called by ACE framework.
|
||||
|
||||
/// Called on open, the void* is the acceptor.
|
||||
virtual int open (void*);
|
||||
|
||||
/// Called on failures inside of the acceptor, don't call from your code.
|
||||
virtual int close (u_long);
|
||||
|
||||
/// Called when we can read from the socket.
|
||||
virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE);
|
||||
|
||||
/// Called when the socket can write.
|
||||
virtual int handle_output (ACE_HANDLE = ACE_INVALID_HANDLE);
|
||||
|
||||
/// Called when connection is closed or error happens.
|
||||
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,
|
||||
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
|
||||
|
||||
/// Called by WorldSocketMgr/ReactorRunnable.
|
||||
int Update (void);
|
||||
ReadDataHandlerResult ReadDataHandler();
|
||||
|
||||
private:
|
||||
/// Helper functions for processing incoming data.
|
||||
int handle_input_header (void);
|
||||
int handle_input_payload (void);
|
||||
int handle_input_missing_data (void);
|
||||
void CheckIpCallback(PreparedQueryResult result);
|
||||
|
||||
/// Help functions to mark/unmark the socket for output.
|
||||
/// @param g the guard is for m_OutBufferLock, the function will release it
|
||||
int cancel_wakeup_output();
|
||||
int schedule_wakeup_output();
|
||||
/// writes network.opcode log
|
||||
/// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock
|
||||
void LogOpcodeText(OpcodeClient opcode, std::unique_lock<std::mutex> const& guard) const;
|
||||
|
||||
/// Drain the queue if its not empty.
|
||||
int handle_output_queue();
|
||||
/// sends and logs network.opcode without accessing WorldSession
|
||||
void SendPacketAndLogOpcode(WorldPacket const& packet);
|
||||
void HandleSendAuthSession();
|
||||
void HandleAuthSession(WorldPacket& recvPacket);
|
||||
void HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSession, PreparedQueryResult result);
|
||||
void LoadSessionPermissionsCallback(PreparedQueryResult result);
|
||||
void SendAuthResponseError(uint8 code);
|
||||
|
||||
/// process one incoming packet.
|
||||
/// @param new_pct received packet, note that you need to delete it.
|
||||
int ProcessIncoming (WorldPacket* new_pct);
|
||||
bool HandlePing(WorldPacket& recvPacket);
|
||||
|
||||
/// Called by ProcessIncoming() on CMSG_AUTH_SESSION.
|
||||
int HandleAuthSession (WorldPacket& recvPacket);
|
||||
std::array<uint8, 4> _authSeed;
|
||||
AuthCrypt _authCrypt;
|
||||
|
||||
/// Called by ProcessIncoming() on CMSG_PING.
|
||||
int HandlePing (WorldPacket& recvPacket);
|
||||
TimePoint _LastPingTime;
|
||||
uint32 _OverSpeedPings;
|
||||
|
||||
private:
|
||||
/// Time in which the last ping was received
|
||||
SystemTimePoint m_LastPingTime;
|
||||
std::mutex _worldSessionLock;
|
||||
WorldSession* _worldSession;
|
||||
bool _authed;
|
||||
|
||||
/// Keep track of over-speed pings, to prevent ping flood.
|
||||
uint32 m_OverSpeedPings;
|
||||
|
||||
/// Address of the remote peer
|
||||
std::string m_Address;
|
||||
|
||||
/// Class used for managing encryption of the headers
|
||||
AuthCrypt m_Crypt;
|
||||
|
||||
/// Mutex lock to protect m_Session
|
||||
std::mutex m_SessionLock;
|
||||
|
||||
/// Session to which received packets are routed
|
||||
WorldSession* m_Session;
|
||||
|
||||
/// here are stored the fragments of the received data
|
||||
WorldPacket* m_RecvWPct;
|
||||
|
||||
/// This block actually refers to m_RecvWPct contents,
|
||||
/// which allows easy and safe writing to it.
|
||||
/// It wont free memory when its deleted. m_RecvWPct takes care of freeing.
|
||||
ACE_Message_Block m_RecvPct;
|
||||
|
||||
/// Fragment of the received header.
|
||||
ACE_Message_Block m_Header;
|
||||
|
||||
/// Mutex for protecting output related data.
|
||||
std::mutex m_OutBufferLock;
|
||||
|
||||
/// Buffer used for writing output.
|
||||
ACE_Message_Block* m_OutBuffer;
|
||||
|
||||
/// Size of the m_OutBuffer.
|
||||
size_t m_OutBufferSize;
|
||||
|
||||
/// True if the socket is registered with the reactor for output
|
||||
bool m_OutActive;
|
||||
|
||||
std::array<uint8, 4> m_Seed;
|
||||
MessageBuffer _headerBuffer;
|
||||
MessageBuffer _packetBuffer;
|
||||
MPSCQueue<EncryptablePacket, &EncryptablePacket::SocketQueueLink> _bufferQueue;
|
||||
std::size_t _sendBufferSize;
|
||||
|
||||
QueryCallbackProcessor _queryProcessor;
|
||||
std::string _ipCountry;
|
||||
};
|
||||
|
||||
#endif /* _WORLDSOCKET_H */
|
||||
|
||||
/// @}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/** \addtogroup u2w User to World Communication
|
||||
* @{
|
||||
* \file WorldSocketMgr.h
|
||||
*/
|
||||
|
||||
#ifndef __WORLDSOCKETACCEPTOR_H_
|
||||
#define __WORLDSOCKETACCEPTOR_H_
|
||||
|
||||
#include "Common.h"
|
||||
#include "WorldSocket.h"
|
||||
#include <ace/Acceptor.h>
|
||||
#include <ace/SOCK_Acceptor.h>
|
||||
|
||||
class WorldSocketAcceptor : public ACE_Acceptor<WorldSocket, ACE_SOCK_Acceptor>
|
||||
{
|
||||
public:
|
||||
WorldSocketAcceptor(void) { }
|
||||
virtual ~WorldSocketAcceptor(void)
|
||||
{
|
||||
if (reactor())
|
||||
reactor()->cancel_timer(this, 1);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/)
|
||||
{
|
||||
LOG_INFO("network", "Resuming acceptor");
|
||||
reactor()->cancel_timer(this, 1);
|
||||
return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);
|
||||
}
|
||||
|
||||
virtual int handle_accept_error(void)
|
||||
{
|
||||
#if defined(ENFILE) && defined(EMFILE)
|
||||
if (errno == ENFILE || errno == EMFILE)
|
||||
{
|
||||
LOG_ERROR("network", "Out of file descriptors, suspending incoming connections for 10 seconds");
|
||||
reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL);
|
||||
reactor()->schedule_timer(this, nullptr, ACE_Time_Value(10));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __WORLDSOCKETACCEPTOR_H_ */
|
||||
/// @}
|
||||
|
|
@ -4,345 +4,109 @@
|
|||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/** \file WorldSocketMgr.cpp
|
||||
* \ingroup u2w
|
||||
* \author Derex <derex101@gmail.com>
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "NetworkThread.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "WorldSocket.h"
|
||||
#include "WorldSocketAcceptor.h"
|
||||
#include "WorldSocketMgr.h"
|
||||
#include <ace/ACE.h>
|
||||
#include <ace/Dev_Poll_Reactor.h>
|
||||
#include <ace/Log_Msg.h>
|
||||
#include <ace/os_include/arpa/os_inet.h>
|
||||
#include <ace/os_include/netinet/os_tcp.h>
|
||||
#include <ace/os_include/sys/os_socket.h>
|
||||
#include <ace/Reactor_Impl.h>
|
||||
#include <ace/Reactor.h>
|
||||
#include <ace/TP_Reactor.h>
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
/**
|
||||
* This is a helper class to WorldSocketMgr, that manages
|
||||
* network threads, and assigning connections from acceptor thread
|
||||
* to other network threads
|
||||
*/
|
||||
class ReactorRunnable : protected ACE_Task_Base
|
||||
static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
|
||||
{
|
||||
sWorldSocketMgr.OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
|
||||
}
|
||||
|
||||
class WorldSocketThread : public NetworkThread<WorldSocket>
|
||||
{
|
||||
public:
|
||||
ReactorRunnable() :
|
||||
m_Reactor(0),
|
||||
m_Connections(0),
|
||||
m_ThreadId(-1)
|
||||
void SocketAdded(std::shared_ptr<WorldSocket> sock) override
|
||||
{
|
||||
ACE_Reactor_Impl* imp;
|
||||
|
||||
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
|
||||
|
||||
imp = new ACE_Dev_Poll_Reactor();
|
||||
|
||||
imp->max_notify_iterations (128);
|
||||
imp->restart (1);
|
||||
|
||||
#else
|
||||
|
||||
imp = new ACE_TP_Reactor();
|
||||
imp->max_notify_iterations (128);
|
||||
|
||||
#endif
|
||||
|
||||
m_Reactor = new ACE_Reactor (imp, 1);
|
||||
}
|
||||
|
||||
~ReactorRunnable() override
|
||||
{
|
||||
Stop();
|
||||
Wait();
|
||||
|
||||
delete m_Reactor;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
m_Reactor->end_reactor_event_loop();
|
||||
}
|
||||
|
||||
int Start()
|
||||
{
|
||||
if (m_ThreadId != -1)
|
||||
return -1;
|
||||
|
||||
return (m_ThreadId = activate());
|
||||
}
|
||||
|
||||
void Wait() { ACE_Task_Base::wait(); }
|
||||
|
||||
long Connections()
|
||||
{
|
||||
return m_Connections;
|
||||
}
|
||||
|
||||
int AddSocket (WorldSocket* sock)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_NewSockets_Lock);
|
||||
|
||||
++m_Connections;
|
||||
sock->AddReference();
|
||||
sock->reactor (m_Reactor);
|
||||
m_NewSockets.insert (sock);
|
||||
|
||||
sock->SetSendBufferSize(sWorldSocketMgr.GetApplicationSendBufferSize());
|
||||
sScriptMgr->OnSocketOpen(sock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ACE_Reactor* GetReactor()
|
||||
void SocketRemoved(std::shared_ptr<WorldSocket> sock) override
|
||||
{
|
||||
return m_Reactor;
|
||||
sScriptMgr->OnSocketClose(sock);
|
||||
}
|
||||
|
||||
protected:
|
||||
void AddNewSockets()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_NewSockets_Lock);
|
||||
|
||||
if (m_NewSockets.empty())
|
||||
return;
|
||||
|
||||
for (SocketSet::const_iterator i = m_NewSockets.begin(); i != m_NewSockets.end(); ++i)
|
||||
{
|
||||
WorldSocket* sock = (*i);
|
||||
|
||||
if (sock->IsClosed())
|
||||
{
|
||||
sScriptMgr->OnSocketClose(sock, true);
|
||||
|
||||
sock->RemoveReference();
|
||||
--m_Connections;
|
||||
}
|
||||
else
|
||||
m_Sockets.insert (sock);
|
||||
}
|
||||
|
||||
m_NewSockets.clear();
|
||||
}
|
||||
|
||||
int svc() override
|
||||
{
|
||||
LOG_DEBUG("network", "Network Thread Starting");
|
||||
|
||||
ASSERT(m_Reactor);
|
||||
|
||||
SocketSet::iterator i, t;
|
||||
|
||||
while (!m_Reactor->reactor_event_loop_done())
|
||||
{
|
||||
// dont be too smart to move this outside the loop
|
||||
// the run_reactor_event_loop will modify interval
|
||||
ACE_Time_Value interval (0, 10000);
|
||||
|
||||
if (m_Reactor->run_reactor_event_loop (interval) == -1)
|
||||
break;
|
||||
|
||||
AddNewSockets();
|
||||
|
||||
for (i = m_Sockets.begin(); i != m_Sockets.end();)
|
||||
{
|
||||
if ((*i)->Update() == -1)
|
||||
{
|
||||
t = i;
|
||||
++i;
|
||||
|
||||
(*t)->CloseSocket("svc()");
|
||||
|
||||
sScriptMgr->OnSocketClose((*t), false);
|
||||
|
||||
(*t)->RemoveReference();
|
||||
--m_Connections;
|
||||
m_Sockets.erase (t);
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("network", "Network Thread exits");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::atomic<int> AtomicInt;
|
||||
typedef std::set<WorldSocket*> SocketSet;
|
||||
|
||||
ACE_Reactor* m_Reactor;
|
||||
AtomicInt m_Connections;
|
||||
int m_ThreadId;
|
||||
|
||||
SocketSet m_Sockets;
|
||||
|
||||
SocketSet m_NewSockets;
|
||||
std::mutex m_NewSockets_Lock;
|
||||
};
|
||||
|
||||
WorldSocketMgr::WorldSocketMgr() :
|
||||
m_NetThreads(0),
|
||||
m_NetThreadsCount(0),
|
||||
m_SockOutKBuff(-1),
|
||||
m_SockOutUBuff(65536),
|
||||
m_UseNoDelay(true),
|
||||
m_Acceptor (0)
|
||||
BaseSocketMgr(), _socketSystemSendBufferSize(-1), _socketApplicationSendBufferSize(65536), _tcpNoDelay(true)
|
||||
{
|
||||
}
|
||||
|
||||
WorldSocketMgr::~WorldSocketMgr()
|
||||
{
|
||||
delete [] m_NetThreads;
|
||||
delete m_Acceptor;
|
||||
}
|
||||
|
||||
WorldSocketMgr* WorldSocketMgr::instance()
|
||||
WorldSocketMgr& WorldSocketMgr::Instance()
|
||||
{
|
||||
static WorldSocketMgr instance;
|
||||
return &instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
int
|
||||
WorldSocketMgr::StartReactiveIO (uint16 port, const char* address)
|
||||
bool WorldSocketMgr::StartWorldNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int threadCount)
|
||||
{
|
||||
m_UseNoDelay = sConfigMgr->GetOption<bool> ("Network.TcpNodelay", true);
|
||||
_tcpNoDelay = sConfigMgr->GetOption<bool>("Network.TcpNodelay", true);
|
||||
|
||||
int num_threads = sConfigMgr->GetOption<int32> ("Network.Threads", 1);
|
||||
|
||||
if (num_threads <= 0)
|
||||
{
|
||||
LOG_ERROR("network", "Network.Threads is wrong in your config file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_NetThreadsCount = static_cast<size_t> (num_threads + 1);
|
||||
|
||||
m_NetThreads = new ReactorRunnable[m_NetThreadsCount];
|
||||
|
||||
LOG_INFO("network", "Max allowed socket connections %d", ACE::max_handles());
|
||||
int const max_connections = ACORE_MAX_LISTEN_CONNECTIONS;
|
||||
LOG_DEBUG("network", "Max allowed socket connections %d", max_connections);
|
||||
|
||||
// -1 means use default
|
||||
m_SockOutKBuff = sConfigMgr->GetOption<int32> ("Network.OutKBuff", -1);
|
||||
_socketSystemSendBufferSize = sConfigMgr->GetOption<int32>("Network.OutKBuff", -1);
|
||||
_socketApplicationSendBufferSize = sConfigMgr->GetOption<int32>("Network.OutUBuff", 65536);
|
||||
|
||||
m_SockOutUBuff = sConfigMgr->GetOption<int32> ("Network.OutUBuff", 65536);
|
||||
|
||||
if (m_SockOutUBuff <= 0)
|
||||
if (_socketApplicationSendBufferSize <= 0)
|
||||
{
|
||||
LOG_ERROR("network", "Network.OutUBuff is wrong in your config file");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Acceptor = new WorldSocketAcceptor;
|
||||
if (!BaseSocketMgr::StartNetwork(ioContext, bindIp, port, threadCount))
|
||||
return false;
|
||||
|
||||
ACE_INET_Addr listen_addr (port, address);
|
||||
|
||||
if (m_Acceptor->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1)
|
||||
{
|
||||
LOG_ERROR("network", "Failed to open acceptor, check if the port is free");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_NetThreadsCount; ++i)
|
||||
m_NetThreads[i].Start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
WorldSocketMgr::StartNetwork (uint16 port, const char* address)
|
||||
{
|
||||
if (!sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG))
|
||||
ACE_Log_Msg::instance()->priority_mask(LM_ERROR, ACE_Log_Msg::PROCESS);
|
||||
|
||||
if (StartReactiveIO(port, address) == -1)
|
||||
return -1;
|
||||
_acceptor->AsyncAcceptWithCallback<&OnSocketAccept>();
|
||||
|
||||
sScriptMgr->OnNetworkStart();
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WorldSocketMgr::StopNetwork()
|
||||
void WorldSocketMgr::StopNetwork()
|
||||
{
|
||||
if (m_Acceptor)
|
||||
{
|
||||
m_Acceptor->close();
|
||||
}
|
||||
|
||||
if (m_NetThreadsCount != 0)
|
||||
{
|
||||
for (size_t i = 0; i < m_NetThreadsCount; ++i)
|
||||
m_NetThreads[i].Stop();
|
||||
}
|
||||
|
||||
Wait();
|
||||
BaseSocketMgr::StopNetwork();
|
||||
|
||||
sScriptMgr->OnNetworkStop();
|
||||
}
|
||||
|
||||
void
|
||||
WorldSocketMgr::Wait()
|
||||
{
|
||||
if (m_NetThreadsCount != 0)
|
||||
{
|
||||
for (size_t i = 0; i < m_NetThreadsCount; ++i)
|
||||
m_NetThreads[i].Wait();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
WorldSocketMgr::OnSocketOpen (WorldSocket* sock)
|
||||
void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex)
|
||||
{
|
||||
// set some options here
|
||||
if (m_SockOutKBuff >= 0)
|
||||
if (_socketSystemSendBufferSize >= 0)
|
||||
{
|
||||
if (sock->peer().set_option (SOL_SOCKET,
|
||||
SO_SNDBUF,
|
||||
(void*) & m_SockOutKBuff,
|
||||
sizeof (int)) == -1 && errno != ENOTSUP)
|
||||
boost::system::error_code err;
|
||||
sock.set_option(boost::asio::socket_base::send_buffer_size(_socketSystemSendBufferSize), err);
|
||||
|
||||
if (err && err != boost::system::errc::not_supported)
|
||||
{
|
||||
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen set_option SO_SNDBUF");
|
||||
return -1;
|
||||
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::socket_base::send_buffer_size) err = %s", err.message().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const int ndoption = 1;
|
||||
|
||||
// Set TCP_NODELAY.
|
||||
if (m_UseNoDelay)
|
||||
if (_tcpNoDelay)
|
||||
{
|
||||
if (sock->peer().set_option (ACE_IPPROTO_TCP,
|
||||
TCP_NODELAY,
|
||||
(void*)&ndoption,
|
||||
sizeof (int)) == -1)
|
||||
boost::system::error_code err;
|
||||
sock.set_option(boost::asio::ip::tcp::no_delay(true), err);
|
||||
|
||||
if (err)
|
||||
{
|
||||
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen: peer().set_option TCP_NODELAY errno = %s", ACE_OS::strerror (errno));
|
||||
return -1;
|
||||
LOG_ERROR("network", "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);
|
||||
|
||||
// we skip the Acceptor Thread
|
||||
size_t min = 1;
|
||||
|
||||
ASSERT(m_NetThreadsCount >= 1);
|
||||
|
||||
for (size_t i = 1; i < m_NetThreadsCount; ++i)
|
||||
if (m_NetThreads[i].Connections() < m_NetThreads[min].Connections())
|
||||
min = i;
|
||||
|
||||
return m_NetThreads[min].AddSocket (sock);
|
||||
BaseSocketMgr::OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
|
||||
}
|
||||
|
||||
NetworkThread<WorldSocket>* WorldSocketMgr::CreateThreads() const
|
||||
{
|
||||
return new WorldSocketThread[GetNetworkThreadCount()];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,58 +4,43 @@
|
|||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/** \addtogroup u2w User to World Communication
|
||||
* @{
|
||||
* \file WorldSocketMgr.h
|
||||
* \author Derex <derex101@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __WORLDSOCKETMGR_H
|
||||
#define __WORLDSOCKETMGR_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "SocketMgr.h"
|
||||
|
||||
class WorldSocket;
|
||||
class ReactorRunnable;
|
||||
class ACE_Event_Handler;
|
||||
|
||||
/// Manages all sockets connected to peers and network threads
|
||||
class WorldSocketMgr
|
||||
class AC_GAME_API WorldSocketMgr : public SocketMgr<WorldSocket>
|
||||
{
|
||||
public:
|
||||
friend class WorldSocket;
|
||||
typedef SocketMgr<WorldSocket> BaseSocketMgr;
|
||||
|
||||
static WorldSocketMgr* instance();
|
||||
public:
|
||||
static WorldSocketMgr& Instance();
|
||||
|
||||
/// Start network, listen at address:port .
|
||||
int StartNetwork(uint16 port, const char* address);
|
||||
bool StartWorldNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int networkThreads);
|
||||
|
||||
/// Stops all network threads, It will wait for all running threads .
|
||||
void StopNetwork();
|
||||
void StopNetwork() override;
|
||||
|
||||
/// Wait untill all network threads have "joined" .
|
||||
void Wait();
|
||||
void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override;
|
||||
|
||||
private:
|
||||
int OnSocketOpen(WorldSocket* sock);
|
||||
std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; }
|
||||
|
||||
int StartReactiveIO(uint16 port, const char* address);
|
||||
|
||||
private:
|
||||
protected:
|
||||
WorldSocketMgr();
|
||||
virtual ~WorldSocketMgr();
|
||||
|
||||
ReactorRunnable* m_NetThreads;
|
||||
size_t m_NetThreadsCount;
|
||||
NetworkThread<WorldSocket>* CreateThreads() const override;
|
||||
|
||||
int m_SockOutKBuff;
|
||||
int m_SockOutUBuff;
|
||||
bool m_UseNoDelay;
|
||||
|
||||
class WorldSocketAcceptor* m_Acceptor;
|
||||
private:
|
||||
int32 _socketSystemSendBufferSize;
|
||||
int32 _socketApplicationSendBufferSize;
|
||||
bool _tcpNoDelay;
|
||||
};
|
||||
|
||||
#define sWorldSocketMgr WorldSocketMgr::instance()
|
||||
#define sWorldSocketMgr WorldSocketMgr::Instance()
|
||||
|
||||
#endif
|
||||
/// @}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
\ingroup world
|
||||
*/
|
||||
|
||||
#include "World.h"
|
||||
#include "AccountMgr.h"
|
||||
#include "AchievementMgr.h"
|
||||
#include "AddonMgr.h"
|
||||
|
|
@ -28,8 +29,8 @@
|
|||
#include "CreatureAIRegistry.h"
|
||||
#include "CreatureGroups.h"
|
||||
#include "CreatureTextMgr.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DBCStores.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DisableMgr.h"
|
||||
#include "DynamicVisibility.h"
|
||||
#include "GameEventMgr.h"
|
||||
|
|
@ -38,16 +39,16 @@
|
|||
#include "GridNotifiersImpl.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "IPLocation.h"
|
||||
#include "InstanceSaveMgr.h"
|
||||
#include "ItemEnchantmentMgr.h"
|
||||
#include "IPLocation.h"
|
||||
#include "Language.h"
|
||||
#include "LFGMgr.h"
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "LootItemStorage.h"
|
||||
#include "LootMgr.h"
|
||||
#include "MapManager.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "MapManager.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "OutdoorPvPMgr.h"
|
||||
|
|
@ -66,17 +67,18 @@
|
|||
#include "Transport.h"
|
||||
#include "TransportMgr.h"
|
||||
#include "Util.h"
|
||||
#include "Vehicle.h"
|
||||
#include "VMapFactory.h"
|
||||
#include "VMapManager2.h"
|
||||
#include "Vehicle.h"
|
||||
#include "Warden.h"
|
||||
#include "WardenCheckMgr.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
#include "WeatherMgr.h"
|
||||
#include "WhoListCache.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include <VMapManager2.h>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#ifdef ELUNA
|
||||
#include "LuaEngine.h"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include "AccountMgr.h"
|
||||
#include "ace/INET_Addr.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "CellImpl.h"
|
||||
|
|
|
|||
|
|
@ -117,7 +117,6 @@ public:
|
|||
|
||||
handler->PSendSysMessage("%s", GitRevision::GetFullVersion());
|
||||
handler->PSendSysMessage("Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||
handler->PSendSysMessage("Using ACE version: %s", ACE_VERSION);
|
||||
handler->PSendSysMessage("Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
|
||||
handler->PSendSysMessage("Using MySQL version: %u", MySQL::GetLibraryVersion());
|
||||
handler->PSendSysMessage("Using CMake version: %s", GitRevision::GetCMakeVersion());
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ public:
|
|||
{
|
||||
{ "version", SEC_CONSOLE, false, &HandleSpectatorVersionCommand, "" },
|
||||
{ "reset", SEC_CONSOLE, false, &HandleSpectatorResetCommand, "" },
|
||||
{ "spectate", SEC_CONSOLE, false, &ArenaSpectator::HandleSpectatorSpectateCommand, "" },
|
||||
{ "watch", SEC_CONSOLE, false, &ArenaSpectator::HandleSpectatorWatchCommand, "" },
|
||||
{ "spectate", SEC_CONSOLE, false, &HandleSpectatorSpectateCommand, "" },
|
||||
{ "watch", SEC_CONSOLE, false, &HandleSpectatorWatchCommand, "" },
|
||||
{ "leave", SEC_CONSOLE, false, &HandleSpectatorLeaveCommand, "" },
|
||||
{ "", SEC_CONSOLE, false, &HandleSpectatorCommand, "" }
|
||||
};
|
||||
|
|
@ -71,6 +71,22 @@ public:
|
|||
player->TeleportToEntryPoint();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!ArenaSpectator::HandleSpectatorSpectateCommand(handler, args))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!ArenaSpectator::HandleSpectatorWatchCommand(handler, args))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_spectator_commandscript()
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
using boost::asio::ip::tcp;
|
||||
|
||||
#if BOOST_VERSION >= 106600
|
||||
#define WARHEAD_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_listen_connections
|
||||
#define ACORE_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_listen_connections
|
||||
#else
|
||||
#define WARHEAD_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_connections
|
||||
#define ACORE_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_connections
|
||||
#endif
|
||||
|
||||
class AsyncAcceptor
|
||||
|
|
@ -72,7 +72,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
#if WARHEAD_PLATFORM != WARHEAD_PLATFORM_WINDOWS
|
||||
#if AC_PLATFORM != AC_PLATFORM_WINDOWS
|
||||
_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
|
|
@ -88,7 +88,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
_acceptor.listen(WARHEAD_MAX_LISTEN_CONNECTIONS, errorCode);
|
||||
_acceptor.listen(ACORE_MAX_LISTEN_CONNECTIONS, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
LOG_INFO("network", "Failed to start listening on %s:%u %s", _endpoint.address().to_string().c_str(), _endpoint.port(), errorCode.message().c_str());
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "Log.h"
|
||||
#include "RealmSocket.h"
|
||||
#include <ace/INET_Addr.h>
|
||||
#include <ace/OS_NS_string.h>
|
||||
|
||||
RealmSocket::Session::Session() = default;
|
||||
|
||||
RealmSocket::Session::~Session() = default;
|
||||
|
||||
RealmSocket::RealmSocket() :
|
||||
input_buffer_(4096),
|
||||
_remoteAddress()
|
||||
{
|
||||
reference_counting_policy().value(ACE_Event_Handler::Reference_Counting_Policy::ENABLED);
|
||||
|
||||
msg_queue()->high_water_mark(8 * 1024 * 1024);
|
||||
msg_queue()->low_water_mark(8 * 1024 * 1024);
|
||||
}
|
||||
|
||||
RealmSocket::~RealmSocket()
|
||||
{
|
||||
if (msg_queue())
|
||||
msg_queue()->close();
|
||||
|
||||
// delete RealmSocketObject must never be called from our code.
|
||||
closing_ = true;
|
||||
|
||||
delete session_;
|
||||
|
||||
peer().close();
|
||||
}
|
||||
|
||||
int RealmSocket::open(void* arg)
|
||||
{
|
||||
ACE_INET_Addr addr;
|
||||
|
||||
if (peer().get_remote_addr(addr) == -1)
|
||||
{
|
||||
LOG_ERROR("network", "Error %s while opening realm socket!", ACE_OS::strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
_remoteAddress = addr.get_host_addr();
|
||||
_remotePort = addr.get_port_number();
|
||||
|
||||
// Register with ACE Reactor
|
||||
if (Base::open(arg) == -1)
|
||||
return -1;
|
||||
|
||||
if (session_)
|
||||
session_->OnAccept();
|
||||
|
||||
// reactor takes care of the socket from now on
|
||||
remove_reference();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RealmSocket::close(u_long)
|
||||
{
|
||||
shutdown();
|
||||
|
||||
closing_ = true;
|
||||
|
||||
remove_reference();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string& RealmSocket::getRemoteAddress() const
|
||||
{
|
||||
return _remoteAddress;
|
||||
}
|
||||
|
||||
uint16 RealmSocket::getRemotePort() const
|
||||
{
|
||||
return _remotePort;
|
||||
}
|
||||
|
||||
size_t RealmSocket::recv_len() const
|
||||
{
|
||||
return input_buffer_.length();
|
||||
}
|
||||
|
||||
bool RealmSocket::recv_soft(char* buf, size_t len)
|
||||
{
|
||||
if (input_buffer_.length() < len)
|
||||
return false;
|
||||
|
||||
ACE_OS::memcpy(buf, input_buffer_.rd_ptr(), len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RealmSocket::recv(char* buf, size_t len)
|
||||
{
|
||||
bool ret = recv_soft(buf, len);
|
||||
|
||||
if (ret)
|
||||
recv_skip(len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RealmSocket::recv_skip(size_t len)
|
||||
{
|
||||
input_buffer_.rd_ptr(len);
|
||||
}
|
||||
|
||||
ssize_t RealmSocket::noblk_send(ACE_Message_Block& message_block)
|
||||
{
|
||||
const size_t len = message_block.length();
|
||||
|
||||
if (len == 0)
|
||||
return -1;
|
||||
|
||||
// Try to send the message directly.
|
||||
#ifdef MSG_NOSIGNAL
|
||||
ssize_t n = peer().send(message_block.rd_ptr(), len, MSG_NOSIGNAL);
|
||||
#else
|
||||
ssize_t n = peer().send(message_block.rd_ptr(), len);
|
||||
#endif // MSG_NOSIGNAL
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
if (errno == EWOULDBLOCK) // Blocking signal
|
||||
return 0;
|
||||
else // Error happened
|
||||
return -1;
|
||||
}
|
||||
else if (n == 0)
|
||||
{
|
||||
// Can this happen ?
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return bytes transmitted
|
||||
return n;
|
||||
}
|
||||
|
||||
bool RealmSocket::send(const char* buf, size_t len)
|
||||
{
|
||||
if (buf == nullptr || len == 0)
|
||||
return true;
|
||||
|
||||
ACE_Data_Block db(len, ACE_Message_Block::MB_DATA, (const char*)buf, nullptr, nullptr, ACE_Message_Block::DONT_DELETE, nullptr);
|
||||
ACE_Message_Block message_block(&db, ACE_Message_Block::DONT_DELETE, nullptr);
|
||||
|
||||
message_block.wr_ptr(len);
|
||||
|
||||
if (msg_queue()->is_empty())
|
||||
{
|
||||
// Try to send it directly.
|
||||
ssize_t n = noblk_send(message_block);
|
||||
|
||||
if (n < 0)
|
||||
return false;
|
||||
|
||||
size_t un = size_t(n);
|
||||
if (un == len)
|
||||
return true;
|
||||
|
||||
// fall down
|
||||
message_block.rd_ptr(un);
|
||||
}
|
||||
|
||||
ACE_Message_Block* mb = message_block.clone();
|
||||
|
||||
if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value*)(&ACE_Time_Value::zero)) == -1)
|
||||
{
|
||||
mb->release();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int RealmSocket::handle_output(ACE_HANDLE)
|
||||
{
|
||||
if (closing_)
|
||||
return -1;
|
||||
|
||||
ACE_Message_Block* mb = nullptr;
|
||||
|
||||
if (msg_queue()->is_empty())
|
||||
{
|
||||
reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg_queue()->dequeue_head(mb, (ACE_Time_Value*)(&ACE_Time_Value::zero)) == -1)
|
||||
return -1;
|
||||
|
||||
ssize_t n = noblk_send(*mb);
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
mb->release();
|
||||
return -1;
|
||||
}
|
||||
else if (size_t(n) == mb->length())
|
||||
{
|
||||
mb->release();
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mb->rd_ptr(n);
|
||||
|
||||
if (msg_queue()->enqueue_head(mb, (ACE_Time_Value*) &ACE_Time_Value::zero) == -1)
|
||||
{
|
||||
mb->release();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ACE_NOTREACHED(return -1);
|
||||
}
|
||||
|
||||
int RealmSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask)
|
||||
{
|
||||
// As opposed to WorldSocket::handle_close, we don't need locks here.
|
||||
closing_ = true;
|
||||
|
||||
if (h == ACE_INVALID_HANDLE)
|
||||
peer().close_writer();
|
||||
|
||||
if (session_)
|
||||
session_->OnClose();
|
||||
|
||||
reactor()->remove_handler(this, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::ALL_EVENTS_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RealmSocket::handle_input(ACE_HANDLE)
|
||||
{
|
||||
if (closing_)
|
||||
return -1;
|
||||
|
||||
const ssize_t space = input_buffer_.space();
|
||||
|
||||
ssize_t n = peer().recv(input_buffer_.wr_ptr(), space);
|
||||
|
||||
if (n < 0)
|
||||
return errno == EWOULDBLOCK ? 0 : -1;
|
||||
else if (n == 0) // EOF
|
||||
return -1;
|
||||
|
||||
input_buffer_.wr_ptr((size_t)n);
|
||||
|
||||
if (session_ != nullptr)
|
||||
{
|
||||
session_->OnRead();
|
||||
input_buffer_.crunch();
|
||||
}
|
||||
|
||||
// return 1 in case there is more data to read from OS
|
||||
return n == space ? 1 : 0;
|
||||
}
|
||||
|
||||
void RealmSocket::set_session(Session* session)
|
||||
{
|
||||
delete session_;
|
||||
|
||||
session_ = session;
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef __REALMSOCKET_H__
|
||||
#define __REALMSOCKET_H__
|
||||
|
||||
#include "Common.h"
|
||||
#include <ace/Message_Block.h>
|
||||
#include <ace/SOCK_Stream.h>
|
||||
#include <ace/Svc_Handler.h>
|
||||
#include <ace/Synch_Traits.h>
|
||||
|
||||
class RealmSocket : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
|
||||
{
|
||||
private:
|
||||
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> Base;
|
||||
|
||||
public:
|
||||
class Session
|
||||
{
|
||||
public:
|
||||
Session();
|
||||
virtual ~Session();
|
||||
|
||||
virtual void OnRead() = 0;
|
||||
virtual void OnAccept() = 0;
|
||||
virtual void OnClose() = 0;
|
||||
};
|
||||
|
||||
RealmSocket();
|
||||
~RealmSocket() override;
|
||||
|
||||
[[nodiscard]] size_t recv_len() const;
|
||||
bool recv_soft(char* buf, size_t len);
|
||||
bool recv(char* buf, size_t len);
|
||||
void recv_skip(size_t len);
|
||||
|
||||
bool send(const char* buf, size_t len);
|
||||
|
||||
[[nodiscard]] const std::string& getRemoteAddress() const;
|
||||
|
||||
[[nodiscard]] uint16 getRemotePort() const;
|
||||
|
||||
int open(void*) override;
|
||||
|
||||
int close(u_long) override;
|
||||
|
||||
int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE) override;
|
||||
int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE) override;
|
||||
|
||||
int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK) override;
|
||||
|
||||
void set_session(Session* session);
|
||||
|
||||
private:
|
||||
ssize_t noblk_send(ACE_Message_Block& message_block);
|
||||
|
||||
ACE_Message_Block input_buffer_;
|
||||
Session* session_{nullptr};
|
||||
std::string _remoteAddress;
|
||||
uint16 _remotePort{0};
|
||||
};
|
||||
|
||||
#endif /* __REALMSOCKET_H__ */
|
||||
44
src/server/shared/Realms/Realm.cpp
Normal file
44
src/server/shared/Realms/Realm.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
#include "Realm.h"
|
||||
#include "IpAddress.h"
|
||||
#include "IpNetwork.h"
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
boost::asio::ip::tcp_endpoint Realm::GetAddressForClient(boost::asio::ip::address const& clientAddr) const
|
||||
{
|
||||
boost::asio::ip::address realmIp;
|
||||
|
||||
// Attempt to send best address for client
|
||||
if (clientAddr.is_loopback())
|
||||
{
|
||||
// Try guessing if realm is also connected locally
|
||||
if (LocalAddress->is_loopback() || ExternalAddress->is_loopback())
|
||||
{
|
||||
realmIp = clientAddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assume that user connecting from the machine that bnetserver is located on
|
||||
// has all realms available in his local network
|
||||
realmIp = *LocalAddress;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (clientAddr.is_v4() && Acore::Net::IsInNetwork(LocalAddress->to_v4(), LocalSubnetMask->to_v4(), clientAddr.to_v4()))
|
||||
{
|
||||
realmIp = *LocalAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
realmIp = *ExternalAddress;
|
||||
}
|
||||
}
|
||||
|
||||
// Return external IP
|
||||
return boost::asio::ip::tcp_endpoint(realmIp, Port);
|
||||
}
|
||||
|
|
@ -6,9 +6,8 @@
|
|||
#ifndef Realm_h__
|
||||
#define Realm_h__
|
||||
|
||||
#include "AsioHacksFwd.h"
|
||||
#include "Common.h"
|
||||
// #include "AsioHacksFwd.h"
|
||||
#include <ace/INET_Addr.h>
|
||||
|
||||
enum RealmFlags
|
||||
{
|
||||
|
|
@ -56,9 +55,9 @@ struct AC_SHARED_API Realm
|
|||
{
|
||||
RealmHandle Id;
|
||||
uint32 Build;
|
||||
std::unique_ptr<ACE_INET_Addr> ExternalAddress;
|
||||
std::unique_ptr<ACE_INET_Addr> LocalAddress;
|
||||
std::unique_ptr<ACE_INET_Addr> LocalSubnetMask;
|
||||
std::unique_ptr<boost::asio::ip::address> ExternalAddress;
|
||||
std::unique_ptr<boost::asio::ip::address> LocalAddress;
|
||||
std::unique_ptr<boost::asio::ip::address> LocalSubnetMask;
|
||||
uint16 Port;
|
||||
std::string Name;
|
||||
uint8 Type;
|
||||
|
|
@ -67,7 +66,7 @@ struct AC_SHARED_API Realm
|
|||
AccountTypes AllowedSecurityLevel;
|
||||
float PopulationLevel;
|
||||
|
||||
// boost::asio::ip::tcp_endpoint GetAddressForClient(boost::asio::ip::address const& clientAddr) const;
|
||||
boost::asio::ip::tcp_endpoint GetAddressForClient(boost::asio::ip::address const& clientAddr) const;
|
||||
};
|
||||
|
||||
#endif // Realm_h__
|
||||
|
|
|
|||
|
|
@ -6,28 +6,38 @@
|
|||
|
||||
#include "RealmList.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DeadlineTimer.h"
|
||||
#include "IoContext.h"
|
||||
#include "Log.h"
|
||||
#include "Optional.h"
|
||||
#include "Resolver.h"
|
||||
#include "Util.h"
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
RealmList::RealmList() :
|
||||
_updateInterval(0) { }
|
||||
|
||||
RealmList* RealmList::instance()
|
||||
RealmList* RealmList::Instance()
|
||||
{
|
||||
static RealmList instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
// Load the realm list from the database
|
||||
void RealmList::Initialize(uint32 updateInterval)
|
||||
void RealmList::Initialize(Acore::Asio::IoContext& ioContext, uint32 updateInterval)
|
||||
{
|
||||
_updateInterval = updateInterval;
|
||||
_updateTimer = std::make_unique<Acore::Asio::DeadlineTimer>(ioContext);
|
||||
_resolver = std::make_unique<Acore::Asio::Resolver>(ioContext);
|
||||
|
||||
LoadBuildInfo();
|
||||
|
||||
// Get the content of the realmlist table in the database
|
||||
UpdateRealms();
|
||||
UpdateRealms(boost::system::error_code());
|
||||
}
|
||||
|
||||
void RealmList::Close()
|
||||
{
|
||||
_updateTimer->cancel();
|
||||
}
|
||||
|
||||
void RealmList::LoadBuildInfo()
|
||||
|
|
@ -72,7 +82,7 @@ void RealmList::LoadBuildInfo()
|
|||
}
|
||||
|
||||
void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, std::string const& name,
|
||||
ACE_INET_Addr&& address, ACE_INET_Addr&& localAddr, ACE_INET_Addr&& localSubmask,
|
||||
boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr, boost::asio::ip::address&& localSubmask,
|
||||
uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population)
|
||||
{
|
||||
// Create new if not exist or update existed
|
||||
|
|
@ -89,38 +99,30 @@ void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, std::string con
|
|||
|
||||
if (!realm.ExternalAddress || *realm.ExternalAddress != address)
|
||||
{
|
||||
realm.ExternalAddress = std::make_unique<ACE_INET_Addr>(std::move(address));
|
||||
realm.ExternalAddress = std::make_unique<boost::asio::ip::address>(std::move(address));
|
||||
}
|
||||
|
||||
if (!realm.LocalAddress || *realm.LocalAddress != localAddr)
|
||||
{
|
||||
realm.LocalAddress = std::make_unique<ACE_INET_Addr>(std::move(localAddr));
|
||||
realm.LocalAddress = std::make_unique<boost::asio::ip::address>(std::move(localAddr));
|
||||
}
|
||||
|
||||
if (!realm.LocalSubnetMask || *realm.LocalSubnetMask != localSubmask)
|
||||
{
|
||||
realm.LocalSubnetMask = std::make_unique<ACE_INET_Addr>(std::move(localSubmask));
|
||||
realm.LocalSubnetMask = std::make_unique<boost::asio::ip::address>(std::move(localSubmask));
|
||||
}
|
||||
|
||||
realm.Port = port;
|
||||
}
|
||||
|
||||
void RealmList::UpdateIfNeed()
|
||||
void RealmList::UpdateRealms(boost::system::error_code const& error)
|
||||
{
|
||||
// maybe disabled or updated recently
|
||||
if (!_updateInterval || _nextUpdateTime > time(nullptr))
|
||||
if (error)
|
||||
{
|
||||
// Skip update if have errors
|
||||
return;
|
||||
}
|
||||
|
||||
_nextUpdateTime = time(nullptr) + _updateInterval;
|
||||
|
||||
// Get the content of the realmlist table in the database
|
||||
UpdateRealms();
|
||||
}
|
||||
|
||||
void RealmList::UpdateRealms()
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "Updating Realm List...");
|
||||
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST);
|
||||
|
|
@ -139,70 +141,87 @@ void RealmList::UpdateRealms()
|
|||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 realmId = fields[0].GetUInt32();
|
||||
std::string name = fields[1].GetString();
|
||||
std::string externalAddressString = fields[2].GetString();
|
||||
std::string localAddressString = fields[3].GetString();
|
||||
std::string localSubmaskString = fields[4].GetString();
|
||||
uint16 port = fields[5].GetUInt16();
|
||||
|
||||
Optional<ACE_INET_Addr> externalAddress = ACE_INET_Addr(port, externalAddressString.c_str(), AF_INET);
|
||||
if (!externalAddress)
|
||||
try
|
||||
{
|
||||
LOG_ERROR("server.authserver", "Could not resolve address %s for realm \"%s\" id %u", externalAddressString.c_str(), name.c_str(), realmId);
|
||||
continue;
|
||||
}
|
||||
Field* fields = result->Fetch();
|
||||
uint32 realmId = fields[0].GetUInt32();
|
||||
std::string name = fields[1].GetString();
|
||||
std::string externalAddressString = fields[2].GetString();
|
||||
std::string localAddressString = fields[3].GetString();
|
||||
std::string localSubmaskString = fields[4].GetString();
|
||||
uint16 port = fields[5].GetUInt16();
|
||||
|
||||
Optional<ACE_INET_Addr> localAddress = ACE_INET_Addr(port, localAddressString.c_str(), AF_INET);
|
||||
if (!localAddress)
|
||||
Optional<boost::asio::ip::tcp::endpoint> externalAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), externalAddressString, "");
|
||||
if (!externalAddress)
|
||||
{
|
||||
LOG_ERROR("server.authserver", "Could not resolve address %s for realm \"%s\" id %u", externalAddressString.c_str(), name.c_str(), realmId);
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> localAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), localAddressString, "");
|
||||
if (!localAddress)
|
||||
{
|
||||
LOG_ERROR("server.authserver", "Could not resolve localAddress %s for realm \"%s\" id %u", localAddressString.c_str(), name.c_str(), realmId);
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> localSubmask = _resolver->Resolve(boost::asio::ip::tcp::v4(), localSubmaskString, "");
|
||||
if (!localSubmask)
|
||||
{
|
||||
LOG_ERROR("server.authserver", "Could not resolve localSubnetMask %s for realm \"%s\" id %u", localSubmaskString.c_str(), name.c_str(), realmId);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8 icon = fields[6].GetUInt8();
|
||||
|
||||
if (icon == REALM_TYPE_FFA_PVP)
|
||||
{
|
||||
icon = REALM_TYPE_PVP;
|
||||
}
|
||||
|
||||
if (icon >= MAX_CLIENT_REALM_TYPE)
|
||||
{
|
||||
icon = REALM_TYPE_NORMAL;
|
||||
}
|
||||
|
||||
RealmFlags flag = RealmFlags(fields[7].GetUInt8());
|
||||
uint8 timezone = fields[8].GetUInt8();
|
||||
uint8 allowedSecurityLevel = fields[9].GetUInt8();
|
||||
float pop = fields[10].GetFloat();
|
||||
uint32 build = fields[11].GetUInt32();
|
||||
|
||||
RealmHandle id{ realmId };
|
||||
|
||||
UpdateRealm(id, build, name, externalAddress->address(), localAddress->address(), localSubmask->address(), port, icon, flag,
|
||||
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
|
||||
|
||||
if (!existingRealms.count(id))
|
||||
{
|
||||
LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
|
||||
}
|
||||
|
||||
existingRealms.erase(id);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
LOG_ERROR("server.authserver", "Could not resolve localAddress %s for realm \"%s\" id %u", localAddressString.c_str(), name.c_str(), realmId);
|
||||
continue;
|
||||
LOG_ERROR("server.authserver", "Realmlist::UpdateRealms has thrown an exception: %s", ex.what());
|
||||
ABORT();
|
||||
}
|
||||
|
||||
Optional<ACE_INET_Addr> localSubmask = ACE_INET_Addr(0, localSubmaskString.c_str(), AF_INET);
|
||||
if (!localSubmask)
|
||||
{
|
||||
LOG_ERROR("server.authserver", "Could not resolve localSubnetMask %s for realm \"%s\" id %u", localSubmaskString.c_str(), name.c_str(), realmId);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8 icon = fields[6].GetUInt8();
|
||||
|
||||
if (icon == REALM_TYPE_FFA_PVP)
|
||||
{
|
||||
icon = REALM_TYPE_PVP;
|
||||
}
|
||||
|
||||
if (icon >= MAX_CLIENT_REALM_TYPE)
|
||||
{
|
||||
icon = REALM_TYPE_NORMAL;
|
||||
}
|
||||
|
||||
RealmFlags flag = RealmFlags(fields[7].GetUInt8());
|
||||
uint8 timezone = fields[8].GetUInt8();
|
||||
uint8 allowedSecurityLevel = fields[9].GetUInt8();
|
||||
float pop = fields[10].GetFloat();
|
||||
uint32 build = fields[11].GetUInt32();
|
||||
|
||||
RealmHandle id{ realmId };
|
||||
|
||||
UpdateRealm(id, build, name, std::move(externalAddress.value()), std::move(localAddress.value()), std::move(localSubmask.value()), port, icon, flag,
|
||||
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
|
||||
|
||||
if (!existingRealms.count(id))
|
||||
{
|
||||
LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
|
||||
}
|
||||
|
||||
existingRealms.erase(id);
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr)
|
||||
LOG_INFO("server.authserver", "Removed realm \"%s\".", itr->second.c_str());
|
||||
|
||||
if (_updateInterval)
|
||||
{
|
||||
_updateTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
|
||||
_updateTimer->async_wait(std::bind(&RealmList::UpdateRealms, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
Realm const* RealmList::GetRealm(RealmHandle const& id) const
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
#include "Realm.h"
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
struct RealmBuildInfo
|
||||
{
|
||||
|
|
@ -25,19 +25,21 @@ struct RealmBuildInfo
|
|||
std::array<uint8, 20> MacHash;
|
||||
};
|
||||
|
||||
namespace boost::system
|
||||
{
|
||||
class error_code;
|
||||
}
|
||||
|
||||
/// Storage object for the list of realms on the server
|
||||
class AC_SHARED_API RealmList
|
||||
{
|
||||
public:
|
||||
typedef std::map<RealmHandle, Realm> RealmMap;
|
||||
|
||||
RealmList();
|
||||
~RealmList() = default;
|
||||
static RealmList* Instance();
|
||||
|
||||
static RealmList* instance();
|
||||
|
||||
void Initialize(uint32 updateInterval);
|
||||
void UpdateIfNeed();
|
||||
void Initialize(Acore::Asio::IoContext& ioContext, uint32 updateInterval);
|
||||
void Close();
|
||||
|
||||
RealmMap const& GetRealms() const { return _realms; }
|
||||
Realm const* GetRealm(RealmHandle const& id) const;
|
||||
|
|
@ -45,18 +47,22 @@ public:
|
|||
RealmBuildInfo const* GetBuildInfo(uint32 build) const;
|
||||
|
||||
private:
|
||||
RealmList();
|
||||
~RealmList() = default;
|
||||
|
||||
void LoadBuildInfo();
|
||||
void UpdateRealms();
|
||||
void UpdateRealms(boost::system::error_code const& error);
|
||||
void UpdateRealm(RealmHandle const& id, uint32 build, std::string const& name,
|
||||
ACE_INET_Addr&& address, ACE_INET_Addr&& localAddr, ACE_INET_Addr&& localSubmask,
|
||||
boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr, boost::asio::ip::address&& localSubmask,
|
||||
uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population);
|
||||
|
||||
std::vector<RealmBuildInfo> _builds;
|
||||
RealmMap _realms;
|
||||
uint32 _updateInterval;
|
||||
time_t _nextUpdateTime;
|
||||
std::unique_ptr<Acore::Asio::DeadlineTimer> _updateTimer;
|
||||
std::unique_ptr<Acore::Asio::Resolver> _resolver;
|
||||
};
|
||||
|
||||
#define sRealmList RealmList::instance()
|
||||
#define sRealmList RealmList::Instance()
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include "Chat.h"
|
||||
#include "CliRunnable.h"
|
||||
#include "Common.h"
|
||||
#include "Configuration/Config.h"
|
||||
#include "Config.h"
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "MapManager.h"
|
||||
|
|
@ -124,7 +124,7 @@ int kb_hit_return()
|
|||
#endif
|
||||
|
||||
/// %Thread start
|
||||
void CliRunnable::run()
|
||||
void CliThread()
|
||||
{
|
||||
///- Display the list of available CLI functions then beep
|
||||
//TC_LOG_INFO("server.worldserver", "");
|
||||
|
|
|
|||
|
|
@ -11,14 +11,8 @@
|
|||
#ifndef __CLIRUNNABLE_H
|
||||
#define __CLIRUNNABLE_H
|
||||
|
||||
#include "Threading.h"
|
||||
|
||||
/// Command Line Interface handling thread
|
||||
class CliRunnable : public Acore::Runnable
|
||||
{
|
||||
public:
|
||||
void run() override;
|
||||
};
|
||||
void CliThread();
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -4,24 +4,49 @@
|
|||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/// \addtogroup Trinityd Trinity Daemon
|
||||
/// @{
|
||||
/// \file
|
||||
|
||||
#include "ACSoap.h"
|
||||
#include "AppenderDB.h"
|
||||
#include "AsyncAcceptor.h"
|
||||
#include "AsyncAuctionListing.h"
|
||||
#include "AvgDiffTracker.h"
|
||||
#include "Banner.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "BigNumber.h"
|
||||
#include "CliRunnable.h"
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
#include "Configuration/Config.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "Master.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "DeadlineTimer.h"
|
||||
#include "GitRevision.h"
|
||||
#include "IoContext.h"
|
||||
#include "MapManager.h"
|
||||
#include "MySQLThreading.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "OpenSSLCrypto.h"
|
||||
#include "OutdoorPvPMgr.h"
|
||||
#include "ProcessPriority.h"
|
||||
#include "RASession.h"
|
||||
#include "RealmList.h"
|
||||
#include "Resolver.h"
|
||||
#include "ScriptLoader.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SecretMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include <ace/Version.h>
|
||||
#include <boost/version.hpp>
|
||||
#include "World.h"
|
||||
#include "WorldSocket.h"
|
||||
#include "WorldSocketMgr.h"
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#ifndef _ACORE_CORE_CONFIG
|
||||
#define _ACORE_CORE_CONFIG "worldserver.conf"
|
||||
#ifdef ELUNA
|
||||
#include "LuaEngine.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -38,6 +63,44 @@ char serviceDescription[] = "AzerothCore World of Warcraft emulator world servic
|
|||
int m_ServiceStatus = -1;
|
||||
#endif
|
||||
|
||||
#ifndef _ACORE_CORE_CONFIG
|
||||
#define _ACORE_CORE_CONFIG "worldserver.conf"
|
||||
#endif
|
||||
|
||||
#define WORLD_SLEEP_CONST 10
|
||||
|
||||
class FreezeDetector
|
||||
{
|
||||
public:
|
||||
FreezeDetector(Acore::Asio::IoContext& ioContext, uint32 maxCoreStuckTime)
|
||||
: _timer(ioContext), _worldLoopCounter(0), _lastChangeMsTime(getMSTime()), _maxCoreStuckTimeInMs(maxCoreStuckTime) { }
|
||||
|
||||
static void Start(std::shared_ptr<FreezeDetector> const& freezeDetector)
|
||||
{
|
||||
freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(5));
|
||||
freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, std::weak_ptr<FreezeDetector>(freezeDetector), std::placeholders::_1));
|
||||
}
|
||||
|
||||
static void Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error);
|
||||
|
||||
private:
|
||||
Acore::Asio::DeadlineTimer _timer;
|
||||
uint32 _worldLoopCounter;
|
||||
uint32 _lastChangeMsTime;
|
||||
uint32 _maxCoreStuckTimeInMs;
|
||||
};
|
||||
|
||||
void SignalHandler(boost::system::error_code const& error, int signalNumber);
|
||||
void ClearOnlineAccounts();
|
||||
bool StartDB();
|
||||
void StopDB();
|
||||
bool LoadRealmInfo(Acore::Asio::IoContext& ioContext);
|
||||
AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext);
|
||||
void ShutdownCLIThread(std::thread* cliThread);
|
||||
void AuctionListingRunnable();
|
||||
void ShutdownAuctionListingThread(std::thread* thread);
|
||||
void WorldUpdateLoop();
|
||||
|
||||
/// Print out the usage string for this program on the console.
|
||||
void usage(const char* prog)
|
||||
{
|
||||
|
|
@ -52,10 +115,11 @@ void usage(const char* prog)
|
|||
#endif
|
||||
}
|
||||
|
||||
/// Launch the Trinity server
|
||||
extern int main(int argc, char** argv)
|
||||
/// Launch the Azeroth server
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Acore::Impl::CurrentServerProcessHolder::_type = SERVER_PROCESS_WORLDSERVER;
|
||||
signal(SIGABRT, &Acore::AbortHandler);
|
||||
|
||||
///- Command line parsing to get the configuration file name
|
||||
std::string configFile = sConfigMgr->GetConfigPath() + std::string(_ACORE_CORE_CONFIG);
|
||||
|
|
@ -124,6 +188,9 @@ extern int main(int argc, char** argv)
|
|||
// Loading modules configs
|
||||
sConfigMgr->LoadModulesConfigs();
|
||||
|
||||
std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
|
||||
|
||||
// Init all logs
|
||||
sLog->RegisterAppender<AppenderDB>();
|
||||
sLog->Initialize();
|
||||
|
||||
|
|
@ -134,23 +201,541 @@ extern int main(int argc, char** argv)
|
|||
},
|
||||
[]()
|
||||
{
|
||||
LOG_INFO("server.worldserver", "> Using configuration file %s.", sConfigMgr->GetFilename().c_str());
|
||||
LOG_INFO("server.worldserver", "> Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||
LOG_INFO("server.worldserver", "> Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
|
||||
LOG_INFO("server.worldserver", "> Using ACE version: %s\n", ACE_VERSION);
|
||||
LOG_INFO("server.worldserver", "> Using configuration file: %s", sConfigMgr->GetFilename().c_str());
|
||||
LOG_INFO("server.worldserver", "> Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||
LOG_INFO("server.worldserver", "> Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
|
||||
}
|
||||
);
|
||||
|
||||
///- and run the 'Master'
|
||||
/// @todo Why do we need this 'Master'? Can't all of this be in the Main as for Realmd?
|
||||
int ret = sMaster->Run();
|
||||
OpenSSLCrypto::threadsSetup();
|
||||
|
||||
std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
|
||||
|
||||
// Seed the OpenSSL's PRNG here.
|
||||
// That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login
|
||||
BigNumber seed;
|
||||
seed.SetRand(16 * 8);
|
||||
|
||||
/// worldserver PID file creation
|
||||
std::string pidFile = sConfigMgr->GetOption<std::string>("PidFile", "");
|
||||
if (!pidFile.empty())
|
||||
{
|
||||
if (uint32 pid = CreatePIDFile(pidFile))
|
||||
LOG_ERROR("server", "Daemon PID: %u\n", pid); // outError for red color in console
|
||||
else
|
||||
{
|
||||
LOG_ERROR("server", "Cannot create PID file %s (possible error: permission)\n", pidFile.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set signal handlers (this must be done before starting IoContext threads, because otherwise they would unblock and exit)
|
||||
boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM);
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
signals.add(SIGBREAK);
|
||||
#endif
|
||||
signals.async_wait(SignalHandler);
|
||||
|
||||
// Start the Boost based thread pool
|
||||
int numThreads = sConfigMgr->GetOption<int32>("ThreadPool", 1);
|
||||
std::shared_ptr<std::vector<std::thread>> threadPool(new std::vector<std::thread>(), [ioContext](std::vector<std::thread>* del)
|
||||
{
|
||||
ioContext->stop();
|
||||
for (std::thread& thr : *del)
|
||||
thr.join();
|
||||
|
||||
delete del;
|
||||
});
|
||||
|
||||
if (numThreads < 1)
|
||||
{
|
||||
numThreads = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
threadPool->push_back(std::thread([ioContext]()
|
||||
{
|
||||
ioContext->run();
|
||||
}));
|
||||
}
|
||||
|
||||
// Set process priority according to configuration settings
|
||||
SetProcessPriority("server.worldserver", sConfigMgr->GetOption<int32>(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetOption<bool>(CONFIG_HIGH_PRIORITY, false));
|
||||
|
||||
// Start the databases
|
||||
if (!StartDB())
|
||||
return 1;
|
||||
|
||||
std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
|
||||
|
||||
// set server offline (not connectable)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
|
||||
|
||||
LoadRealmInfo(*ioContext);
|
||||
|
||||
// Loading modules configs
|
||||
sConfigMgr->PrintLoadedModulesConfigs();
|
||||
|
||||
sScriptMgr->SetScriptLoader(AddScripts);
|
||||
std::shared_ptr<void> sScriptMgrHandle(nullptr, [](void*)
|
||||
{
|
||||
sScriptMgr->Unload();
|
||||
//sScriptReloadMgr->Unload();
|
||||
});
|
||||
|
||||
///- Initialize the World
|
||||
sSecretMgr->Initialize();
|
||||
sWorld->SetInitialWorldSettings();
|
||||
|
||||
std::shared_ptr<void> mapManagementHandle(nullptr, [](void*)
|
||||
{
|
||||
// unload battleground templates before different singletons destroyed
|
||||
sBattlegroundMgr->DeleteAllBattlegrounds();
|
||||
|
||||
sOutdoorPvPMgr->Die(); // unload it before MapManager
|
||||
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
|
||||
|
||||
#ifdef ELUNA
|
||||
Eluna::Uninitialize();
|
||||
#endif
|
||||
});
|
||||
|
||||
// Start the Remote Access port (acceptor) if enabled
|
||||
std::unique_ptr<AsyncAcceptor> raAcceptor;
|
||||
if (sConfigMgr->GetOption<bool>("Ra.Enable", false))
|
||||
{
|
||||
raAcceptor.reset(StartRaSocketAcceptor(*ioContext));
|
||||
}
|
||||
|
||||
// Start soap serving thread if enabled
|
||||
std::shared_ptr<std::thread> soapThread;
|
||||
if (sConfigMgr->GetOption<bool>("SOAP.Enabled", false))
|
||||
{
|
||||
soapThread.reset(new std::thread(ACSoapThread, sConfigMgr->GetOption<std::string>("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetOption<int32>("SOAP.Port", 7878))),
|
||||
[](std::thread* thr)
|
||||
{
|
||||
thr->join();
|
||||
delete thr;
|
||||
});
|
||||
}
|
||||
|
||||
// Launch the worldserver listener socket
|
||||
uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));
|
||||
std::string worldListener = sConfigMgr->GetOption<std::string>("BindIP", "0.0.0.0");
|
||||
|
||||
int networkThreads = sConfigMgr->GetOption<int32>("Network.Threads", 1);
|
||||
|
||||
if (networkThreads <= 0)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Network.Threads must be greater than 0");
|
||||
World::StopNow(ERROR_EXIT_CODE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!sWorldSocketMgr.StartWorldNetwork(*ioContext, worldListener, worldPort, networkThreads))
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Failed to initialize network");
|
||||
World::StopNow(ERROR_EXIT_CODE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> sWorldSocketMgrHandle(nullptr, [](void*)
|
||||
{
|
||||
sWorld->KickAll(); // save and kick all players
|
||||
sWorld->UpdateSessions(1); // real players unload required UpdateSessions call
|
||||
|
||||
sWorldSocketMgr.StopNetwork();
|
||||
|
||||
///- Clean database before leaving
|
||||
ClearOnlineAccounts();
|
||||
});
|
||||
|
||||
// Set server online (allow connecting now)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
|
||||
realm.PopulationLevel = 0.0f;
|
||||
realm.Flags = RealmFlags(realm.Flags & ~uint32(REALM_FLAG_VERSION_MISMATCH));
|
||||
|
||||
// Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec)
|
||||
std::shared_ptr<FreezeDetector> freezeDetector;
|
||||
if (int32 coreStuckTime = sConfigMgr->GetOption<int32>("MaxCoreStuckTime", 60))
|
||||
{
|
||||
freezeDetector = std::make_shared<FreezeDetector>(*ioContext, coreStuckTime * 1000);
|
||||
FreezeDetector::Start(freezeDetector);
|
||||
LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", coreStuckTime);
|
||||
}
|
||||
|
||||
LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", GitRevision::GetFullVersion());
|
||||
|
||||
sScriptMgr->OnStartup();
|
||||
|
||||
// Launch CliRunnable thread
|
||||
std::shared_ptr<std::thread> cliThread;
|
||||
#ifdef _WIN32
|
||||
if (sConfigMgr->GetOption<bool>("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
||||
#else
|
||||
if (sConfigMgr->GetOption<bool>("Console.Enable", true))
|
||||
#endif
|
||||
{
|
||||
cliThread.reset(new std::thread(CliThread), &ShutdownCLIThread);
|
||||
}
|
||||
|
||||
// Launch CliRunnable thread
|
||||
std::shared_ptr<std::thread> auctionLisingThread;
|
||||
auctionLisingThread.reset(new std::thread(AuctionListingRunnable),
|
||||
[](std::thread* thr)
|
||||
{
|
||||
thr->join();
|
||||
delete thr;
|
||||
});
|
||||
|
||||
WorldUpdateLoop();
|
||||
|
||||
// Shutdown starts here
|
||||
threadPool.reset();
|
||||
|
||||
sScriptMgr->OnShutdown();
|
||||
|
||||
// set server offline
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
||||
|
||||
LOG_INFO("server.worldserver", "Halting process...");
|
||||
|
||||
// at sMaster return function exist with codes
|
||||
// 0 - normal shutdown
|
||||
// 1 - shutdown at error
|
||||
// 2 - restart command used, this code can be used by restarter for restart Trinityd
|
||||
// 2 - restart command used, this code can be used by restarter for restart Warheadd
|
||||
|
||||
return ret;
|
||||
return World::GetExitCode();
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// Initialize connection to the databases
|
||||
bool StartDB()
|
||||
{
|
||||
MySQL::Library_Init();
|
||||
|
||||
// Load databases
|
||||
DatabaseLoader loader("server.worldserver");
|
||||
loader
|
||||
.AddDatabase(LoginDatabase, "Login")
|
||||
.AddDatabase(CharacterDatabase, "Character")
|
||||
.AddDatabase(WorldDatabase, "World");
|
||||
|
||||
if (!loader.Load())
|
||||
return false;
|
||||
|
||||
///- Get the realm Id from the configuration file
|
||||
realm.Id.Realm = sConfigMgr->GetIntDefault("RealmID", 0);
|
||||
if (!realm.Id.Realm)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
|
||||
return false;
|
||||
}
|
||||
else if (realm.Id.Realm > 255)
|
||||
{
|
||||
/*
|
||||
* Due to the client only being able to read a realm.Id.Realm
|
||||
* with a size of uint8 we can "only" store up to 255 realms
|
||||
* anything further the client will behave anormaly
|
||||
*/
|
||||
LOG_ERROR("server.worldserver", "Realm ID must range from 1 to 255");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", "Loading world information...");
|
||||
LOG_INFO("server.loading", "> RealmID: %u", realm.Id.Realm);
|
||||
|
||||
///- Clean the database before starting
|
||||
ClearOnlineAccounts();
|
||||
|
||||
///- Insert version info into DB
|
||||
WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", GitRevision::GetFullVersion(), GitRevision::GetHash()); // One-time query
|
||||
|
||||
sWorld->LoadDBVersion();
|
||||
sWorld->LoadDBRevision();
|
||||
|
||||
LOG_INFO("server.loading", "> Version DB world: %s", sWorld->GetDBVersion());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StopDB()
|
||||
{
|
||||
CharacterDatabase.Close();
|
||||
WorldDatabase.Close();
|
||||
LoginDatabase.Close();
|
||||
|
||||
MySQL::Library_End();
|
||||
}
|
||||
|
||||
/// Clear 'online' status for all accounts with characters in this realm
|
||||
void ClearOnlineAccounts()
|
||||
{
|
||||
// Reset online status for all accounts with characters on the current realm
|
||||
// pussywizard: tc query would set online=0 even if logged in on another realm >_>
|
||||
LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online = %u", realm.Id.Realm);
|
||||
|
||||
// Reset online status for all characters
|
||||
CharacterDatabase.DirectExecute("UPDATE characters SET online = 0 WHERE online <> 0");
|
||||
}
|
||||
|
||||
void ShutdownCLIThread(std::thread* cliThread)
|
||||
{
|
||||
if (cliThread != nullptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// First try to cancel any I/O in the CLI thread
|
||||
if (!CancelSynchronousIo(cliThread->native_handle()))
|
||||
{
|
||||
// if CancelSynchronousIo() fails, print the error and try with old way
|
||||
DWORD errorCode = GetLastError();
|
||||
LPCSTR errorBuffer;
|
||||
|
||||
DWORD formatReturnCode = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, errorCode, 0, (LPTSTR)&errorBuffer, 0, nullptr);
|
||||
if (!formatReturnCode)
|
||||
errorBuffer = "Unknown error";
|
||||
|
||||
LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code %u, detail: %s", uint32(errorCode), errorBuffer);
|
||||
|
||||
if (!formatReturnCode)
|
||||
LocalFree((LPSTR)errorBuffer);
|
||||
|
||||
// send keyboard input to safely unblock the CLI thread
|
||||
INPUT_RECORD b[4];
|
||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
b[0].EventType = KEY_EVENT;
|
||||
b[0].Event.KeyEvent.bKeyDown = TRUE;
|
||||
b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
|
||||
b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
|
||||
b[0].Event.KeyEvent.wRepeatCount = 1;
|
||||
|
||||
b[1].EventType = KEY_EVENT;
|
||||
b[1].Event.KeyEvent.bKeyDown = FALSE;
|
||||
b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
|
||||
b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
|
||||
b[1].Event.KeyEvent.wRepeatCount = 1;
|
||||
|
||||
b[2].EventType = KEY_EVENT;
|
||||
b[2].Event.KeyEvent.bKeyDown = TRUE;
|
||||
b[2].Event.KeyEvent.dwControlKeyState = 0;
|
||||
b[2].Event.KeyEvent.uChar.AsciiChar = '\r';
|
||||
b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||
b[2].Event.KeyEvent.wRepeatCount = 1;
|
||||
b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
|
||||
|
||||
b[3].EventType = KEY_EVENT;
|
||||
b[3].Event.KeyEvent.bKeyDown = FALSE;
|
||||
b[3].Event.KeyEvent.dwControlKeyState = 0;
|
||||
b[3].Event.KeyEvent.uChar.AsciiChar = '\r';
|
||||
b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||
b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
|
||||
b[3].Event.KeyEvent.wRepeatCount = 1;
|
||||
DWORD numb;
|
||||
WriteConsoleInput(hStdIn, b, 4, &numb);
|
||||
}
|
||||
#endif
|
||||
cliThread->join();
|
||||
delete cliThread;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldUpdateLoop()
|
||||
{
|
||||
uint32 realCurrTime = 0;
|
||||
uint32 realPrevTime = getMSTime();
|
||||
|
||||
LoginDatabase.WarnAboutSyncQueries(true);
|
||||
CharacterDatabase.WarnAboutSyncQueries(true);
|
||||
WorldDatabase.WarnAboutSyncQueries(true);
|
||||
|
||||
///- While we have not World::m_stopEvent, update the world
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
++World::m_worldLoopCounter;
|
||||
realCurrTime = getMSTime();
|
||||
|
||||
uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime);
|
||||
|
||||
sWorld->Update(diff);
|
||||
realPrevTime = realCurrTime;
|
||||
|
||||
uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime());
|
||||
devDiffTracker.Update(executionTimeDiff);
|
||||
avgDiffTracker.Update(executionTimeDiff > WORLD_SLEEP_CONST ? executionTimeDiff : WORLD_SLEEP_CONST);
|
||||
|
||||
// we know exactly how long it took to update the world, if the update took less than WORLD_SLEEP_CONST, sleep for WORLD_SLEEP_CONST - world update time
|
||||
if (executionTimeDiff < WORLD_SLEEP_CONST)
|
||||
{
|
||||
std::this_thread::sleep_for(Milliseconds(WORLD_SLEEP_CONST - executionTimeDiff));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_ServiceStatus == 0)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
|
||||
while (m_ServiceStatus == 2)
|
||||
Sleep(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
LoginDatabase.WarnAboutSyncQueries(false);
|
||||
CharacterDatabase.WarnAboutSyncQueries(false);
|
||||
WorldDatabase.WarnAboutSyncQueries(false);
|
||||
}
|
||||
|
||||
void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/)
|
||||
{
|
||||
if (!error)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
}
|
||||
|
||||
void FreezeDetector::Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
|
||||
{
|
||||
uint32 curtime = getMSTime();
|
||||
|
||||
uint32 worldLoopCounter = World::m_worldLoopCounter;
|
||||
if (freezeDetector->_worldLoopCounter != worldLoopCounter)
|
||||
{
|
||||
freezeDetector->_lastChangeMsTime = curtime;
|
||||
freezeDetector->_worldLoopCounter = worldLoopCounter;
|
||||
}
|
||||
// possible freeze
|
||||
else if (getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime) > freezeDetector->_maxCoreStuckTimeInMs)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
|
||||
ABORT();
|
||||
}
|
||||
|
||||
freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(1));
|
||||
freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, freezeDetectorRef, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext)
|
||||
{
|
||||
uint16 raPort = uint16(sConfigMgr->GetOption<int32>("Ra.Port", 3443));
|
||||
std::string raListener = sConfigMgr->GetOption<std::string>("Ra.IP", "0.0.0.0");
|
||||
|
||||
AsyncAcceptor* acceptor = new AsyncAcceptor(ioContext, raListener, raPort);
|
||||
if (!acceptor->Bind())
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Failed to bind RA socket acceptor");
|
||||
delete acceptor;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
acceptor->AsyncAccept<RASession>();
|
||||
return acceptor;
|
||||
}
|
||||
|
||||
bool LoadRealmInfo(Acore::Asio::IoContext& ioContext)
|
||||
{
|
||||
QueryResult result = LoginDatabase.PQuery("SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE id = %u", realm.Id.Realm);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
Acore::Asio::Resolver resolver(ioContext);
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
realm.Name = fields[1].GetString();
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> externalAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[2].GetString(), "");
|
||||
if (!externalAddress)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[2].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
realm.ExternalAddress = std::make_unique<boost::asio::ip::address>(externalAddress->address());
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> localAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[3].GetString(), "");
|
||||
if (!localAddress)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[3].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
realm.LocalAddress = std::make_unique<boost::asio::ip::address>(localAddress->address());
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> localSubmask = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[4].GetString(), "");
|
||||
if (!localSubmask)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[4].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
realm.LocalSubnetMask = std::make_unique<boost::asio::ip::address>(localSubmask->address());
|
||||
|
||||
realm.Port = fields[5].GetUInt16();
|
||||
realm.Type = fields[6].GetUInt8();
|
||||
realm.Flags = RealmFlags(fields[7].GetUInt8());
|
||||
realm.Timezone = fields[8].GetUInt8();
|
||||
realm.AllowedSecurityLevel = AccountTypes(fields[9].GetUInt8());
|
||||
realm.PopulationLevel = fields[10].GetFloat();
|
||||
realm.Build = fields[11].GetUInt32();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuctionListingRunnable()
|
||||
{
|
||||
LOG_INFO("server", "Starting up Auction House Listing thread...");
|
||||
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
if (AsyncAuctionListingMgr::IsAuctionListingAllowed())
|
||||
{
|
||||
uint32 diff = AsyncAuctionListingMgr::GetDiff();
|
||||
AsyncAuctionListingMgr::ResetDiff();
|
||||
|
||||
if (AsyncAuctionListingMgr::GetTempList().size() || AsyncAuctionListingMgr::GetList().size())
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetLock());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetTempLock());
|
||||
|
||||
for (auto const& delayEvent : AsyncAuctionListingMgr::GetTempList())
|
||||
AsyncAuctionListingMgr::GetList().emplace_back(delayEvent);
|
||||
|
||||
AsyncAuctionListingMgr::GetTempList().clear();
|
||||
}
|
||||
|
||||
for (auto& itr : AsyncAuctionListingMgr::GetList())
|
||||
{
|
||||
if (itr._msTimer <= diff)
|
||||
itr._msTimer = 0;
|
||||
else
|
||||
itr._msTimer -= diff;
|
||||
}
|
||||
|
||||
for (std::list<AuctionListItemsDelayEvent>::iterator itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr)
|
||||
{
|
||||
if ((*itr)._msTimer != 0)
|
||||
continue;
|
||||
|
||||
if ((*itr).Execute())
|
||||
AsyncAuctionListingMgr::GetList().erase(itr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
LOG_INFO("server", "Auction House Listing thread exiting without problems.");
|
||||
}
|
||||
|
||||
void ShutdownAuctionListingThread(std::thread* thread)
|
||||
{
|
||||
if (thread != nullptr)
|
||||
{
|
||||
thread->join();
|
||||
delete thread;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,436 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/** \file
|
||||
\ingroup Trinityd
|
||||
*/
|
||||
|
||||
#include "ACSoap.h"
|
||||
#include "AsyncAcceptor.h"
|
||||
#include "BigNumber.h"
|
||||
#include "CliRunnable.h"
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "GitRevision.h"
|
||||
#include "IoContext.h"
|
||||
#include "Log.h"
|
||||
#include "Master.h"
|
||||
#include "OpenSSLCrypto.h"
|
||||
#include "RASession.h"
|
||||
#include "Realm.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SignalHandler.h"
|
||||
#include "ScriptLoader.h"
|
||||
#include "Timer.h"
|
||||
#include "Util.h"
|
||||
#include "World.h"
|
||||
#include "WorldRunnable.h"
|
||||
#include "WorldSocket.h"
|
||||
#include "WorldSocketMgr.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "Optional.h"
|
||||
#include "MySQLThreading.h"
|
||||
#include "SecretMgr.h"
|
||||
#include "ProcessPriority.h"
|
||||
#include <ace/Sig_Handler.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "ServiceWin32.h"
|
||||
extern int m_ServiceStatus;
|
||||
#endif
|
||||
|
||||
/// Handle worldservers's termination signals
|
||||
void HandleSignal(int sigNum)
|
||||
{
|
||||
switch (sigNum)
|
||||
{
|
||||
case SIGINT:
|
||||
World::StopNow(RESTART_EXIT_CODE);
|
||||
break;
|
||||
case SIGTERM:
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
case SIGBREAK:
|
||||
if (m_ServiceStatus != 1)
|
||||
#endif
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class FreezeDetectorRunnable : public Acore::Runnable
|
||||
{
|
||||
private:
|
||||
uint32 _loops;
|
||||
uint32 _lastChange;
|
||||
uint32 _delayTime;
|
||||
|
||||
public:
|
||||
FreezeDetectorRunnable(uint32 freezeDelay) : _loops(0), _lastChange(0), _delayTime(freezeDelay) {}
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!_delayTime)
|
||||
return;
|
||||
|
||||
LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", _delayTime / 1000);
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
uint32 curtime = getMSTime();
|
||||
if (_loops != World::m_worldLoopCounter)
|
||||
{
|
||||
_lastChange = curtime;
|
||||
_loops = World::m_worldLoopCounter;
|
||||
}
|
||||
else if (getMSTimeDiff(_lastChange, curtime) > _delayTime)
|
||||
{
|
||||
LOG_INFO("server.worldserver", "World Thread hangs, kicking out server!");
|
||||
ABORT();
|
||||
}
|
||||
|
||||
Acore::Thread::Sleep(1000);
|
||||
}
|
||||
LOG_INFO("server.worldserver", "Anti-freeze thread exiting without problems.");
|
||||
}
|
||||
};
|
||||
|
||||
bool LoadRealmInfo();
|
||||
AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext);
|
||||
|
||||
Master* Master::instance()
|
||||
{
|
||||
static Master instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
/// Main function
|
||||
int Master::Run()
|
||||
{
|
||||
std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
|
||||
|
||||
OpenSSLCrypto::threadsSetup();
|
||||
BigNumber seed1;
|
||||
seed1.SetRand(16 * 8);
|
||||
|
||||
/// worldserver PID file creation
|
||||
std::string pidFile = sConfigMgr->GetOption<std::string>("PidFile", "");
|
||||
if (!pidFile.empty())
|
||||
{
|
||||
if (uint32 pid = CreatePIDFile(pidFile))
|
||||
LOG_ERROR("server.worldserver", "Daemon PID: %u\n", pid); // outError for red color in console
|
||||
else
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Cannot create PID file %s (possible error: permission)\n", pidFile.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set process priority according to configuration settings
|
||||
SetProcessPriority("server.worldserver", sConfigMgr->GetOption<int32>(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetOption<bool>(CONFIG_HIGH_PRIORITY, false));
|
||||
|
||||
///- Start the databases
|
||||
if (!_StartDB())
|
||||
return 1;
|
||||
|
||||
// set server offline (not connectable)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
|
||||
|
||||
LoadRealmInfo();
|
||||
|
||||
// Loading modules configs
|
||||
sConfigMgr->PrintLoadedModulesConfigs();
|
||||
|
||||
///- Initialize the World
|
||||
sSecretMgr->Initialize();
|
||||
sScriptMgr->SetScriptLoader(AddScripts);
|
||||
sWorld->SetInitialWorldSettings();
|
||||
|
||||
sScriptMgr->OnStartup();
|
||||
|
||||
///- Initialize the signal handlers
|
||||
Acore::SignalHandler signalHandler;
|
||||
|
||||
signalHandler.handle_signal(SIGINT, &HandleSignal);
|
||||
signalHandler.handle_signal(SIGTERM, &HandleSignal);
|
||||
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
signalHandler.handle_signal(SIGBREAK, &HandleSignal);
|
||||
#endif
|
||||
|
||||
///- Launch WorldRunnable thread
|
||||
Acore::Thread worldThread(new WorldRunnable);
|
||||
worldThread.setPriority(Acore::Priority_Highest);
|
||||
|
||||
Acore::Thread* cliThread = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (sConfigMgr->GetOption<bool>("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
||||
#else
|
||||
if (sConfigMgr->GetOption<bool>("Console.Enable", true))
|
||||
#endif
|
||||
{
|
||||
///- Launch CliRunnable thread
|
||||
cliThread = new Acore::Thread(new CliRunnable);
|
||||
}
|
||||
|
||||
// pussywizard:
|
||||
Acore::Thread auctionLising_thread(new AuctionListingRunnable);
|
||||
auctionLising_thread.setPriority(Acore::Priority_High);
|
||||
|
||||
// Start the Remote Access port (acceptor) if enabled
|
||||
std::unique_ptr<AsyncAcceptor> raAcceptor;
|
||||
if (sConfigMgr->GetOption<bool>("Ra.Enable", false))
|
||||
raAcceptor.reset(StartRaSocketAcceptor(*ioContext));
|
||||
|
||||
// Start soap serving thread if enabled
|
||||
std::shared_ptr<std::thread> soapThread;
|
||||
if (sConfigMgr->GetOption<bool>("SOAP.Enabled", false))
|
||||
{
|
||||
soapThread.reset(new std::thread(ACSoapThread, sConfigMgr->GetOption<std::string>("SOAP.IP", "127.0.0.1"), sConfigMgr->GetOption<uint16>("SOAP.Port", 7878)),
|
||||
[](std::thread* thr)
|
||||
{
|
||||
thr->join();
|
||||
delete thr;
|
||||
});
|
||||
}
|
||||
|
||||
// Start up freeze catcher thread
|
||||
Acore::Thread* freezeThread = nullptr;
|
||||
if (uint32 freezeDelay = sConfigMgr->GetOption<int32>("MaxCoreStuckTime", 0))
|
||||
{
|
||||
FreezeDetectorRunnable* runnable = new FreezeDetectorRunnable(freezeDelay * 1000);
|
||||
freezeThread = new Acore::Thread(runnable);
|
||||
freezeThread->setPriority(Acore::Priority_Highest);
|
||||
}
|
||||
|
||||
///- Launch the world listener socket
|
||||
uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));
|
||||
std::string bindIp = sConfigMgr->GetOption<std::string>("BindIP", "0.0.0.0");
|
||||
if (sWorldSocketMgr->StartNetwork(worldPort, bindIp.c_str()) == -1)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Failed to start network");
|
||||
World::StopNow(ERROR_EXIT_CODE);
|
||||
// go down and shutdown the server
|
||||
}
|
||||
|
||||
// set server online (allow connecting now)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
|
||||
|
||||
LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", GitRevision::GetFullVersion());
|
||||
|
||||
// when the main thread closes the singletons get unloaded
|
||||
// since worldrunnable uses them, it will crash if unloaded after master
|
||||
worldThread.wait();
|
||||
auctionLising_thread.wait();
|
||||
|
||||
if (freezeThread)
|
||||
{
|
||||
freezeThread->wait();
|
||||
delete freezeThread;
|
||||
}
|
||||
|
||||
// set server offline
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
||||
|
||||
///- Clean database before leaving
|
||||
ClearOnlineAccounts();
|
||||
|
||||
_StopDB();
|
||||
|
||||
LOG_INFO("server.worldserver", "Halting process...");
|
||||
|
||||
if (cliThread)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
// this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API)
|
||||
//_exit(1);
|
||||
// send keyboard input to safely unblock the CLI thread
|
||||
INPUT_RECORD b[4];
|
||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
b[0].EventType = KEY_EVENT;
|
||||
b[0].Event.KeyEvent.bKeyDown = TRUE;
|
||||
b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
|
||||
b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
|
||||
b[0].Event.KeyEvent.wRepeatCount = 1;
|
||||
|
||||
b[1].EventType = KEY_EVENT;
|
||||
b[1].Event.KeyEvent.bKeyDown = FALSE;
|
||||
b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
|
||||
b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
|
||||
b[1].Event.KeyEvent.wRepeatCount = 1;
|
||||
|
||||
b[2].EventType = KEY_EVENT;
|
||||
b[2].Event.KeyEvent.bKeyDown = TRUE;
|
||||
b[2].Event.KeyEvent.dwControlKeyState = 0;
|
||||
b[2].Event.KeyEvent.uChar.AsciiChar = '\r';
|
||||
b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||
b[2].Event.KeyEvent.wRepeatCount = 1;
|
||||
b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
|
||||
|
||||
b[3].EventType = KEY_EVENT;
|
||||
b[3].Event.KeyEvent.bKeyDown = FALSE;
|
||||
b[3].Event.KeyEvent.dwControlKeyState = 0;
|
||||
b[3].Event.KeyEvent.uChar.AsciiChar = '\r';
|
||||
b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||
b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
|
||||
b[3].Event.KeyEvent.wRepeatCount = 1;
|
||||
DWORD numb;
|
||||
WriteConsoleInput(hStdIn, b, 4, &numb);
|
||||
|
||||
cliThread->wait();
|
||||
|
||||
#else
|
||||
|
||||
cliThread->destroy();
|
||||
|
||||
#endif
|
||||
|
||||
delete cliThread;
|
||||
}
|
||||
|
||||
// for some unknown reason, unloading scripts here and not in worldrunnable
|
||||
// fixes a memory leak related to detaching threads from the module
|
||||
//UnloadScriptingModule();
|
||||
|
||||
OpenSSLCrypto::threadsCleanup();
|
||||
// Exit the process with specified return value
|
||||
return World::GetExitCode();
|
||||
}
|
||||
|
||||
/// Initialize connection to the databases
|
||||
bool Master::_StartDB()
|
||||
{
|
||||
MySQL::Library_Init();
|
||||
|
||||
// Load databases
|
||||
DatabaseLoader loader("server.worldserver");
|
||||
loader
|
||||
.AddDatabase(LoginDatabase, "Login")
|
||||
.AddDatabase(CharacterDatabase, "Character")
|
||||
.AddDatabase(WorldDatabase, "World");
|
||||
|
||||
if (!loader.Load())
|
||||
return false;
|
||||
|
||||
///- Get the realm Id from the configuration file
|
||||
realm.Id.Realm = sConfigMgr->GetOption<int32>("RealmID", 0);
|
||||
if (!realm.Id.Realm)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
|
||||
return false;
|
||||
}
|
||||
else if (realm.Id.Realm > 255)
|
||||
{
|
||||
/*
|
||||
* Due to the client only being able to read a realm.Id.Realm
|
||||
* with a size of uint8 we can "only" store up to 255 realms
|
||||
* anything further the client will behave anormaly
|
||||
*/
|
||||
LOG_ERROR("server.worldserver", "Realm ID must range from 1 to 255");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("server.worldserver", "Realm running as realm ID %d", realm.Id.Realm);
|
||||
|
||||
///- Clean the database before starting
|
||||
ClearOnlineAccounts();
|
||||
|
||||
///- Insert version info into DB
|
||||
WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", GitRevision::GetFullVersion(), GitRevision::GetHash()); // One-time query
|
||||
|
||||
sWorld->LoadDBVersion();
|
||||
sWorld->LoadDBRevision();
|
||||
|
||||
LOG_INFO("server.worldserver", "Using World DB: %s", sWorld->GetDBVersion());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Master::_StopDB()
|
||||
{
|
||||
CharacterDatabase.Close();
|
||||
WorldDatabase.Close();
|
||||
LoginDatabase.Close();
|
||||
|
||||
MySQL::Library_End();
|
||||
}
|
||||
|
||||
/// Clear 'online' status for all accounts with characters in this realm
|
||||
void Master::ClearOnlineAccounts()
|
||||
{
|
||||
// Reset online status for all accounts with characters on the current realm
|
||||
// pussywizard: tc query would set online=0 even if logged in on another realm >_>
|
||||
LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online = %u", realm.Id.Realm);
|
||||
|
||||
// Reset online status for all characters
|
||||
CharacterDatabase.DirectExecute("UPDATE characters SET online = 0 WHERE online <> 0");
|
||||
}
|
||||
|
||||
bool LoadRealmInfo()
|
||||
{
|
||||
QueryResult result = LoginDatabase.PQuery("SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE id = %u", realm.Id.Realm);
|
||||
if (!result)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "> Not found realm with ID %u", realm.Id.Realm);
|
||||
return false;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
realm.Name = fields[1].GetString();
|
||||
realm.Port = fields[5].GetUInt16();
|
||||
|
||||
Optional<ACE_INET_Addr> externalAddress = ACE_INET_Addr(realm.Port, fields[2].GetCString(), AF_INET);
|
||||
if (!externalAddress)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[2].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<ACE_INET_Addr> localAddress = ACE_INET_Addr(realm.Port, fields[3].GetCString(), AF_INET);
|
||||
if (!localAddress)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[3].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<ACE_INET_Addr> localSubmask = ACE_INET_Addr(0, fields[4].GetCString(), AF_INET);
|
||||
if (!localSubmask)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[4].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
realm.ExternalAddress = std::make_unique<ACE_INET_Addr>(*externalAddress);
|
||||
realm.LocalAddress = std::make_unique<ACE_INET_Addr>(*localAddress);
|
||||
realm.LocalSubnetMask = std::make_unique<ACE_INET_Addr>(*localSubmask);
|
||||
|
||||
realm.Type = fields[6].GetUInt8();
|
||||
realm.Flags = RealmFlags(fields[7].GetUInt8());
|
||||
realm.Timezone = fields[8].GetUInt8();
|
||||
realm.AllowedSecurityLevel = AccountTypes(fields[9].GetUInt8());
|
||||
realm.PopulationLevel = fields[10].GetFloat();
|
||||
realm.Build = fields[11].GetUInt32();
|
||||
return true;
|
||||
}
|
||||
|
||||
AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext)
|
||||
{
|
||||
uint16 raPort = uint16(sConfigMgr->GetOption<int32>("Ra.Port", 3443));
|
||||
std::string raListener = sConfigMgr->GetOption<std::string>("Ra.IP", "0.0.0.0");
|
||||
|
||||
AsyncAcceptor* acceptor = new AsyncAcceptor(ioContext, raListener, raPort);
|
||||
if (!acceptor->Bind())
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Failed to bind RA socket acceptor");
|
||||
delete acceptor;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
acceptor->AsyncAccept<RASession>();
|
||||
return acceptor;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/// \addtogroup Trinityd
|
||||
/// @{
|
||||
/// \file
|
||||
|
||||
#ifndef _MASTER_H
|
||||
#define _MASTER_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
/// Start the server
|
||||
class Master
|
||||
{
|
||||
public:
|
||||
static Master* instance();
|
||||
int Run();
|
||||
|
||||
private:
|
||||
bool _StartDB();
|
||||
void _StopDB();
|
||||
|
||||
void ClearOnlineAccounts();
|
||||
};
|
||||
|
||||
#define sMaster Master::instance()
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/** \file
|
||||
\ingroup Trinityd
|
||||
*/
|
||||
|
||||
#include "AsyncAuctionListing.h"
|
||||
#include "AvgDiffTracker.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "Common.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "MapManager.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "OutdoorPvPMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Timer.h"
|
||||
#include "World.h"
|
||||
#include "WorldRunnable.h"
|
||||
#include "WorldSocketMgr.h"
|
||||
|
||||
#ifdef ELUNA
|
||||
#include "LuaEngine.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "ServiceWin32.h"
|
||||
extern int m_ServiceStatus;
|
||||
#endif
|
||||
|
||||
/// Heartbeat for the World
|
||||
void WorldRunnable::run()
|
||||
{
|
||||
uint32 realCurrTime = 0;
|
||||
uint32 realPrevTime = getMSTime();
|
||||
|
||||
///- While we have not World::m_stopEvent, update the world
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
++World::m_worldLoopCounter;
|
||||
realCurrTime = getMSTime();
|
||||
|
||||
uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime);
|
||||
|
||||
sWorld->Update( diff );
|
||||
realPrevTime = realCurrTime;
|
||||
|
||||
uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime());
|
||||
devDiffTracker.Update(executionTimeDiff);
|
||||
avgDiffTracker.Update(executionTimeDiff > WORLD_SLEEP_CONST ? executionTimeDiff : WORLD_SLEEP_CONST);
|
||||
|
||||
if (executionTimeDiff < WORLD_SLEEP_CONST)
|
||||
Acore::Thread::Sleep(WORLD_SLEEP_CONST - executionTimeDiff);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_ServiceStatus == 0)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
|
||||
while (m_ServiceStatus == 2)
|
||||
Sleep(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
sScriptMgr->OnShutdown();
|
||||
|
||||
sWorld->KickAll(); // save and kick all players
|
||||
sWorld->UpdateSessions( 1 ); // real players unload required UpdateSessions call
|
||||
|
||||
// unload battleground templates before different singletons destroyed
|
||||
sBattlegroundMgr->DeleteAllBattlegrounds();
|
||||
|
||||
sWorldSocketMgr->StopNetwork();
|
||||
|
||||
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
|
||||
sOutdoorPvPMgr->Die();
|
||||
sScriptMgr->Unload();
|
||||
#ifdef ELUNA
|
||||
Eluna::Uninitialize();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AuctionListingRunnable::run()
|
||||
{
|
||||
LOG_INFO("auctionHouse", "Starting up Auction House Listing thread...");
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
if (AsyncAuctionListingMgr::IsAuctionListingAllowed())
|
||||
{
|
||||
uint32 diff = AsyncAuctionListingMgr::GetDiff();
|
||||
AsyncAuctionListingMgr::ResetDiff();
|
||||
|
||||
if (AsyncAuctionListingMgr::GetTempList().size() || AsyncAuctionListingMgr::GetList().size())
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetLock());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetTempLock());
|
||||
for (std::list<AuctionListItemsDelayEvent>::iterator itr = AsyncAuctionListingMgr::GetTempList().begin(); itr != AsyncAuctionListingMgr::GetTempList().end(); ++itr)
|
||||
AsyncAuctionListingMgr::GetList().push_back( (*itr) );
|
||||
AsyncAuctionListingMgr::GetTempList().clear();
|
||||
}
|
||||
|
||||
for (std::list<AuctionListItemsDelayEvent>::iterator itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr)
|
||||
{
|
||||
if ((*itr)._msTimer <= diff)
|
||||
(*itr)._msTimer = 0;
|
||||
else
|
||||
(*itr)._msTimer -= diff;
|
||||
}
|
||||
|
||||
for (std::list<AuctionListItemsDelayEvent>::iterator itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr)
|
||||
if ((*itr)._msTimer == 0)
|
||||
{
|
||||
if ((*itr).Execute())
|
||||
AsyncAuctionListingMgr::GetList().erase(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Acore::Thread::Sleep(1);
|
||||
}
|
||||
LOG_INFO("auctionHouse", "Auction House Listing thread exiting without problems.");
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/// \addtogroup Trinityd
|
||||
/// @{
|
||||
/// \file
|
||||
|
||||
#ifndef __WORLDRUNNABLE_H
|
||||
#define __WORLDRUNNABLE_H
|
||||
|
||||
#include "Threading.h"
|
||||
|
||||
/// Heartbeat thread for the World
|
||||
class WorldRunnable : public Acore::Runnable
|
||||
{
|
||||
public:
|
||||
void run() override;
|
||||
};
|
||||
|
||||
class AuctionListingRunnable : public Acore::Runnable
|
||||
{
|
||||
public:
|
||||
void run() override;
|
||||
};
|
||||
#endif
|
||||
/// @}
|
||||
|
|
@ -197,6 +197,19 @@ SourceDirectory = ""
|
|||
|
||||
MySQLExecutable = ""
|
||||
|
||||
#
|
||||
# ThreadPool
|
||||
# Description: Number of threads to be used for the global thread pool
|
||||
# The thread pool is currently used for:
|
||||
# - Signal handling
|
||||
# - Remote access
|
||||
# - Database keep-alive ping
|
||||
# - Core freeze check
|
||||
# - World socket networking
|
||||
# Default: 2
|
||||
|
||||
ThreadPool = 2
|
||||
|
||||
#
|
||||
# IPLocationFile
|
||||
# Description: The path to your IP2Location database CSV file.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
void AddScripts() {}
|
||||
bool ArenaSpectator::HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args) { return false; }
|
||||
|
||||
class WorldMock: public IWorld {
|
||||
public:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue