mirror of
https://github.com/mangosfour/server.git
synced 2025-12-12 10:37:03 +00:00
New thread pool reactor implementation and refactoring world daemon. (#8)
* New thread pool reactor implementation and refactoring world daemon. * Fix Linux build. * Fix Windows debug builds.
This commit is contained in:
parent
dfe6c3ec3e
commit
f959ae40b6
30 changed files with 1687 additions and 2126 deletions
|
|
@ -79,7 +79,7 @@ message(
|
||||||
To set an option simply type -D<OPTION>=<VALUE> after 'cmake <srcs>'.
|
To set an option simply type -D<OPTION>=<VALUE> after 'cmake <srcs>'.
|
||||||
Also, you can specify the generator with -G. see 'cmake --help' for more details
|
Also, you can specify the generator with -G. see 'cmake --help' for more details
|
||||||
For example: cmake .. -DDEBUG=1 -DCMAKE_INSTALL_PREFIX=/opt/mangos
|
For example: cmake .. -DDEBUG=1 -DCMAKE_INSTALL_PREFIX=/opt/mangos
|
||||||
|
|
||||||
Note: On UNIX systems, CONF_DIR is relative to the bin folder."
|
Note: On UNIX systems, CONF_DIR is relative to the bin folder."
|
||||||
)
|
)
|
||||||
message("")
|
message("")
|
||||||
|
|
|
||||||
|
|
@ -35,33 +35,10 @@
|
||||||
#include "Config/Config.h"
|
#include "Config/Config.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "AccountMgr.h"
|
#include "AccountMgr.h"
|
||||||
#include "CliRunnable.h"
|
|
||||||
#include "MapManager.h"
|
#include "MapManager.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
|
|
||||||
void utf8print(void* /*arg*/, const char* str)
|
|
||||||
{
|
|
||||||
#if PLATFORM == PLATFORM_WINDOWS
|
|
||||||
wchar_t wtemp_buf[6000];
|
|
||||||
size_t wtemp_len = 6000 - 1;
|
|
||||||
if (!Utf8toWStr(str, strlen(str), wtemp_buf, wtemp_len))
|
|
||||||
{ return; }
|
|
||||||
|
|
||||||
char temp_buf[6000];
|
|
||||||
CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len + 1);
|
|
||||||
printf("%s", temp_buf);
|
|
||||||
#else
|
|
||||||
printf("%s", str);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void commandFinished(void*, bool /*sucess*/)
|
|
||||||
{
|
|
||||||
printf("mangos>");
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete a user account and all associated characters in this realm
|
/// Delete a user account and all associated characters in this realm
|
||||||
/// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account
|
/// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account
|
||||||
bool ChatHandler::HandleAccountDeleteCommand(char* args)
|
bool ChatHandler::HandleAccountDeleteCommand(char* args)
|
||||||
|
|
@ -579,83 +556,3 @@ bool ChatHandler::HandleServerLogLevelCommand(char* args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
#if (PLATFORM == PLATFORM_APPLE) || (PLATFORM == PLATFORM_UNIX)
|
|
||||||
// Non-blocking keypress detector, when return pressed, return 1, else always return 0
|
|
||||||
int kb_hit_return()
|
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
fd_set fds;
|
|
||||||
tv.tv_sec = 0;
|
|
||||||
tv.tv_usec = 0;
|
|
||||||
FD_ZERO(&fds);
|
|
||||||
FD_SET(STDIN_FILENO, &fds);
|
|
||||||
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
|
|
||||||
return FD_ISSET(STDIN_FILENO, &fds);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// %Thread start
|
|
||||||
void CliRunnable::run()
|
|
||||||
{
|
|
||||||
///- Init new SQL thread for the world database (one connection call enough)
|
|
||||||
WorldDatabase.ThreadStart(); // let thread do safe mySQL requests
|
|
||||||
|
|
||||||
char commandbuf[256];
|
|
||||||
|
|
||||||
///- Display the list of available CLI functions then beep
|
|
||||||
sLog.outString();
|
|
||||||
|
|
||||||
if (sConfig.GetBoolDefault("BeepAtStart", true))
|
|
||||||
{ printf("\a"); } // \a = Alert
|
|
||||||
|
|
||||||
// print this here the first time
|
|
||||||
// later it will be printed after command queue updates
|
|
||||||
printf("mangos>");
|
|
||||||
|
|
||||||
///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
|
|
||||||
while (!World::IsStopped())
|
|
||||||
{
|
|
||||||
fflush(stdout);
|
|
||||||
#if (PLATFORM == PLATFORM_APPLE) || (PLATFORM == PLATFORM_UNIX)
|
|
||||||
while (!kb_hit_return() && !World::IsStopped())
|
|
||||||
// With this, we limit CLI to 10commands/second
|
|
||||||
{ usleep(100); }
|
|
||||||
if (World::IsStopped())
|
|
||||||
{ break; }
|
|
||||||
#endif
|
|
||||||
char* command_str = fgets(commandbuf, sizeof(commandbuf), stdin);
|
|
||||||
if (command_str != NULL)
|
|
||||||
{
|
|
||||||
for (int x = 0; command_str[x]; ++x)
|
|
||||||
if (command_str[x] == '\r' || command_str[x] == '\n')
|
|
||||||
{
|
|
||||||
command_str[x] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!*command_str)
|
|
||||||
{
|
|
||||||
printf("mangos>");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string command;
|
|
||||||
if (!consoleToUtf8(command_str, command)) // convert from console encoding to utf8
|
|
||||||
{
|
|
||||||
printf("mangos>");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sWorld.QueueCliCommand(new CliCommandHolder(0, SEC_CONSOLE, NULL, command.c_str(), &utf8print, &commandFinished));
|
|
||||||
}
|
|
||||||
else if (feof(stdin))
|
|
||||||
{
|
|
||||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///- End the database thread
|
|
||||||
WorldDatabase.ThreadEnd(); // free mySQL thread resources
|
|
||||||
}
|
|
||||||
|
|
@ -28,8 +28,8 @@
|
||||||
* \author Derex <derex101@gmail.com>
|
* \author Derex <derex101@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _WORLDSOCKET_H
|
#ifndef MANGOS_H_WORLDSOCKET
|
||||||
#define _WORLDSOCKET_H
|
#define MANGOS_H_WORLDSOCKET
|
||||||
|
|
||||||
#include <ace/Basic_Types.h>
|
#include <ace/Basic_Types.h>
|
||||||
#include <ace/Synch_Traits.h>
|
#include <ace/Synch_Traits.h>
|
||||||
|
|
@ -53,9 +53,10 @@
|
||||||
class ACE_Message_Block;
|
class ACE_Message_Block;
|
||||||
class WorldPacket;
|
class WorldPacket;
|
||||||
class WorldSession;
|
class WorldSession;
|
||||||
|
class WorldSocket;
|
||||||
|
|
||||||
/// Handler that can communicate over stream sockets.
|
|
||||||
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
|
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
|
||||||
|
typedef ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR > WorldAcceptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WorldSocket.
|
* WorldSocket.
|
||||||
|
|
@ -93,16 +94,13 @@ typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
|
||||||
* notification.
|
* notification.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class WorldSocket : protected WorldHandler
|
class WorldSocket : protected WorldHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Declare some friends
|
/// Declare some friends
|
||||||
friend class ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR >;
|
friend class ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR >;
|
||||||
friend class WorldSocketMgr;
|
friend class WorldSocketMgr;
|
||||||
friend class ReactorRunnable;
|
|
||||||
|
|
||||||
/// Declare the acceptor for this class
|
|
||||||
typedef ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR > Acceptor;
|
|
||||||
|
|
||||||
/// Mutex type used for various synchronizations.
|
/// Mutex type used for various synchronizations.
|
||||||
typedef ACE_Thread_Mutex LockType;
|
typedef ACE_Thread_Mutex LockType;
|
||||||
|
|
|
||||||
|
|
@ -27,16 +27,14 @@
|
||||||
* \author Derex <derex101@gmail.com>
|
* \author Derex <derex101@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Config/Config.h"
|
||||||
|
#include "WorldSocket.h"
|
||||||
#include "WorldSocketMgr.h"
|
#include "WorldSocketMgr.h"
|
||||||
|
|
||||||
#include <ace/ACE.h>
|
#include <ace/ACE.h>
|
||||||
#include <ace/Log_Msg.h>
|
|
||||||
#include <ace/Reactor.h>
|
|
||||||
#include <ace/Reactor_Impl.h>
|
|
||||||
#include <ace/TP_Reactor.h>
|
#include <ace/TP_Reactor.h>
|
||||||
#include <ace/Dev_Poll_Reactor.h>
|
|
||||||
#include <ace/Guard_T.h>
|
|
||||||
#include <ace/Atomic_Op.h>
|
|
||||||
#include <ace/os_include/arpa/os_inet.h>
|
#include <ace/os_include/arpa/os_inet.h>
|
||||||
#include <ace/os_include/netinet/os_tcp.h>
|
#include <ace/os_include/netinet/os_tcp.h>
|
||||||
#include <ace/os_include/sys/os_types.h>
|
#include <ace/os_include/sys/os_types.h>
|
||||||
|
|
@ -44,275 +42,99 @@
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "Log.h"
|
WorldSocketMgr::WorldSocketMgr()
|
||||||
#include "Common.h"
|
: m_SockOutKBuff(-1), m_SockOutUBuff(65536), m_UseNoDelay(true),
|
||||||
#include "Config/Config.h"
|
acceptor_(NULL),reactor_(NULL),
|
||||||
#include "Database/DatabaseEnv.h"
|
sockets_()
|
||||||
#include "WorldSocket.h"
|
|
||||||
#include "Opcodes.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
ReactorRunnable() :
|
|
||||||
m_Reactor(0),
|
|
||||||
m_Connections(0),
|
|
||||||
m_ThreadId(-1)
|
|
||||||
{
|
|
||||||
ACE_Reactor_Impl* imp = 0;
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ReactorRunnable()
|
|
||||||
{
|
|
||||||
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 static_cast<long>(m_Connections.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
int AddSocket(WorldSocket* sock)
|
|
||||||
{
|
|
||||||
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, m_NewSockets_Lock, -1);
|
|
||||||
|
|
||||||
++m_Connections;
|
|
||||||
sock->AddReference();
|
|
||||||
sock->reactor(m_Reactor);
|
|
||||||
m_NewSockets.insert(sock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACE_Reactor* GetReactor()
|
|
||||||
{
|
|
||||||
return m_Reactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void AddNewSockets()
|
|
||||||
{
|
|
||||||
ACE_GUARD(ACE_Thread_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())
|
|
||||||
{
|
|
||||||
sock->RemoveReference();
|
|
||||||
--m_Connections;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_Sockets.insert(sock);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_NewSockets.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int svc()
|
|
||||||
{
|
|
||||||
DEBUG_LOG("Network Thread Starting");
|
|
||||||
|
|
||||||
WorldDatabase.ThreadStart();
|
|
||||||
|
|
||||||
MANGOS_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();
|
|
||||||
(*t)->RemoveReference();
|
|
||||||
--m_Connections;
|
|
||||||
m_Sockets.erase(t);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WorldDatabase.ThreadEnd();
|
|
||||||
|
|
||||||
DEBUG_LOG("Network Thread Exitting");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> AtomicInt;
|
|
||||||
typedef std::set<WorldSocket*> SocketSet;
|
|
||||||
|
|
||||||
ACE_Reactor* m_Reactor;
|
|
||||||
AtomicInt m_Connections;
|
|
||||||
int m_ThreadId;
|
|
||||||
|
|
||||||
SocketSet m_Sockets;
|
|
||||||
|
|
||||||
SocketSet m_NewSockets;
|
|
||||||
ACE_Thread_Mutex m_NewSockets_Lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
WorldSocketMgr::WorldSocketMgr():
|
|
||||||
m_NetThreads(0),
|
|
||||||
m_NetThreadsCount(0),
|
|
||||||
m_SockOutKBuff(-1),
|
|
||||||
m_SockOutUBuff(65536),
|
|
||||||
m_UseNoDelay(true),
|
|
||||||
m_Acceptor(0)
|
|
||||||
{
|
|
||||||
InitializeOpcodes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldSocketMgr::~WorldSocketMgr()
|
WorldSocketMgr::~WorldSocketMgr()
|
||||||
{
|
{
|
||||||
delete[] m_NetThreads;
|
if (reactor_) delete reactor_;
|
||||||
delete m_Acceptor;
|
if (acceptor_) delete acceptor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WorldSocketMgr::StartReactiveIO(ACE_UINT16 port, const char* address)
|
|
||||||
|
int WorldSocketMgr::svc()
|
||||||
{
|
{
|
||||||
m_UseNoDelay = sConfig.GetBoolDefault("Network.TcpNodelay", true);
|
DEBUG_LOG("Starting Network Thread");
|
||||||
|
|
||||||
|
SocketSet::iterator i, t;
|
||||||
|
|
||||||
|
while (!reactor_->reactor_event_loop_done())
|
||||||
|
{
|
||||||
|
ACE_Time_Value interval(0, 10000);
|
||||||
|
if (reactor_->run_reactor_event_loop(interval) == -1)
|
||||||
|
{ break; }
|
||||||
|
|
||||||
|
for (i = sockets_->begin(); i != sockets_->end();)
|
||||||
|
{
|
||||||
|
if ((*i)->Update() == -1)
|
||||||
|
{
|
||||||
|
t = i;
|
||||||
|
++i;
|
||||||
|
(*t)->CloseSocket();
|
||||||
|
(*t)->RemoveReference();
|
||||||
|
sockets_->erase(t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ ++i; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("Network Thread Exitting");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int WorldSocketMgr::StartNetwork(ACE_INET_Addr& addr)
|
||||||
|
{
|
||||||
int num_threads = sConfig.GetIntDefault("Network.Threads", 1);
|
int num_threads = sConfig.GetIntDefault("Network.Threads", 1);
|
||||||
|
|
||||||
if (num_threads <= 0)
|
if (num_threads <= 0)
|
||||||
{
|
{
|
||||||
sLog.outError("Network.Threads is wrong in your config file");
|
sLog.outError("Network.Threads is wrong in your config file");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_NetThreadsCount = static_cast<size_t>(num_threads + 1);
|
|
||||||
|
|
||||||
m_NetThreads = new ReactorRunnable[m_NetThreadsCount];
|
|
||||||
|
|
||||||
BASIC_LOG("Max allowed socket connections %d", ACE::max_handles());
|
|
||||||
|
|
||||||
// -1 means use default
|
|
||||||
m_SockOutKBuff = sConfig.GetIntDefault("Network.OutKBuff", -1);
|
|
||||||
|
|
||||||
m_SockOutUBuff = sConfig.GetIntDefault("Network.OutUBuff", 65536);
|
m_SockOutUBuff = sConfig.GetIntDefault("Network.OutUBuff", 65536);
|
||||||
|
|
||||||
if (m_SockOutUBuff <= 0)
|
if (m_SockOutUBuff <= 0)
|
||||||
{
|
{
|
||||||
sLog.outError("Network.OutUBuff is wrong in your config file");
|
sLog.outError("Network.OutUBuff is wrong in your config file");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldSocket::Acceptor* acc = new WorldSocket::Acceptor;
|
// -1 means use default
|
||||||
m_Acceptor = acc;
|
m_SockOutKBuff = sConfig.GetIntDefault("Network.OutKBuff", -1);
|
||||||
|
m_UseNoDelay = sConfig.GetBoolDefault("Network.TcpNodelay", true);
|
||||||
|
|
||||||
ACE_INET_Addr listen_addr(port, address);
|
|
||||||
|
|
||||||
if (acc->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1)
|
ACE_Reactor_Impl* imp = 0;
|
||||||
|
imp = new ACE_TP_Reactor();
|
||||||
|
imp->max_notify_iterations(128);
|
||||||
|
reactor_ = new ACE_Reactor(imp, 1);
|
||||||
|
|
||||||
|
acceptor_ = new WorldAcceptor;
|
||||||
|
|
||||||
|
if (acceptor_->open(addr, reactor_, ACE_NONBLOCK) == -1)
|
||||||
{
|
{
|
||||||
sLog.outError("Failed to open acceptor, check if the port is free");
|
sLog.outError("Failed to open acceptor, check if the port is free");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < m_NetThreadsCount; ++i)
|
if (activate(THR_NEW_LWP | THR_JOINABLE, num_threads) == -1)
|
||||||
m_NetThreads[i].Start();
|
return -1;
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WorldSocketMgr::StartNetwork(ACE_UINT16 port, std::string& address)
|
|
||||||
{
|
|
||||||
m_addr = address;
|
|
||||||
m_port = port;
|
|
||||||
|
|
||||||
if (!sLog.HasLogLevelOrHigher(LOG_LVL_DEBUG))
|
|
||||||
ACE_Log_Msg::instance()->priority_mask(LM_ERROR, ACE_Log_Msg::PROCESS);
|
|
||||||
|
|
||||||
if (StartReactiveIO(port, address.c_str()) == -1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
|
sLog.outString("Max allowed socket connections: %d", ACE::max_handles());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSocketMgr::StopNetwork()
|
void WorldSocketMgr::StopNetwork()
|
||||||
{
|
{
|
||||||
if (m_Acceptor)
|
if (acceptor_) acceptor_->close();
|
||||||
{
|
if (reactor_) reactor_->end_reactor_event_loop();
|
||||||
WorldSocket::Acceptor* acc = dynamic_cast<WorldSocket::Acceptor*>(m_Acceptor);
|
wait();
|
||||||
|
|
||||||
if (acc)
|
|
||||||
acc->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_NetThreadsCount != 0)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_NetThreadsCount; ++i)
|
|
||||||
m_NetThreads[i].Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
int WorldSocketMgr::OnSocketOpen(WorldSocket* sock)
|
||||||
|
|
@ -341,19 +163,9 @@ int WorldSocketMgr::OnSocketOpen(WorldSocket* sock)
|
||||||
|
|
||||||
sock->m_OutBufferSize = static_cast<size_t>(m_SockOutUBuff);
|
sock->m_OutBufferSize = static_cast<size_t>(m_SockOutUBuff);
|
||||||
|
|
||||||
// we skip the Acceptor Thread
|
sock->AddReference();
|
||||||
size_t min = 1;
|
sock->reactor(reactor_);
|
||||||
|
sockets_->insert(sock); //no need for synch here, due to ACE_TSS
|
||||||
|
|
||||||
MANGOS_ASSERT(m_NetThreadsCount >= 1);
|
return 0;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
WorldSocketMgr* WorldSocketMgr::Instance()
|
|
||||||
{
|
|
||||||
return ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>::instance();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,62 +28,51 @@
|
||||||
* \author Derex <derex101@gmail.com>
|
* \author Derex <derex101@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __WORLDSOCKETMGR_H
|
#ifndef MANGOS_H_WORLDSOCKETMGR
|
||||||
#define __WORLDSOCKETMGR_H
|
|
||||||
|
#define MANGOS_H_WORLDSOCKETMGR
|
||||||
|
|
||||||
#include <ace/Basic_Types.h>
|
#include <ace/Basic_Types.h>
|
||||||
#include <ace/Singleton.h>
|
#include <ace/Singleton.h>
|
||||||
#include <ace/Thread_Mutex.h>
|
#include <ace/TSS_T.h>
|
||||||
|
#include <ace/INET_Addr.h>
|
||||||
#include <string>
|
#include <ace/Task.h>
|
||||||
|
#include <ace/Acceptor.h>
|
||||||
|
|
||||||
class WorldSocket;
|
class WorldSocket;
|
||||||
class ReactorRunnable;
|
|
||||||
class ACE_Event_Handler;
|
|
||||||
|
|
||||||
/// Manages all sockets connected to peers and network threads
|
/// This is a pool of threads designed to be used by an ACE_TP_Reactor.
|
||||||
class WorldSocketMgr
|
/// Manages all sockets connected to peers
|
||||||
|
|
||||||
|
class WorldSocketMgr : public ACE_Task_Base
|
||||||
{
|
{
|
||||||
|
friend class ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>;
|
||||||
|
friend class WorldSocket;
|
||||||
public:
|
public:
|
||||||
friend class WorldSocket;
|
int StartNetwork(ACE_INET_Addr& addr);
|
||||||
friend class ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>;
|
|
||||||
|
|
||||||
/// Start network, listen at address:port .
|
|
||||||
int StartNetwork(ACE_UINT16 port, std::string& address);
|
|
||||||
|
|
||||||
/// Stops all network threads, It will wait for all running threads .
|
|
||||||
void StopNetwork();
|
void StopNetwork();
|
||||||
|
|
||||||
/// Wait untill all network threads have "joined" .
|
|
||||||
void Wait();
|
|
||||||
|
|
||||||
std::string& GetBindAddress() { return m_addr; }
|
|
||||||
ACE_UINT16 GetBindPort() { return m_port; }
|
|
||||||
|
|
||||||
/// Make this class singleton .
|
|
||||||
static WorldSocketMgr* Instance();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int OnSocketOpen(WorldSocket* sock);
|
int OnSocketOpen(WorldSocket* sock);
|
||||||
int StartReactiveIO(ACE_UINT16 port, const char* address);
|
virtual int svc();
|
||||||
|
|
||||||
WorldSocketMgr();
|
WorldSocketMgr();
|
||||||
virtual ~WorldSocketMgr();
|
virtual ~WorldSocketMgr();
|
||||||
|
|
||||||
ReactorRunnable* m_NetThreads;
|
private:
|
||||||
size_t m_NetThreadsCount;
|
|
||||||
|
|
||||||
int m_SockOutKBuff;
|
int m_SockOutKBuff;
|
||||||
int m_SockOutUBuff;
|
int m_SockOutUBuff;
|
||||||
bool m_UseNoDelay;
|
bool m_UseNoDelay;
|
||||||
|
|
||||||
std::string m_addr;
|
ACE_Reactor *reactor_;
|
||||||
ACE_UINT16 m_port;
|
WorldAcceptor *acceptor_;
|
||||||
|
|
||||||
|
typedef std::set<WorldSocket*> SocketSet;
|
||||||
|
ACE_TSS<SocketSet> sockets_;
|
||||||
|
|
||||||
ACE_Event_Handler* m_Acceptor;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define sWorldSocketMgr WorldSocketMgr::Instance()
|
#define sWorldSocketMgr ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>::instance()
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@
|
||||||
#ifndef MANGOS_MASS_MAIL_MGR_H
|
#ifndef MANGOS_MASS_MAIL_MGR_H
|
||||||
#define MANGOS_MASS_MAIL_MGR_H
|
#define MANGOS_MASS_MAIL_MGR_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Mail.h"
|
#include "Mail.h"
|
||||||
#include "Policies/Singleton.h"
|
#include "Policies/Singleton.h"
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,8 @@ extern void LoadGameObjectModelList();
|
||||||
|
|
||||||
volatile bool World::m_stopEvent = false;
|
volatile bool World::m_stopEvent = false;
|
||||||
uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE;
|
uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE;
|
||||||
volatile uint32 World::m_worldLoopCounter = 0;
|
|
||||||
|
ACE_Atomic_Op<ACE_Thread_Mutex, uint32> World::m_worldLoopCounter = 0;
|
||||||
|
|
||||||
float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE;
|
float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE;
|
||||||
float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE;
|
float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE;
|
||||||
|
|
|
||||||
|
|
@ -496,7 +496,7 @@ typedef UNORDERED_MAP<uint32, WorldSession*> SessionMap;
|
||||||
class World
|
class World
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static volatile uint32 m_worldLoopCounter;
|
static ACE_Atomic_Op<ACE_Thread_Mutex, uint32> m_worldLoopCounter;
|
||||||
|
|
||||||
World();
|
World();
|
||||||
~World();
|
~World();
|
||||||
|
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
# MaNGOS is a full featured server for World of Warcraft, supporting
|
|
||||||
# the following clients: 1.12.x, 2.4.3, 3.2.5a, 4.2.3 and 5.4.8
|
|
||||||
#
|
|
||||||
# Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
|
||||||
#
|
|
||||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
# ***** END GPL LICENSE BLOCK *****
|
|
||||||
#
|
|
||||||
# World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
|
||||||
# and lore are copyrighted by Blizzard Entertainment, Inc.
|
|
||||||
|
|
||||||
include(MacroMangosSourceGroup)
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Define the vmap library
|
|
||||||
file(GLOB sources *.cpp)
|
|
||||||
file(GLOB headers *.h)
|
|
||||||
|
|
||||||
set(vmap_LIB_SRCS ${sources} ${headers})
|
|
||||||
|
|
||||||
mangos_source_group(${vmap_LIB_SRCS})
|
|
||||||
|
|
||||||
include_directories(
|
|
||||||
${ACE_INCLUDE_DIRS}
|
|
||||||
${MYSQL_INCLUDE_DIR}
|
|
||||||
${CMAKE_SOURCE_DIR}/dep/g3dlite/
|
|
||||||
${CMAKE_SOURCE_DIR}/src/framework/
|
|
||||||
${CMAKE_SOURCE_DIR}/src/shared/
|
|
||||||
${CMAKE_SOURCE_DIR}/src/game/
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
|
||||||
${CMAKE_BINARY_DIR}
|
|
||||||
${CMAKE_BINARY_DIR}/src/shared/
|
|
||||||
)
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Build the vmap library
|
|
||||||
add_library(mangos-vmap STATIC ${vmap_LIB_SRCS})
|
|
||||||
target_link_libraries(mangos-vmap mangos-shared mangos-framework g3dlite ${ACE_LIBRARIES} ${MYSQL_LIBRARIES})
|
|
||||||
|
|
||||||
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
|
|
||||||
set(BUILD_PROPERTIES "-DMANGOS_DEBUG")
|
|
||||||
set_target_properties(mangos-vmap PROPERTIES COMPILE_FLAGS ${BUILD_PROPERTIES})
|
|
||||||
endif()
|
|
||||||
73
src/mangosd/AFThread.cpp
Normal file
73
src/mangosd/AFThread.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||||
|
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ace/OS.h"
|
||||||
|
#include "AFThread.h"
|
||||||
|
#include "World.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
|
||||||
|
AntiFreezeThread::AntiFreezeThread(uint32 delay) : delaytime_(delay)
|
||||||
|
{
|
||||||
|
m_loops = 0;
|
||||||
|
w_loops = 0;
|
||||||
|
m_lastchange = 0;
|
||||||
|
w_lastchange = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AntiFreezeThread::open(void* unused)
|
||||||
|
{
|
||||||
|
activate();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AntiFreezeThread::svc(void)
|
||||||
|
{
|
||||||
|
if (!delaytime_)
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
|
sLog.outString("AntiFreeze Thread started (%u seconds max stuck time)", delaytime_/1000);
|
||||||
|
while (!World::IsStopped())
|
||||||
|
{
|
||||||
|
ACE_OS::sleep(1);
|
||||||
|
|
||||||
|
uint32 curtime = WorldTimer::getMSTime();
|
||||||
|
|
||||||
|
// normal work
|
||||||
|
if (w_loops != World::m_worldLoopCounter.value())
|
||||||
|
{
|
||||||
|
w_lastchange = curtime;
|
||||||
|
w_loops = World::m_worldLoopCounter.value();
|
||||||
|
}
|
||||||
|
// possible freeze
|
||||||
|
else if (WorldTimer::getMSTimeDiff(w_lastchange, curtime) > delaytime_)
|
||||||
|
{
|
||||||
|
sLog.outError("World Thread hangs, kicking out server!");
|
||||||
|
*((uint32 volatile*)NULL) = 0; // bang crash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sLog.outString("AntiFreeze Thread stopped.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
46
src/mangosd/AFThread.h
Normal file
46
src/mangosd/AFThread.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||||
|
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ANTIFREEZE_THREAD
|
||||||
|
#define ANTIFREEZE_THREAD
|
||||||
|
|
||||||
|
#include "ace/Task.h"
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
class AntiFreezeThread : public ACE_Task_Base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AntiFreezeThread(uint32 delay);
|
||||||
|
virtual int open(void*) override;
|
||||||
|
virtual int svc() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 m_loops;
|
||||||
|
uint32 m_lastchange;
|
||||||
|
uint32 w_loops;
|
||||||
|
uint32 w_lastchange;
|
||||||
|
uint32 delaytime_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -23,17 +23,7 @@ set(EXECUTABLE_NAME mangosd)
|
||||||
file(GLOB SRC_GRP_MAIN *.cpp *.h)
|
file(GLOB SRC_GRP_MAIN *.cpp *.h)
|
||||||
source_group("Main" FILES ${SRC_GRP_MAIN})
|
source_group("Main" FILES ${SRC_GRP_MAIN})
|
||||||
|
|
||||||
#Command Line Files
|
|
||||||
file(GLOB SRC_GRP_COMM Comm/*.cpp Comm/*.h)
|
|
||||||
source_group("Command Line" FILES ${SRC_GRP_COMM})
|
|
||||||
|
|
||||||
#Remote Access Files
|
|
||||||
file(GLOB SRC_GRP_RA RA/*.cpp RA/*.h)
|
|
||||||
source_group("Remote Access" FILES ${SRC_GRP_RA})
|
|
||||||
|
|
||||||
set(EXECUTABLE_SRCS
|
set(EXECUTABLE_SRCS
|
||||||
${SRC_GRP_COMM}
|
|
||||||
${SRC_GRP_RA}
|
|
||||||
${SRC_GRP_MAIN}
|
${SRC_GRP_MAIN}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -50,6 +40,7 @@ endif()
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_SOURCE_DIR}/src/shared
|
${CMAKE_SOURCE_DIR}/src/shared
|
||||||
|
${CMAKE_SOURCE_DIR}/src/shared/Config
|
||||||
${CMAKE_SOURCE_DIR}/src/shared/Common
|
${CMAKE_SOURCE_DIR}/src/shared/Common
|
||||||
${CMAKE_SOURCE_DIR}/src/shared/Threading
|
${CMAKE_SOURCE_DIR}/src/shared/Threading
|
||||||
${CMAKE_SOURCE_DIR}/src/shared/Utilities
|
${CMAKE_SOURCE_DIR}/src/shared/Utilities
|
||||||
|
|
|
||||||
141
src/mangosd/CliThread.cpp
Normal file
141
src/mangosd/CliThread.cpp
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
/**
|
||||||
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||||
|
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// \addtogroup mangosd
|
||||||
|
/// @{
|
||||||
|
/// \file
|
||||||
|
|
||||||
|
#include <ace/OS.h>
|
||||||
|
|
||||||
|
#include "CliThread.h"
|
||||||
|
#include "World.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void prompt(void* callback = NULL, bool status = true)
|
||||||
|
{
|
||||||
|
printf("mangos>");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-blocking keypress detector, when return pressed, return 1, else always return 0
|
||||||
|
#if (PLATFORM != PLATFORM_WINDOWS)
|
||||||
|
static int kb_hit_return()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
fd_set fds;
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
|
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
|
||||||
|
return FD_ISSET(STDIN_FILENO, &fds);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
CliThread::CliThread(bool beep) : beep_(beep)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// %Thread start
|
||||||
|
int CliThread::svc()
|
||||||
|
{
|
||||||
|
ACE_OS::sleep(1);
|
||||||
|
|
||||||
|
if (beep_)
|
||||||
|
{ printf("\a"); } // \a = Alert
|
||||||
|
|
||||||
|
prompt();
|
||||||
|
|
||||||
|
///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
|
||||||
|
while (!World::IsStopped())
|
||||||
|
{
|
||||||
|
#if (PLATFORM != PLATFORM_WINDOWS)
|
||||||
|
while (!kb_hit_return() && !World::IsStopped())
|
||||||
|
// With this, we limit CLI to 10 commands/second
|
||||||
|
{ usleep(100); }
|
||||||
|
if (World::IsStopped())
|
||||||
|
{ break; }
|
||||||
|
#endif
|
||||||
|
char* command_str = fgets(buffer_, sizeof(buffer_), stdin);
|
||||||
|
if (command_str != NULL)
|
||||||
|
{
|
||||||
|
for (int x = 0; command_str[x]; ++x)
|
||||||
|
if (command_str[x] == '\r' || command_str[x] == '\n')
|
||||||
|
{
|
||||||
|
command_str[x] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*command_str)
|
||||||
|
{
|
||||||
|
prompt();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string command;
|
||||||
|
if (!consoleToUtf8(command_str, command)) // convert from console encoding to utf8
|
||||||
|
{
|
||||||
|
prompt();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sWorld.QueueCliCommand(new CliCommandHolder(0, SEC_CONSOLE, NULL, command.c_str(), &utf8print, &prompt));
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (feof(stdin))
|
||||||
|
{
|
||||||
|
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CliThread::cli_shutdown()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
// send keyboard input to safely unblock the CLI thread, which is blocked on fgets
|
||||||
|
INPUT_RECORD b;
|
||||||
|
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
|
||||||
|
b.EventType = KEY_EVENT;
|
||||||
|
b.Event.KeyEvent.bKeyDown = TRUE;
|
||||||
|
b.Event.KeyEvent.dwControlKeyState = 0;
|
||||||
|
b.Event.KeyEvent.uChar.AsciiChar = '\r';
|
||||||
|
b.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||||
|
b.Event.KeyEvent.wRepeatCount = 1;
|
||||||
|
b.Event.KeyEvent.wVirtualScanCode = 0x1c;
|
||||||
|
|
||||||
|
DWORD numb = 0;
|
||||||
|
BOOL ret = WriteConsoleInput(hStdIn, &b, 1, &numb);
|
||||||
|
|
||||||
|
wait();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
@ -26,24 +26,25 @@
|
||||||
/// @{
|
/// @{
|
||||||
/// \file
|
/// \file
|
||||||
|
|
||||||
#ifndef MANGOS_H_CLIRUNNABLE
|
#ifndef MANGOS_H_CLITHREAD
|
||||||
#define MANGOS_H_CLIRUNNABLE
|
#define MANGOS_H_CLITHREAD
|
||||||
|
|
||||||
#include "Common.h"
|
#include "ace/Task.h"
|
||||||
#include "Threading.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Command Line Interface handling thread
|
* @brief Command Line Interface handling thread
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class CliRunnable : public ACE_Based::Runnable
|
class CliThread : public ACE_Task_Base
|
||||||
{
|
{
|
||||||
|
enum { BUFFSIZE = 256 };
|
||||||
public:
|
public:
|
||||||
/**
|
CliThread(bool);
|
||||||
* @brief
|
virtual int svc() override;
|
||||||
*
|
void cli_shutdown();
|
||||||
*/
|
private:
|
||||||
void run() override;
|
char buffer_[BUFFSIZE];
|
||||||
|
bool beep_;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
/// @}
|
/// @}
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
/**
|
|
||||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
|
||||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*
|
|
||||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
|
||||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// \addtogroup mangosd Mangos Daemon
|
|
||||||
/// @{
|
|
||||||
/// \file
|
|
||||||
|
|
||||||
#include "Common.h"
|
|
||||||
#include "Database/DatabaseEnv.h"
|
|
||||||
#include "Config/Config.h"
|
|
||||||
#include "ProgressBar.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Master.h"
|
|
||||||
#include "SystemConfig.h"
|
|
||||||
#include "AuctionHouseBot.h"
|
|
||||||
#include "revision.h"
|
|
||||||
#include <openssl/opensslv.h>
|
|
||||||
#include <openssl/crypto.h>
|
|
||||||
#include <ace/Version.h>
|
|
||||||
#include <ace/Get_Opt.h>
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#include "ServiceWin32.h"
|
|
||||||
char serviceName[] = "mangosd";
|
|
||||||
char serviceLongName[] = "MaNGOS world service";
|
|
||||||
char serviceDescription[] = "Massive Network Game Object Server";
|
|
||||||
const char RAW_VMAP_MAGIC[] = "VMAPc06"; /**< used in extracted vmap files with raw data */
|
|
||||||
/*
|
|
||||||
* -1 - not in service mode
|
|
||||||
* 0 - stopped
|
|
||||||
* 1 - running
|
|
||||||
* 2 - paused
|
|
||||||
*/
|
|
||||||
int m_ServiceStatus = -1;
|
|
||||||
#else
|
|
||||||
#include "PosixDaemon.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DatabaseType WorldDatabase; ///< Accessor to the world database
|
|
||||||
DatabaseType CharacterDatabase; ///< Accessor to the character database
|
|
||||||
DatabaseType LoginDatabase; ///< Accessor to the realm/login database
|
|
||||||
|
|
||||||
uint32 realmID; ///< Id of the realm
|
|
||||||
|
|
||||||
/// Print out the usage string for this program on the console.
|
|
||||||
void usage(const char* prog)
|
|
||||||
{
|
|
||||||
sLog.outString("Usage: \n %s [<options>]\n"
|
|
||||||
" -v, --version print version and exist\n\r"
|
|
||||||
" -c config_file use config_file as configuration file\n\r"
|
|
||||||
" -a, --ahbot config_file use config_file as ahbot configuration file\n\r"
|
|
||||||
#ifdef WIN32
|
|
||||||
" Running as service functions:\n\r"
|
|
||||||
" -s run run as service\n\r"
|
|
||||||
" -s install install service\n\r"
|
|
||||||
" -s uninstall uninstall service\n\r"
|
|
||||||
#else
|
|
||||||
" Running as daemon functions:\n\r"
|
|
||||||
" -s run run as daemon\n\r"
|
|
||||||
" -s stop stop daemon\n\r"
|
|
||||||
#endif
|
|
||||||
, prog);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Launch the mangos server
|
|
||||||
extern int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
///- Command line parsing
|
|
||||||
char const* cfg_file = MANGOSD_CONFIG_LOCATION;
|
|
||||||
|
|
||||||
char const* options = ":a:c:s:";
|
|
||||||
|
|
||||||
ACE_Get_Opt cmd_opts(argc, argv, options);
|
|
||||||
cmd_opts.long_option("version", 'v', ACE_Get_Opt::NO_ARG);
|
|
||||||
cmd_opts.long_option("ahbot", 'a', ACE_Get_Opt::ARG_REQUIRED);
|
|
||||||
|
|
||||||
char serviceDaemonMode = '\0';
|
|
||||||
|
|
||||||
int option;
|
|
||||||
while ((option = cmd_opts()) != EOF)
|
|
||||||
{
|
|
||||||
switch (option)
|
|
||||||
{
|
|
||||||
case 'a':
|
|
||||||
sAuctionBotConfig.SetConfigFileName(cmd_opts.opt_arg());
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
cfg_file = cmd_opts.opt_arg();
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
printf("%s\n", REVISION_NR);
|
|
||||||
return 0;
|
|
||||||
case 's':
|
|
||||||
{
|
|
||||||
const char* mode = cmd_opts.opt_arg();
|
|
||||||
|
|
||||||
if (!strcmp(mode, "run"))
|
|
||||||
{ serviceDaemonMode = 'r'; }
|
|
||||||
#ifdef WIN32
|
|
||||||
else if (!strcmp(mode, "install"))
|
|
||||||
{ serviceDaemonMode = 'i'; }
|
|
||||||
else if (!strcmp(mode, "uninstall"))
|
|
||||||
{ serviceDaemonMode = 'u'; }
|
|
||||||
#else
|
|
||||||
else if (!strcmp(mode, "stop"))
|
|
||||||
{ serviceDaemonMode = 's'; }
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sLog.outError("Runtime-Error: -%c unsupported argument %s", cmd_opts.opt_opt(), mode);
|
|
||||||
usage(argv[0]);
|
|
||||||
Log::WaitBeforeContinueIfNeed();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ':':
|
|
||||||
sLog.outError("Runtime-Error: -%c option requires an input argument", cmd_opts.opt_opt());
|
|
||||||
usage(argv[0]);
|
|
||||||
Log::WaitBeforeContinueIfNeed();
|
|
||||||
return 1;
|
|
||||||
default:
|
|
||||||
sLog.outError("Runtime-Error: bad format of commandline arguments");
|
|
||||||
usage(argv[0]);
|
|
||||||
Log::WaitBeforeContinueIfNeed();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WIN32 // windows service command need execute before config read
|
|
||||||
switch (serviceDaemonMode)
|
|
||||||
{
|
|
||||||
case 'i':
|
|
||||||
if (WinServiceInstall())
|
|
||||||
{ sLog.outString("Installing service"); }
|
|
||||||
return 1;
|
|
||||||
case 'u':
|
|
||||||
if (WinServiceUninstall())
|
|
||||||
{ sLog.outString("Uninstalling service"); }
|
|
||||||
return 1;
|
|
||||||
case 'r':
|
|
||||||
WinServiceRun();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!sConfig.SetSource(cfg_file))
|
|
||||||
{
|
|
||||||
sLog.outError("Could not find configuration file %s.", cfg_file);
|
|
||||||
Log::WaitBeforeContinueIfNeed();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef WIN32 // posix daemon commands need apply after config read
|
|
||||||
switch (serviceDaemonMode)
|
|
||||||
{
|
|
||||||
case 'r':
|
|
||||||
startDaemon();
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
stopDaemon();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sLog.outString("%s [world-daemon]", REVISION_NR);
|
|
||||||
sLog.outString("<Ctrl-C> to stop.\n"
|
|
||||||
" __ __ _ _ ___ ___ ___ \n"
|
|
||||||
" | \\/ |__ _| \\| |/ __|/ _ \\/ __| \n"
|
|
||||||
" | |\\/| / _` | .` | (_ | (_) \\__ \\ \n"
|
|
||||||
" |_| |_\\__,_|_|\\_|\\___|\\___/|___/ \n"
|
|
||||||
" _____ _ \n"
|
|
||||||
" For help and support please visit: |_ _| |_ _ _ ___ ___ \n"
|
|
||||||
" Website: https://getmangos.eu | | | ' \\| '_/ -_) -_) \n"
|
|
||||||
" Forum / Wiki: https://getmangos.eu |_| |_||_|_| \\___\\___| \n"
|
|
||||||
);
|
|
||||||
sLog.outString("Using configuration file %s.", cfg_file);
|
|
||||||
|
|
||||||
DETAIL_LOG("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
|
||||||
if (SSLeay() < 0x009080bfL)
|
|
||||||
{
|
|
||||||
DETAIL_LOG("WARNING: Outdated version of OpenSSL lib. Logins to server may not work!");
|
|
||||||
DETAIL_LOG("WARNING: Minimal required version [OpenSSL 0.9.8k]");
|
|
||||||
}
|
|
||||||
|
|
||||||
DETAIL_LOG("Using ACE: %s", ACE_VERSION);
|
|
||||||
|
|
||||||
///- Set progress bars show mode
|
|
||||||
BarGoLink::SetOutputState(sConfig.GetBoolDefault("ShowProgressBars", true));
|
|
||||||
|
|
||||||
///- and run the 'Master'
|
|
||||||
/// \todo Why do we need this 'Master'? Can't all of this be in the Main as for Realmd?
|
|
||||||
int code = sMaster.Run();
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return code;
|
|
||||||
|
|
||||||
// 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 mangosd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
@ -1,620 +0,0 @@
|
||||||
/**
|
|
||||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
|
||||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*
|
|
||||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
|
||||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** \file
|
|
||||||
\ingroup mangosd
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
#include "PosixDaemon.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "WorldSocketMgr.h"
|
|
||||||
#include "Common.h"
|
|
||||||
#include "Master.h"
|
|
||||||
#include "WorldSocket.h"
|
|
||||||
#include "WorldRunnable.h"
|
|
||||||
#include "World.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Timer.h"
|
|
||||||
#include "Policies/Singleton.h"
|
|
||||||
#include "SystemConfig.h"
|
|
||||||
#include "Config/Config.h"
|
|
||||||
#include "Database/DatabaseEnv.h"
|
|
||||||
#include "Comm/CliRunnable.h"
|
|
||||||
#include "RA/RASocket.h"
|
|
||||||
#include "Util.h"
|
|
||||||
#include "revision.h"
|
|
||||||
#include "MassMailMgr.h"
|
|
||||||
#include "DBCStores.h"
|
|
||||||
#include "ScriptMgr.h"
|
|
||||||
#ifdef ENABLE_SOAP
|
|
||||||
#include "SOAP/MaNGOSsoap.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ace/OS_NS_signal.h>
|
|
||||||
#include <ace/TP_Reactor.h>
|
|
||||||
#include <ace/Dev_Poll_Reactor.h>
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#include "ServiceWin32.h"
|
|
||||||
extern int m_ServiceStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
INSTANTIATE_SINGLETON_1(Master);
|
|
||||||
|
|
||||||
volatile uint32 Master::m_masterLoopCounter = 0;
|
|
||||||
|
|
||||||
class FreezeDetectorRunnable : public ACE_Based::Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FreezeDetectorRunnable() { _delaytime = 0; }
|
|
||||||
uint32 m_loops, m_lastchange;
|
|
||||||
uint32 w_loops, w_lastchange;
|
|
||||||
uint32 _delaytime;
|
|
||||||
void SetDelayTime(uint32 t) { _delaytime = t; }
|
|
||||||
void run(void)
|
|
||||||
{
|
|
||||||
if (!_delaytime)
|
|
||||||
{ return; }
|
|
||||||
sLog.outString("Starting up anti-freeze thread (%u seconds max stuck time)...", _delaytime / 1000);
|
|
||||||
m_loops = 0;
|
|
||||||
w_loops = 0;
|
|
||||||
m_lastchange = 0;
|
|
||||||
w_lastchange = 0;
|
|
||||||
while (!World::IsStopped())
|
|
||||||
{
|
|
||||||
ACE_Based::Thread::Sleep(1000);
|
|
||||||
|
|
||||||
uint32 curtime = WorldTimer::getMSTime();
|
|
||||||
// DEBUG_LOG("anti-freeze: time=%u, counters=[%u; %u]",curtime,Master::m_masterLoopCounter,World::m_worldLoopCounter);
|
|
||||||
|
|
||||||
// normal work
|
|
||||||
if (w_loops != World::m_worldLoopCounter)
|
|
||||||
{
|
|
||||||
w_lastchange = curtime;
|
|
||||||
w_loops = World::m_worldLoopCounter;
|
|
||||||
}
|
|
||||||
// possible freeze
|
|
||||||
else if (WorldTimer::getMSTimeDiff(w_lastchange, curtime) > _delaytime)
|
|
||||||
{
|
|
||||||
sLog.outError("World Thread hangs, kicking out server!");
|
|
||||||
*((uint32 volatile*)NULL) = 0; // bang crash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sLog.outString("Anti-freeze thread exiting without problems.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class RARunnable : public ACE_Based::Runnable
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
ACE_Reactor* m_Reactor;
|
|
||||||
RASocket::Acceptor* m_Acceptor;
|
|
||||||
public:
|
|
||||||
RARunnable()
|
|
||||||
{
|
|
||||||
ACE_Reactor_Impl* imp = 0;
|
|
||||||
|
|
||||||
#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 /* 1= delete implementation so we don't have to care */);
|
|
||||||
|
|
||||||
m_Acceptor = new RASocket::Acceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
~RARunnable()
|
|
||||||
{
|
|
||||||
delete m_Reactor;
|
|
||||||
delete m_Acceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run()
|
|
||||||
{
|
|
||||||
uint16 raport = sConfig.GetIntDefault("Ra.Port", 3443);
|
|
||||||
std::string stringip = sConfig.GetStringDefault("Ra.IP", "0.0.0.0");
|
|
||||||
|
|
||||||
ACE_INET_Addr listen_addr(raport, stringip.c_str());
|
|
||||||
|
|
||||||
if (m_Acceptor->open(listen_addr, m_Reactor, ACE_NONBLOCK) == -1)
|
|
||||||
{
|
|
||||||
sLog.outError("MaNGOS RA can not bind to port %d on %s", raport, stringip.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
sLog.outString("Starting Remote access listener on port %d on %s", raport, stringip.c_str());
|
|
||||||
|
|
||||||
while (!m_Reactor->reactor_event_loop_done())
|
|
||||||
{
|
|
||||||
ACE_Time_Value interval(0, 10000);
|
|
||||||
|
|
||||||
if (m_Reactor->run_reactor_event_loop(interval) == -1)
|
|
||||||
{ break; }
|
|
||||||
|
|
||||||
if (World::IsStopped())
|
|
||||||
{
|
|
||||||
m_Acceptor->close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sLog.outString("RARunnable thread ended");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Master::Master()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Master::~Master()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Main function
|
|
||||||
int Master::Run()
|
|
||||||
{
|
|
||||||
/// worldd PID file creation
|
|
||||||
std::string pidfile = sConfig.GetStringDefault("PidFile", "");
|
|
||||||
if (!pidfile.empty())
|
|
||||||
{
|
|
||||||
uint32 pid = CreatePIDFile(pidfile);
|
|
||||||
if (!pid)
|
|
||||||
{
|
|
||||||
sLog.outError("Can not create PID file %s.\n", pidfile.c_str());
|
|
||||||
Log::WaitBeforeContinueIfNeed();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sLog.outString("Daemon PID: %u\n", pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Start the databases
|
|
||||||
if (!_StartDB())
|
|
||||||
{
|
|
||||||
Log::WaitBeforeContinueIfNeed();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Set Realm to Offline, if crash happens. Only used once.
|
|
||||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET realmflags = realmflags | %u WHERE id = '%u'", REALM_FLAG_OFFLINE, realmID);
|
|
||||||
|
|
||||||
///- Initialize the World
|
|
||||||
sWorld.SetInitialWorldSettings();
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
detachDaemon();
|
|
||||||
#endif
|
|
||||||
// server loaded successfully => enable async DB requests
|
|
||||||
// this is done to forbid any async transactions during server startup!
|
|
||||||
CharacterDatabase.AllowAsyncTransactions();
|
|
||||||
WorldDatabase.AllowAsyncTransactions();
|
|
||||||
LoginDatabase.AllowAsyncTransactions();
|
|
||||||
|
|
||||||
///- Catch termination signals
|
|
||||||
_HookSignals();
|
|
||||||
|
|
||||||
///- Launch WorldRunnable thread
|
|
||||||
ACE_Based::Thread world_thread(new WorldRunnable);
|
|
||||||
world_thread.setPriority(ACE_Based::Highest);
|
|
||||||
|
|
||||||
// set realmbuilds depend on mangosd expected builds, and set server online
|
|
||||||
{
|
|
||||||
std::string builds = AcceptableClientBuildsListStr();
|
|
||||||
LoginDatabase.escape_string(builds);
|
|
||||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET realmflags = realmflags & ~(%u), population = 0, realmbuilds = '%s' WHERE id = '%u'", REALM_FLAG_OFFLINE, builds.c_str(), realmID);
|
|
||||||
}
|
|
||||||
|
|
||||||
ACE_Based::Thread* cliThread = NULL;
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
|
||||||
#else
|
|
||||||
if (sConfig.GetBoolDefault("Console.Enable", true))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
///- Launch CliRunnable thread
|
|
||||||
cliThread = new ACE_Based::Thread(new CliRunnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
ACE_Based::Thread* rar_thread = NULL;
|
|
||||||
if (sConfig.GetBoolDefault("Ra.Enable", false))
|
|
||||||
{
|
|
||||||
rar_thread = new ACE_Based::Thread(new RARunnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Handle affinity for multiple processors and process priority on Windows
|
|
||||||
#ifdef WIN32
|
|
||||||
{
|
|
||||||
HANDLE hProcess = GetCurrentProcess();
|
|
||||||
|
|
||||||
uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
|
|
||||||
if (Aff > 0)
|
|
||||||
{
|
|
||||||
ULONG_PTR appAff;
|
|
||||||
ULONG_PTR sysAff;
|
|
||||||
|
|
||||||
if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
|
|
||||||
{
|
|
||||||
ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
|
|
||||||
|
|
||||||
if (!curAff)
|
|
||||||
{
|
|
||||||
sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for mangosd. Accessible processors bitmask (hex): %x", Aff, appAff);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (SetProcessAffinityMask(hProcess, curAff))
|
|
||||||
{ sLog.outString("Using processors (bitmask, hex): %x", curAff); }
|
|
||||||
else
|
|
||||||
{ sLog.outError("Can't set used processors (hex): %x", curAff); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sLog.outString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
|
|
||||||
|
|
||||||
// if(Prio && (m_ServiceStatus == -1)/* need set to default process priority class in service mode*/)
|
|
||||||
if (Prio)
|
|
||||||
{
|
|
||||||
if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
|
|
||||||
{ sLog.outString("mangosd process priority class set to HIGH"); }
|
|
||||||
else
|
|
||||||
{ sLog.outError("Can't set mangosd process priority class."); }
|
|
||||||
sLog.outString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ENABLE_SOAP
|
|
||||||
///- Start soap serving thread
|
|
||||||
ACE_Based::Thread* soap_thread = NULL;
|
|
||||||
|
|
||||||
if (sConfig.GetBoolDefault("SOAP.Enabled", false))
|
|
||||||
{
|
|
||||||
MaNGOSsoapRunnable* runnable = new MaNGOSsoapRunnable();
|
|
||||||
|
|
||||||
runnable->setListenArguments(sConfig.GetStringDefault("SOAP.IP", "127.0.0.1"), sConfig.GetIntDefault("SOAP.Port", 7878));
|
|
||||||
soap_thread = new ACE_Based::Thread(runnable);
|
|
||||||
}
|
|
||||||
#else /* ENABLE_SOAP */
|
|
||||||
if (sConfig.GetBoolDefault("SOAP.Enabled", false))
|
|
||||||
{
|
|
||||||
sLog.outError("SOAP is enabled but wasn't included during compilation, not activating it.");
|
|
||||||
}
|
|
||||||
#endif /* ENABLE_SOAP */
|
|
||||||
|
|
||||||
///- Start up freeze catcher thread
|
|
||||||
ACE_Based::Thread* freeze_thread = NULL;
|
|
||||||
if (uint32 freeze_delay = sConfig.GetIntDefault("MaxCoreStuckTime", 0))
|
|
||||||
{
|
|
||||||
FreezeDetectorRunnable* fdr = new FreezeDetectorRunnable();
|
|
||||||
fdr->SetDelayTime(freeze_delay * 1000);
|
|
||||||
freeze_thread = new ACE_Based::Thread(fdr);
|
|
||||||
freeze_thread->setPriority(ACE_Based::Highest);
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Launch the world listener socket
|
|
||||||
uint16 wsport = sWorld.getConfig(CONFIG_UINT32_PORT_WORLD);
|
|
||||||
std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0");
|
|
||||||
|
|
||||||
if (sWorldSocketMgr->StartNetwork(wsport, bind_ip) == -1)
|
|
||||||
{
|
|
||||||
sLog.outError("Failed to start network");
|
|
||||||
Log::WaitBeforeContinueIfNeed();
|
|
||||||
World::StopNow(ERROR_EXIT_CODE);
|
|
||||||
// go down and shutdown the server
|
|
||||||
}
|
|
||||||
|
|
||||||
sWorldSocketMgr->Wait();
|
|
||||||
|
|
||||||
///- Stop freeze protection before shutdown tasks
|
|
||||||
if (freeze_thread)
|
|
||||||
{
|
|
||||||
freeze_thread->destroy();
|
|
||||||
delete freeze_thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_SOAP
|
|
||||||
///- Stop soap thread
|
|
||||||
if (soap_thread)
|
|
||||||
{
|
|
||||||
soap_thread->wait();
|
|
||||||
soap_thread->destroy();
|
|
||||||
delete soap_thread;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///- Set server offline in realmlist
|
|
||||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET realmflags = realmflags | %u WHERE id = '%u'", REALM_FLAG_OFFLINE, realmID);
|
|
||||||
|
|
||||||
///- Remove signal handling before leaving
|
|
||||||
_UnhookSignals();
|
|
||||||
|
|
||||||
// when the main thread closes the singletons get unloaded
|
|
||||||
// since worldrunnable uses them, it will crash if unloaded after master
|
|
||||||
world_thread.wait();
|
|
||||||
|
|
||||||
if (rar_thread)
|
|
||||||
{
|
|
||||||
rar_thread->wait();
|
|
||||||
rar_thread->destroy();
|
|
||||||
delete rar_thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Clean account database before leaving
|
|
||||||
clearOnlineAccounts();
|
|
||||||
|
|
||||||
// send all still queued mass mails (before DB connections shutdown)
|
|
||||||
sMassMailMgr.Update(true);
|
|
||||||
|
|
||||||
///- Wait for DB delay threads to end
|
|
||||||
CharacterDatabase.HaltDelayThread();
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
LoginDatabase.HaltDelayThread();
|
|
||||||
|
|
||||||
sLog.outString("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[5];
|
|
||||||
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;
|
|
||||||
BOOL ret = WriteConsoleInput(hStdIn, b, 4, &numb);
|
|
||||||
|
|
||||||
cliThread->wait();
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
cliThread->destroy();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
delete cliThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is done to make sure that we cleanup our so file before it's
|
|
||||||
// unloaded automatically, since the ~ScriptMgr() is called to late
|
|
||||||
// as it's allocated with static storage.
|
|
||||||
sScriptMgr.UnloadScriptLibrary();
|
|
||||||
|
|
||||||
///- Exit the process with specified return value
|
|
||||||
return World::GetExitCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize connection to the databases
|
|
||||||
bool Master::_StartDB()
|
|
||||||
{
|
|
||||||
///- Get world database info from configuration file
|
|
||||||
std::string dbstring = sConfig.GetStringDefault("WorldDatabaseInfo", "");
|
|
||||||
int nConnections = sConfig.GetIntDefault("WorldDatabaseConnections", 1);
|
|
||||||
if (dbstring.empty())
|
|
||||||
{
|
|
||||||
sLog.outError("Database not specified in configuration file");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sLog.outString("World Database total connections: %i", nConnections + 1);
|
|
||||||
|
|
||||||
///- Initialise the world database
|
|
||||||
if (!WorldDatabase.Initialize(dbstring.c_str(), nConnections))
|
|
||||||
{
|
|
||||||
sLog.outError("Can not connect to world database %s", dbstring.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Check the World database version
|
|
||||||
if(!WorldDatabase.CheckDatabaseVersion(DATABASE_WORLD))
|
|
||||||
{
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo", "");
|
|
||||||
nConnections = sConfig.GetIntDefault("CharacterDatabaseConnections", 1);
|
|
||||||
if (dbstring.empty())
|
|
||||||
{
|
|
||||||
sLog.outError("Character Database not specified in configuration file");
|
|
||||||
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sLog.outString("Character Database total connections: %i", nConnections + 1);
|
|
||||||
|
|
||||||
///- Initialise the Character database
|
|
||||||
if (!CharacterDatabase.Initialize(dbstring.c_str(), nConnections))
|
|
||||||
{
|
|
||||||
sLog.outError("Can not connect to Character database %s", dbstring.c_str());
|
|
||||||
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Check the Character database version
|
|
||||||
if (!CharacterDatabase.CheckDatabaseVersion(DATABASE_CHARACTER))
|
|
||||||
{
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
CharacterDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Get login database info from configuration file
|
|
||||||
dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
|
|
||||||
nConnections = sConfig.GetIntDefault("LoginDatabaseConnections", 1);
|
|
||||||
if (dbstring.empty())
|
|
||||||
{
|
|
||||||
sLog.outError("Login database not specified in configuration file");
|
|
||||||
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
CharacterDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Initialise the login database
|
|
||||||
sLog.outString("Login Database total connections: %i", nConnections + 1);
|
|
||||||
if (!LoginDatabase.Initialize(dbstring.c_str(), nConnections))
|
|
||||||
{
|
|
||||||
sLog.outError("Can not connect to login database %s", dbstring.c_str());
|
|
||||||
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
CharacterDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Check the Realm database version
|
|
||||||
if (!LoginDatabase.CheckDatabaseVersion(DATABASE_REALMD))
|
|
||||||
{
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
CharacterDatabase.HaltDelayThread();
|
|
||||||
LoginDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sLog.outString();
|
|
||||||
|
|
||||||
///- Get the realm Id from the configuration file
|
|
||||||
realmID = sConfig.GetIntDefault("RealmID", 0);
|
|
||||||
if (!realmID)
|
|
||||||
{
|
|
||||||
sLog.outError("Realm ID not defined in configuration file");
|
|
||||||
|
|
||||||
///- Wait for already started DB delay threads to end
|
|
||||||
WorldDatabase.HaltDelayThread();
|
|
||||||
CharacterDatabase.HaltDelayThread();
|
|
||||||
LoginDatabase.HaltDelayThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sLog.outString("Realm running as realm ID %d", realmID);
|
|
||||||
sLog.outString();
|
|
||||||
|
|
||||||
///- Clean the database before starting
|
|
||||||
clearOnlineAccounts();
|
|
||||||
|
|
||||||
sWorld.LoadDBVersion();
|
|
||||||
|
|
||||||
sLog.outString("Using World DB: %s", sWorld.GetDBVersion());
|
|
||||||
sLog.outString();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear 'online' status for all accounts with characters in this realm
|
|
||||||
void Master::clearOnlineAccounts()
|
|
||||||
{
|
|
||||||
// Cleanup online status for characters hosted at current realm
|
|
||||||
/// \todo Only accounts with characters logged on *this* realm should have online status reset. Move the online column from 'account' to 'realmcharacters'?
|
|
||||||
LoginDatabase.PExecute("UPDATE account SET active_realm_id = 0, os = '' WHERE active_realm_id = '%u'", realmID);
|
|
||||||
|
|
||||||
CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0");
|
|
||||||
|
|
||||||
// Battleground instance ids reset at server restart
|
|
||||||
CharacterDatabase.Execute("UPDATE character_battleground_data SET instance_id = 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle termination signals
|
|
||||||
void Master::_OnSignal(int s)
|
|
||||||
{
|
|
||||||
switch (s)
|
|
||||||
{
|
|
||||||
case SIGINT:
|
|
||||||
World::StopNow(RESTART_EXIT_CODE);
|
|
||||||
break;
|
|
||||||
case SIGTERM:
|
|
||||||
#ifdef _WIN32
|
|
||||||
case SIGBREAK:
|
|
||||||
#endif
|
|
||||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
signal(s, _OnSignal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define hook '_OnSignal' for all termination signals
|
|
||||||
void Master::_HookSignals()
|
|
||||||
{
|
|
||||||
signal(SIGINT, _OnSignal);
|
|
||||||
signal(SIGTERM, _OnSignal);
|
|
||||||
#ifdef _WIN32
|
|
||||||
signal(SIGBREAK, _OnSignal);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unhook the signals before leaving
|
|
||||||
void Master::_UnhookSignals()
|
|
||||||
{
|
|
||||||
signal(SIGINT, 0);
|
|
||||||
signal(SIGTERM, 0);
|
|
||||||
#ifdef _WIN32
|
|
||||||
signal(SIGBREAK, 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
@ -1,330 +0,0 @@
|
||||||
/**
|
|
||||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
|
||||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*
|
|
||||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
|
||||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** \file
|
|
||||||
\ingroup mangosd
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Common.h"
|
|
||||||
#include "Database/DatabaseEnv.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "RASocket.h"
|
|
||||||
#include "World.h"
|
|
||||||
#include "Config/Config.h"
|
|
||||||
#include "Util.h"
|
|
||||||
#include "AccountMgr.h"
|
|
||||||
#include "Language.h"
|
|
||||||
#include "ObjectMgr.h"
|
|
||||||
|
|
||||||
/// RASocket constructor
|
|
||||||
RASocket::RASocket()
|
|
||||||
: RAHandler(),
|
|
||||||
pendingCommands(0, USYNC_THREAD, "pendingCommands"),
|
|
||||||
outActive(false),
|
|
||||||
inputBufferLen(0),
|
|
||||||
outputBufferLen(0),
|
|
||||||
stage(NONE)
|
|
||||||
{
|
|
||||||
///- Get the config parameters
|
|
||||||
bSecure = sConfig.GetBoolDefault("RA.Secure", true);
|
|
||||||
bStricted = sConfig.GetBoolDefault("RA.Stricted", false);
|
|
||||||
iMinLevel = AccountTypes(sConfig.GetIntDefault("RA.MinLevel", SEC_ADMINISTRATOR));
|
|
||||||
reference_counting_policy().value(ACE_Event_Handler::Reference_Counting_Policy::ENABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RASocket destructor
|
|
||||||
RASocket::~RASocket()
|
|
||||||
{
|
|
||||||
peer().close();
|
|
||||||
sLog.outRALog("Connection was closed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Accept an incoming connection
|
|
||||||
int RASocket::open(void*)
|
|
||||||
{
|
|
||||||
if (reactor()->register_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK) == -1)
|
|
||||||
{
|
|
||||||
sLog.outError("RASocket::open: unable to register client handler errno = %s", ACE_OS::strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACE_INET_Addr remote_addr;
|
|
||||||
|
|
||||||
if (peer().get_remote_addr(remote_addr) == -1)
|
|
||||||
{
|
|
||||||
sLog.outError("RASocket::open: peer ().get_remote_addr errno = %s", ACE_OS::strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sLog.outRALog("Incoming connection from %s.", remote_addr.get_host_addr());
|
|
||||||
|
|
||||||
///- print Motd
|
|
||||||
sendf(sWorld.GetMotd());
|
|
||||||
sendf("\r\n");
|
|
||||||
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_USER));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int RASocket::close(int)
|
|
||||||
{
|
|
||||||
if (closing_)
|
|
||||||
{ return -1; }
|
|
||||||
DEBUG_LOG("RASocket::close");
|
|
||||||
shutdown();
|
|
||||||
|
|
||||||
closing_ = true;
|
|
||||||
|
|
||||||
remove_reference();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int RASocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask)
|
|
||||||
{
|
|
||||||
if (closing_)
|
|
||||||
{ return -1; }
|
|
||||||
DEBUG_LOG("RASocket::handle_close");
|
|
||||||
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, outBufferLock, -1);
|
|
||||||
|
|
||||||
closing_ = true;
|
|
||||||
|
|
||||||
if (h == ACE_INVALID_HANDLE)
|
|
||||||
{ peer().close_writer(); }
|
|
||||||
remove_reference();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int RASocket::handle_output(ACE_HANDLE)
|
|
||||||
{
|
|
||||||
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, outBufferLock, -1);
|
|
||||||
|
|
||||||
if (closing_)
|
|
||||||
{ return -1; }
|
|
||||||
|
|
||||||
if (!outputBufferLen)
|
|
||||||
{
|
|
||||||
if (reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1)
|
|
||||||
{
|
|
||||||
sLog.outError("RASocket::handle_output: error while cancel_wakeup");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
outActive = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#ifdef MSG_NOSIGNAL
|
|
||||||
ssize_t n = peer().send(outputBuffer, outputBufferLen, MSG_NOSIGNAL);
|
|
||||||
#else
|
|
||||||
ssize_t n = peer().send(outputBuffer, outputBufferLen);
|
|
||||||
#endif // MSG_NOSIGNAL
|
|
||||||
|
|
||||||
if (n <= 0)
|
|
||||||
{ return -1; }
|
|
||||||
|
|
||||||
ACE_OS::memmove(outputBuffer, outputBuffer + n, outputBufferLen - n);
|
|
||||||
|
|
||||||
outputBufferLen -= n;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read data from the network
|
|
||||||
int RASocket::handle_input(ACE_HANDLE)
|
|
||||||
{
|
|
||||||
DEBUG_LOG("RASocket::handle_input");
|
|
||||||
if (closing_)
|
|
||||||
{
|
|
||||||
sLog.outError("Called RASocket::handle_input with closing_ = true");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t readBytes = peer().recv(inputBuffer + inputBufferLen, RA_BUFF_SIZE - inputBufferLen - 1);
|
|
||||||
|
|
||||||
if (readBytes <= 0)
|
|
||||||
{
|
|
||||||
DEBUG_LOG("read " SIZEFMTD " bytes in RASocket::handle_input", readBytes);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Discard data after line break or line feed
|
|
||||||
bool gotenter = false;
|
|
||||||
for (; readBytes > 0 ; --readBytes)
|
|
||||||
{
|
|
||||||
char c = inputBuffer[inputBufferLen];
|
|
||||||
if (c == '\r' || c == '\n')
|
|
||||||
{
|
|
||||||
gotenter = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++inputBufferLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gotenter)
|
|
||||||
{
|
|
||||||
inputBuffer[inputBufferLen] = 0;
|
|
||||||
inputBufferLen = 0;
|
|
||||||
switch (stage)
|
|
||||||
{
|
|
||||||
/// <ul> <li> If the input is '<username>'
|
|
||||||
case NONE:
|
|
||||||
{
|
|
||||||
std::string szLogin = inputBuffer;
|
|
||||||
|
|
||||||
accId = sAccountMgr.GetId(szLogin);
|
|
||||||
|
|
||||||
///- If the user is not found, deny access
|
|
||||||
if (!accId)
|
|
||||||
{
|
|
||||||
sendf("-No such user.\r\n");
|
|
||||||
sLog.outRALog("User %s does not exist.", szLogin.c_str());
|
|
||||||
if (bSecure)
|
|
||||||
{
|
|
||||||
handle_output();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sendf("\r\n");
|
|
||||||
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_USER));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
accAccessLevel = sAccountMgr.GetSecurity(accId);
|
|
||||||
|
|
||||||
///- if gmlevel is too low, deny access
|
|
||||||
if (accAccessLevel < iMinLevel)
|
|
||||||
{
|
|
||||||
sendf("-Not enough privileges.\r\n");
|
|
||||||
sLog.outRALog("User %s has no privilege.", szLogin.c_str());
|
|
||||||
if (bSecure)
|
|
||||||
{
|
|
||||||
handle_output();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sendf("\r\n");
|
|
||||||
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_USER));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- allow by remotely connected admin use console level commands dependent from config setting
|
|
||||||
if (accAccessLevel >= SEC_ADMINISTRATOR && !bStricted)
|
|
||||||
{ accAccessLevel = SEC_CONSOLE; }
|
|
||||||
|
|
||||||
stage = LG;
|
|
||||||
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_PASS));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
///<li> If the input is '<password>' (and the user already gave his username)
|
|
||||||
case LG:
|
|
||||||
{
|
|
||||||
// login+pass ok
|
|
||||||
std::string pw = inputBuffer;
|
|
||||||
|
|
||||||
if (sAccountMgr.CheckPassword(accId, pw))
|
|
||||||
{
|
|
||||||
stage = OK;
|
|
||||||
|
|
||||||
sendf("+Logged in.\r\n");
|
|
||||||
sLog.outRALog("User account %u has logged in.", accId);
|
|
||||||
sendf("mangos>");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
///- Else deny access
|
|
||||||
sendf("-Wrong pass.\r\n");
|
|
||||||
sLog.outRALog("User account %u has failed to log in.", accId);
|
|
||||||
if (bSecure)
|
|
||||||
{
|
|
||||||
handle_output();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sendf("\r\n");
|
|
||||||
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_PASS));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
///<li> If user is logged, parse and execute the command
|
|
||||||
case OK:
|
|
||||||
if (strlen(inputBuffer))
|
|
||||||
{
|
|
||||||
sLog.outRALog("Got '%s' cmd.", inputBuffer);
|
|
||||||
if (strncmp(inputBuffer, "quit", 4) == 0)
|
|
||||||
{ return -1; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CliCommandHolder* cmd = new CliCommandHolder(accId, accAccessLevel, this, inputBuffer, &RASocket::zprint, &RASocket::commandFinished);
|
|
||||||
sWorld.QueueCliCommand(cmd);
|
|
||||||
pendingCommands.acquire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ sendf("mangos>"); }
|
|
||||||
break;
|
|
||||||
///</ul>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// no enter yet? wait for next input...
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Output function
|
|
||||||
void RASocket::zprint(void* callbackArg, const char* szText)
|
|
||||||
{
|
|
||||||
if (!szText)
|
|
||||||
{ return; }
|
|
||||||
|
|
||||||
((RASocket*)callbackArg)->sendf(szText);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RASocket::commandFinished(void* callbackArg, bool /*success*/)
|
|
||||||
{
|
|
||||||
RASocket* raSocket = (RASocket*)callbackArg;
|
|
||||||
raSocket->sendf("mangos>");
|
|
||||||
raSocket->pendingCommands.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
int RASocket::sendf(const char* msg)
|
|
||||||
{
|
|
||||||
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, outBufferLock, -1);
|
|
||||||
|
|
||||||
if (closing_)
|
|
||||||
{ return -1; }
|
|
||||||
|
|
||||||
int msgLen = strlen(msg);
|
|
||||||
|
|
||||||
if (msgLen + outputBufferLen > RA_BUFF_SIZE)
|
|
||||||
{ return -1; }
|
|
||||||
|
|
||||||
ACE_OS::memcpy(outputBuffer + outputBufferLen, msg, msgLen);
|
|
||||||
outputBufferLen += msgLen;
|
|
||||||
|
|
||||||
if (!outActive)
|
|
||||||
{
|
|
||||||
if (reactor()->schedule_wakeup
|
|
||||||
(this, ACE_Event_Handler::WRITE_MASK) == -1)
|
|
||||||
{
|
|
||||||
sLog.outError("RASocket::sendf error while schedule_wakeup");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
outActive = true;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
/**
|
|
||||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
|
||||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*
|
|
||||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
|
||||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// \addtogroup mangosd
|
|
||||||
/// @{
|
|
||||||
/// \file
|
|
||||||
|
|
||||||
#ifndef MANGOS_H_RASOCKET
|
|
||||||
#define MANGOS_H_RASOCKET
|
|
||||||
|
|
||||||
#include "Common.h"
|
|
||||||
#include <ace/Synch_Traits.h>
|
|
||||||
#include <ace/Svc_Handler.h>
|
|
||||||
#include <ace/SOCK_Acceptor.h>
|
|
||||||
#include <ace/Acceptor.h>
|
|
||||||
#include <ace/Thread_Mutex.h>
|
|
||||||
#include <ace/Semaphore.h>
|
|
||||||
|
|
||||||
#define RA_BUFF_SIZE 8192
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remote Administration socket
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef ACE_Svc_Handler < ACE_SOCK_STREAM, ACE_NULL_SYNCH> RAHandler;
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class RASocket: protected RAHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ACE_Semaphore pendingCommands; /**< TODO */
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef ACE_Acceptor<RASocket, ACE_SOCK_ACCEPTOR > Acceptor;
|
|
||||||
friend class ACE_Acceptor<RASocket, ACE_SOCK_ACCEPTOR >;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
* @param
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
int sendf(const char*);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* @brief things called by ACE framework.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
RASocket(void);
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
virtual ~RASocket(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Called on open ,the void* is the acceptor.
|
|
||||||
*
|
|
||||||
* @param
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
virtual int open(void*) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Called on failures inside of the acceptor, don't call from your code.
|
|
||||||
*
|
|
||||||
* @param int
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
virtual int close(int);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Called when we can read from the socket.
|
|
||||||
*
|
|
||||||
* @param ACE_HANDLE
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Called when the socket can write.
|
|
||||||
*
|
|
||||||
* @param ACE_HANDLE
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Called when connection is closed or error happens.
|
|
||||||
*
|
|
||||||
* @param ACE_HANDLE
|
|
||||||
* @param ACE_Reactor_Mask
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE,
|
|
||||||
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool outActive; /**< TODO */
|
|
||||||
|
|
||||||
char inputBuffer[RA_BUFF_SIZE]; /**< TODO */
|
|
||||||
uint32 inputBufferLen; /**< TODO */
|
|
||||||
|
|
||||||
ACE_Thread_Mutex outBufferLock; /**< TODO */
|
|
||||||
char outputBuffer[RA_BUFF_SIZE]; /**< TODO */
|
|
||||||
uint32 outputBufferLen; /**< TODO */
|
|
||||||
|
|
||||||
uint32 accId; /**< TODO */
|
|
||||||
AccountTypes accAccessLevel; /**< TODO */
|
|
||||||
bool bSecure; /**< kick on wrong pass, non exist. user OR user with no priv. will protect from DOS, bruteforce attacks */
|
|
||||||
bool bStricted; /**< not allow execute console only commands (SEC_CONSOLE) remotly */
|
|
||||||
AccountTypes iMinLevel; /**< TODO */
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
NONE, // initial value
|
|
||||||
LG, // only login was entered
|
|
||||||
OK // both login and pass were given, they were correct and user has enough priv.
|
|
||||||
} stage; /**< TODO */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
* @param callbackArg
|
|
||||||
* @param szText
|
|
||||||
*/
|
|
||||||
static void zprint(void* callbackArg, const char* szText);
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
* @param callbackArg
|
|
||||||
* @param success
|
|
||||||
*/
|
|
||||||
static void commandFinished(void* callbackArg, bool success);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
/// @}
|
|
||||||
402
src/mangosd/RAThread.cpp
Normal file
402
src/mangosd/RAThread.cpp
Normal file
|
|
@ -0,0 +1,402 @@
|
||||||
|
/**
|
||||||
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||||
|
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// \addtogroup mangosd
|
||||||
|
/// @{
|
||||||
|
/// \file
|
||||||
|
|
||||||
|
#include <ace/Synch_Traits.h>
|
||||||
|
#include <ace/Svc_Handler.h>
|
||||||
|
#include <ace/Thread_Mutex.h>
|
||||||
|
#include <ace/TP_Reactor.h>
|
||||||
|
#include <ace/Event_Handler.h>
|
||||||
|
|
||||||
|
#include "RAThread.h"
|
||||||
|
|
||||||
|
#include "AccountMgr.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "World.h"
|
||||||
|
#include "Util.h"
|
||||||
|
#include "Language.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
|
|
||||||
|
|
||||||
|
class RASocket: protected ACE_Svc_Handler < ACE_SOCK_STREAM, ACE_NULL_SYNCH>
|
||||||
|
{
|
||||||
|
typedef ACE_Svc_Handler < ACE_SOCK_STREAM, ACE_NULL_SYNCH> Base;
|
||||||
|
|
||||||
|
enum { RA_BUFF_SIZE = 8192 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
friend class ACE_Acceptor<RASocket, ACE_SOCK_ACCEPTOR >;
|
||||||
|
|
||||||
|
int sendf(const char* msg)
|
||||||
|
{
|
||||||
|
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, outBufferLock, -1);
|
||||||
|
|
||||||
|
if (closing_)
|
||||||
|
{ return -1; }
|
||||||
|
|
||||||
|
int msgLen = strlen(msg);
|
||||||
|
|
||||||
|
if (msgLen + outputBufferLen > RA_BUFF_SIZE)
|
||||||
|
{ return -1; }
|
||||||
|
|
||||||
|
ACE_OS::memcpy(outputBuffer + outputBufferLen, msg, msgLen);
|
||||||
|
outputBufferLen += msgLen;
|
||||||
|
|
||||||
|
if (!outActive)
|
||||||
|
{
|
||||||
|
if (reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1)
|
||||||
|
{
|
||||||
|
sLog.outError("RASocket::sendf error while schedule_wakeup");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
outActive = true;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RASocket(void) : Base(),outBufferLock(), outActive(false), inputBufferLen(0),
|
||||||
|
outputBufferLen(0), stage(NONE)
|
||||||
|
{
|
||||||
|
bSecure = sConfig.GetBoolDefault("RA.Secure", true);
|
||||||
|
bStricted = sConfig.GetBoolDefault("RA.Stricted", false);
|
||||||
|
iMinLevel = AccountTypes(sConfig.GetIntDefault("RA.MinLevel", SEC_ADMINISTRATOR));
|
||||||
|
reference_counting_policy().value(ACE_Event_Handler::Reference_Counting_Policy::ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~RASocket(void)
|
||||||
|
{
|
||||||
|
peer().close();
|
||||||
|
sLog.outRALog("Connection was closed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int open(void* unused) override
|
||||||
|
{
|
||||||
|
if (reactor()->register_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK) == -1)
|
||||||
|
{
|
||||||
|
sLog.outError("RASocket::open: unable to accept connection from client, error = %s", ACE_OS::strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ACE_INET_Addr remote_addr;
|
||||||
|
|
||||||
|
if (peer().get_remote_addr(remote_addr) == -1)
|
||||||
|
{
|
||||||
|
sLog.outError("RASocket::open: peer ().get_remote_addr errno = %s", ACE_OS::strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sLog.outRALog("Incoming connection from %s.", remote_addr.get_host_addr());
|
||||||
|
|
||||||
|
///- print Motd
|
||||||
|
sendf(sWorld.GetMotd());
|
||||||
|
sendf("\r\n");
|
||||||
|
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_USER));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int close(u_long unused) override
|
||||||
|
{
|
||||||
|
if (closing_)
|
||||||
|
{ return -1; }
|
||||||
|
|
||||||
|
shutdown();
|
||||||
|
|
||||||
|
closing_ = true;
|
||||||
|
|
||||||
|
remove_reference();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE) override
|
||||||
|
{
|
||||||
|
if (closing_)
|
||||||
|
{
|
||||||
|
sLog.outError("Called RASocket::handle_input with closing_ = true");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t readBytes = peer().recv(inputBuffer + inputBufferLen, RA_BUFF_SIZE - inputBufferLen - 1);
|
||||||
|
|
||||||
|
if (readBytes <= 0)
|
||||||
|
{
|
||||||
|
DEBUG_LOG("read " SIZEFMTD " bytes in RASocket::handle_input", readBytes);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Discard data after line break or line feed
|
||||||
|
bool gotenter = false;
|
||||||
|
for (; readBytes > 0 ; --readBytes)
|
||||||
|
{
|
||||||
|
char c = inputBuffer[inputBufferLen];
|
||||||
|
if (c == '\r' || c == '\n')
|
||||||
|
{
|
||||||
|
gotenter = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++inputBufferLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gotenter)
|
||||||
|
{
|
||||||
|
inputBuffer[inputBufferLen] = 0;
|
||||||
|
inputBufferLen = 0;
|
||||||
|
switch (stage)
|
||||||
|
{
|
||||||
|
case NONE:
|
||||||
|
{
|
||||||
|
std::string szLogin = inputBuffer;
|
||||||
|
accId = sAccountMgr.GetId(szLogin);
|
||||||
|
|
||||||
|
///- If the user is not found, deny access
|
||||||
|
if (!accId)
|
||||||
|
{
|
||||||
|
sendf("-No such user.\r\n");
|
||||||
|
sLog.outRALog("User %s does not exist.", szLogin.c_str());
|
||||||
|
if (bSecure)
|
||||||
|
{
|
||||||
|
handle_output();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sendf("\r\n");
|
||||||
|
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_USER));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
accAccessLevel = sAccountMgr.GetSecurity(accId);
|
||||||
|
|
||||||
|
///- if gmlevel is too low, deny access
|
||||||
|
if (accAccessLevel < iMinLevel)
|
||||||
|
{
|
||||||
|
sendf("-Not enough privileges.\r\n");
|
||||||
|
sLog.outRALog("User %s has no privilege.", szLogin.c_str());
|
||||||
|
if (bSecure)
|
||||||
|
{
|
||||||
|
handle_output();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sendf("\r\n");
|
||||||
|
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_USER));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- allow by remotely connected admin use console level commands dependent from config setting
|
||||||
|
if (accAccessLevel >= SEC_ADMINISTRATOR && !bStricted)
|
||||||
|
{ accAccessLevel = SEC_CONSOLE; }
|
||||||
|
|
||||||
|
stage = LG;
|
||||||
|
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_PASS));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LG:
|
||||||
|
{
|
||||||
|
// login+pass ok
|
||||||
|
std::string pw = inputBuffer;
|
||||||
|
|
||||||
|
if (sAccountMgr.CheckPassword(accId, pw))
|
||||||
|
{
|
||||||
|
stage = OK;
|
||||||
|
|
||||||
|
sendf("+Logged in.\r\n");
|
||||||
|
sLog.outRALog("User account %u has logged in.", accId);
|
||||||
|
sendf("mangos>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
///- Else deny access
|
||||||
|
sendf("-Wrong pass.\r\n");
|
||||||
|
sLog.outRALog("User account %u has failed to log in.", accId);
|
||||||
|
if (bSecure)
|
||||||
|
{
|
||||||
|
handle_output();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sendf("\r\n");
|
||||||
|
sendf(sObjectMgr.GetMangosStringForDBCLocale(LANG_RA_PASS));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OK:
|
||||||
|
if (strlen(inputBuffer))
|
||||||
|
{
|
||||||
|
sLog.outRALog("Got '%s' cmd.", inputBuffer);
|
||||||
|
if (strncmp(inputBuffer, "quit", 4) == 0)
|
||||||
|
{ return -1; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CliCommandHolder* cmd = new CliCommandHolder(accId, accAccessLevel, this, inputBuffer,
|
||||||
|
&RASocket::zprint, &RASocket::commandFinished);
|
||||||
|
sWorld.QueueCliCommand(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ sendf("mangos>"); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no enter yet? wait for next input...
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int handle_output(ACE_HANDLE h = ACE_INVALID_HANDLE) override
|
||||||
|
{
|
||||||
|
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, outBufferLock, -1);
|
||||||
|
|
||||||
|
if (closing_)
|
||||||
|
{ return -1; }
|
||||||
|
|
||||||
|
if (!outputBufferLen)
|
||||||
|
{
|
||||||
|
if (reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1)
|
||||||
|
{ return -1; }
|
||||||
|
outActive = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#ifdef MSG_NOSIGNAL
|
||||||
|
ssize_t n = peer().send(outputBuffer, outputBufferLen, MSG_NOSIGNAL);
|
||||||
|
#else
|
||||||
|
ssize_t n = peer().send(outputBuffer, outputBufferLen);
|
||||||
|
#endif // MSG_NOSIGNAL
|
||||||
|
|
||||||
|
if (n <= 0)
|
||||||
|
{ return -1; }
|
||||||
|
|
||||||
|
ACE_OS::memmove(outputBuffer, outputBuffer + n, outputBufferLen - n);
|
||||||
|
|
||||||
|
outputBufferLen -= n;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int handle_close(ACE_HANDLE h = ACE_INVALID_HANDLE,
|
||||||
|
ACE_Reactor_Mask mask = ACE_Event_Handler::ALL_EVENTS_MASK) override
|
||||||
|
{
|
||||||
|
if (closing_)
|
||||||
|
{ return -1; }
|
||||||
|
|
||||||
|
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, outBufferLock, -1);
|
||||||
|
|
||||||
|
closing_ = true;
|
||||||
|
|
||||||
|
if (h == ACE_INVALID_HANDLE)
|
||||||
|
{ peer().close_writer(); }
|
||||||
|
remove_reference();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool outActive;
|
||||||
|
|
||||||
|
char inputBuffer[RA_BUFF_SIZE];
|
||||||
|
uint32 inputBufferLen;
|
||||||
|
|
||||||
|
char outputBuffer[RA_BUFF_SIZE];
|
||||||
|
uint32 outputBufferLen;
|
||||||
|
|
||||||
|
uint32 accId;
|
||||||
|
AccountTypes accAccessLevel;
|
||||||
|
bool bSecure; /**< kick on wrong pass, non exist. user OR user with no priv. will protect from DOS, bruteforce attacks */
|
||||||
|
bool bStricted; /**< not allow execute console only commands (SEC_CONSOLE) remotly */
|
||||||
|
AccountTypes iMinLevel;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
NONE, // initial value
|
||||||
|
LG, // only login was entered
|
||||||
|
OK // both login and pass were given, they were correct and user has enough priv.
|
||||||
|
} stage;
|
||||||
|
|
||||||
|
ACE_Thread_Mutex outBufferLock;
|
||||||
|
|
||||||
|
static void zprint(void* callbackArg, const char* szText)
|
||||||
|
{
|
||||||
|
if (!szText)
|
||||||
|
{ return; }
|
||||||
|
|
||||||
|
((RASocket*)callbackArg)->sendf(szText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void commandFinished(void* callbackArg, bool success)
|
||||||
|
{
|
||||||
|
RASocket* raSocket = (RASocket*)callbackArg;
|
||||||
|
raSocket->sendf("mangos>");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
RAThread::RAThread(uint16 port, const char* host) : listen_addr(port, host)
|
||||||
|
{
|
||||||
|
ACE_Reactor_Impl* imp = 0;
|
||||||
|
|
||||||
|
imp = new ACE_TP_Reactor();
|
||||||
|
imp->max_notify_iterations(128);
|
||||||
|
|
||||||
|
m_Reactor = new ACE_Reactor(imp, 1);
|
||||||
|
m_Acceptor = new RAAcceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
RAThread::~RAThread()
|
||||||
|
{
|
||||||
|
delete m_Reactor;
|
||||||
|
delete m_Acceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RAThread::open(void* unused)
|
||||||
|
{
|
||||||
|
if (m_Acceptor->open(listen_addr, m_Reactor, ACE_NONBLOCK) == -1)
|
||||||
|
{
|
||||||
|
sLog.outError("MaNGOS RA can not bind to port %d on %s\n", listen_addr.get_port_number(), listen_addr.get_host_addr());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
activate();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RAThread::svc()
|
||||||
|
{
|
||||||
|
sLog.outString("Remote Access Thread started (listening on %s:%d)",
|
||||||
|
listen_addr.get_host_addr(),
|
||||||
|
listen_addr.get_port_number());
|
||||||
|
|
||||||
|
while (!m_Reactor->reactor_event_loop_done())
|
||||||
|
{
|
||||||
|
ACE_Time_Value interval(0, 10000);
|
||||||
|
|
||||||
|
if (m_Reactor->run_reactor_event_loop(interval) == -1)
|
||||||
|
{ break; }
|
||||||
|
|
||||||
|
if (World::IsStopped())
|
||||||
|
{
|
||||||
|
m_Acceptor->close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sLog.outString("Remote Access Thread stopped");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -26,69 +26,36 @@
|
||||||
/// @{
|
/// @{
|
||||||
/// \file
|
/// \file
|
||||||
|
|
||||||
#ifndef MANGOS_H_MASTER
|
#ifndef MANGOS_H_RATHREAD
|
||||||
#define MANGOS_H_MASTER
|
#define MANGOS_H_RATHREAD
|
||||||
|
|
||||||
|
#include <ace/SOCK_Acceptor.h>
|
||||||
|
#include <ace/Acceptor.h>
|
||||||
|
#include <ace/Task.h>
|
||||||
|
#include <ace/INET_Addr.h>
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Policies/Singleton.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Start the server
|
class RASocket;
|
||||||
*
|
class ACE_Reactor;
|
||||||
*/
|
|
||||||
class Master
|
typedef ACE_Acceptor < RASocket, ACE_SOCK_ACCEPTOR > RAAcceptor;
|
||||||
|
|
||||||
|
class RAThread : public ACE_Task_Base
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Master();
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
~Master();
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
int Run();
|
|
||||||
static volatile uint32 m_masterLoopCounter; /**< TODO */
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
ACE_Reactor *m_Reactor;
|
||||||
* @brief
|
RAAcceptor *m_Acceptor;
|
||||||
*
|
ACE_INET_Addr listen_addr;
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
bool _StartDB();
|
|
||||||
|
|
||||||
/**
|
public:
|
||||||
* @brief
|
explicit RAThread(uint16 port, const char* host);
|
||||||
*
|
virtual ~RAThread();
|
||||||
*/
|
|
||||||
void _HookSignals();
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void _UnhookSignals();
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
* @param s
|
|
||||||
*/
|
|
||||||
static void _OnSignal(int s);
|
|
||||||
|
|
||||||
/**
|
virtual int open(void* unused) override;
|
||||||
* @brief
|
virtual int svc() override;
|
||||||
*
|
|
||||||
*/
|
|
||||||
void clearOnlineAccounts();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define sMaster MaNGOS::Singleton<Master>::Instance()
|
|
||||||
#endif
|
#endif
|
||||||
/// @}
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
/**
|
|
||||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
|
||||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*
|
|
||||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
|
||||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MANGOS_H_MANGOSSOAP
|
|
||||||
#define MANGOS_H_MANGOSSOAP
|
|
||||||
|
|
||||||
#include "Common.h"
|
|
||||||
#include "World.h"
|
|
||||||
#include "AccountMgr.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include "soapH.h"
|
|
||||||
#include "soapStub.h"
|
|
||||||
|
|
||||||
#include <ace/Semaphore.h>
|
|
||||||
#include <ace/Task.h>
|
|
||||||
|
|
||||||
class MaNGOSsoapRunnable: public ACE_Based::Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MaNGOSsoapRunnable() { }
|
|
||||||
void run() override;
|
|
||||||
void setListenArguments(std::string host, uint16 port)
|
|
||||||
{
|
|
||||||
m_host = host;
|
|
||||||
m_port = port;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::string m_host;
|
|
||||||
uint16 m_port;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SOAPWorkingThread : public ACE_Task<ACE_MT_SYNCH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SOAPWorkingThread()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
virtual int svc(void) override
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
ACE_Message_Block* mb = 0;
|
|
||||||
if (this->getq(mb) == -1)
|
|
||||||
{
|
|
||||||
ACE_DEBUG((LM_INFO,
|
|
||||||
ACE_TEXT("(%t) Shutting down\n")));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the message.
|
|
||||||
process_message(mb);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void process_message(ACE_Message_Block* mb);
|
|
||||||
};
|
|
||||||
|
|
||||||
class SOAPCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SOAPCommand():
|
|
||||||
pendingCommands(0, USYNC_THREAD, "pendingCommands")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~SOAPCommand()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendToPrintBuffer(const char* msg)
|
|
||||||
{
|
|
||||||
m_printBuffer += msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACE_Semaphore pendingCommands;
|
|
||||||
|
|
||||||
void setCommandSuccess(bool val)
|
|
||||||
{
|
|
||||||
m_success = val;
|
|
||||||
}
|
|
||||||
bool hasCommandSucceeded()
|
|
||||||
{
|
|
||||||
return m_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print(void* callbackArg, const char* msg)
|
|
||||||
{
|
|
||||||
((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void commandFinished(void* callbackArg, bool success);
|
|
||||||
|
|
||||||
bool m_success;
|
|
||||||
std::string m_printBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -22,39 +22,123 @@
|
||||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MaNGOSsoap.h"
|
#include <ace/OS.h>
|
||||||
|
#include <ace/Message_Block.h>
|
||||||
|
|
||||||
#define POOL_SIZE 5
|
#include "SoapThread.h"
|
||||||
|
#include "soapH.h"
|
||||||
|
#include "soapStub.h"
|
||||||
|
|
||||||
void MaNGOSsoapRunnable::run()
|
#include "World.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "AccountMgr.h"
|
||||||
|
|
||||||
|
|
||||||
|
/// WARNING! This code needs serious reviewing
|
||||||
|
|
||||||
|
|
||||||
|
struct SOAPCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void appendToPrintBuffer(const char* msg)
|
||||||
|
{ m_printBuffer += msg; }
|
||||||
|
|
||||||
|
void setCommandSuccess(bool val)
|
||||||
|
{ m_success = val; }
|
||||||
|
|
||||||
|
bool hasCommandSucceeded()
|
||||||
|
{ return m_success; }
|
||||||
|
|
||||||
|
static void print(void* callbackArg, const char* msg)
|
||||||
|
{
|
||||||
|
((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void commandFinished(void* callbackArg, bool success);
|
||||||
|
|
||||||
|
bool m_success;
|
||||||
|
std::string m_printBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SoapPool : public ACE_Task<ACE_MT_SYNCH>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int svc(void) override
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ACE_Message_Block* mb = 0;
|
||||||
|
if (this->getq(mb) == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the message.
|
||||||
|
process_message(mb);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void process_message(ACE_Message_Block* mb)
|
||||||
|
{
|
||||||
|
struct soap* soap;
|
||||||
|
ACE_OS::memcpy(&soap, mb->rd_ptr(), sizeof(struct soap*));
|
||||||
|
mb->release();
|
||||||
|
|
||||||
|
soap_serve(soap);
|
||||||
|
|
||||||
|
soap_destroy(soap); // dealloc C++ data
|
||||||
|
soap_end(soap); // dealloc data and clean up
|
||||||
|
soap_done(soap); // detach soap struct
|
||||||
|
free(soap);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SoapThread::~SoapThread()
|
||||||
|
{
|
||||||
|
if(pool_)
|
||||||
|
delete pool_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SoapThread::open(void* unused)
|
||||||
{
|
{
|
||||||
// create pool
|
// create pool
|
||||||
SOAPWorkingThread pool;
|
pool_ = new SoapPool;
|
||||||
pool.activate(THR_NEW_LWP | THR_JOINABLE, POOL_SIZE);
|
pool_->activate(THR_NEW_LWP | THR_JOINABLE, SOAP_THREADS);
|
||||||
|
|
||||||
struct soap soap;
|
int m;
|
||||||
int m, s;
|
soap_init(&soap_);
|
||||||
soap_init(&soap);
|
soap_set_imode(&soap_, SOAP_C_UTFSTRING);
|
||||||
soap_set_imode(&soap, SOAP_C_UTFSTRING);
|
soap_set_omode(&soap_, SOAP_C_UTFSTRING);
|
||||||
soap_set_omode(&soap, SOAP_C_UTFSTRING);
|
m = soap_bind(&soap_, host_, port_, 100);
|
||||||
m = soap_bind(&soap, m_host.c_str(), m_port, 100);
|
|
||||||
|
|
||||||
// check every 3 seconds if world ended
|
|
||||||
soap.accept_timeout = 3;
|
|
||||||
|
|
||||||
soap.recv_timeout = 5;
|
|
||||||
soap.send_timeout = 5;
|
|
||||||
if (m < 0)
|
if (m < 0)
|
||||||
{
|
{
|
||||||
sLog.outError("MaNGOSsoap: couldn't bind to %s:%d", m_host.c_str(), m_port);
|
sLog.outError("SoapThread: couldn't bind to %s:%d", host_, port_);
|
||||||
exit(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sLog.outString("MaNGOSsoap: bound to http://%s:%d", m_host.c_str(), m_port);
|
// check every 3 seconds if world ended
|
||||||
|
soap_.accept_timeout = 3;
|
||||||
|
soap_.recv_timeout = 5;
|
||||||
|
soap_.send_timeout = 5;
|
||||||
|
|
||||||
|
activate();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int SoapThread::svc()
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
sLog.outString("SOAP Thread started (listening on %s:%d)", host_, port_);
|
||||||
while (!World::IsStopped())
|
while (!World::IsStopped())
|
||||||
{
|
{
|
||||||
s = soap_accept(&soap);
|
s = soap_accept(&soap_);
|
||||||
|
|
||||||
if (s < 0)
|
if (s < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -62,38 +146,28 @@ void MaNGOSsoapRunnable::run()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG("MaNGOSsoap: accepted connection from IP=%d.%d.%d.%d", (int)(soap.ip >> 24) & 0xFF, (int)(soap.ip >> 16) & 0xFF, (int)(soap.ip >> 8) & 0xFF, (int)soap.ip & 0xFF);
|
struct soap* thread_soap = soap_copy(&soap_);// make a safe copy
|
||||||
struct soap* thread_soap = soap_copy(&soap);// make a safe copy
|
|
||||||
|
|
||||||
ACE_Message_Block* mb = new ACE_Message_Block(sizeof(struct soap*));
|
ACE_Message_Block* mb = new ACE_Message_Block(sizeof(struct soap*));
|
||||||
ACE_OS::memcpy(mb->wr_ptr(), &thread_soap, sizeof(struct soap*));
|
ACE_OS::memcpy(mb->wr_ptr(), &thread_soap, sizeof(struct soap*));
|
||||||
pool.putq(mb);
|
pool_->putq(mb);
|
||||||
}
|
}
|
||||||
pool.msg_queue()->deactivate();
|
|
||||||
pool.wait();
|
|
||||||
|
|
||||||
soap_done(&soap);
|
pool_->msg_queue()->deactivate();
|
||||||
|
pool_->wait();
|
||||||
|
|
||||||
|
soap_done(&soap_);
|
||||||
|
sLog.outString("SOAP Thread stopped");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SOAPWorkingThread::process_message(ACE_Message_Block* mb)
|
|
||||||
{
|
|
||||||
ACE_TRACE(ACE_TEXT("SOAPWorkingThread::process_message"));
|
|
||||||
|
|
||||||
struct soap* soap;
|
|
||||||
ACE_OS::memcpy(&soap, mb->rd_ptr(), sizeof(struct soap*));
|
|
||||||
mb->release();
|
|
||||||
|
|
||||||
soap_serve(soap);
|
|
||||||
soap_destroy(soap); // dealloc C++ data
|
|
||||||
soap_end(soap); // dealloc data and clean up
|
|
||||||
soap_done(soap); // detach soap struct
|
|
||||||
free(soap);
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
Code used for generating stubs:
|
Code used for generating stubs:
|
||||||
|
|
||||||
int ns1__executeCommand(char* command, char** result);
|
int ns1__executeCommand(char* command, char** result);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int ns1__executeCommand(soap* soap, char* command, char** result)
|
int ns1__executeCommand(soap* soap, char* command, char** result)
|
||||||
{
|
{
|
||||||
// security check
|
// security check
|
||||||
|
|
@ -135,15 +209,7 @@ int ns1__executeCommand(soap* soap, char* command, char** result)
|
||||||
sWorld.QueueCliCommand(cmd);
|
sWorld.QueueCliCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for callback to complete command
|
ACE_OS::sleep(1);
|
||||||
|
|
||||||
int acc = connection.pendingCommands.acquire();
|
|
||||||
if (acc)
|
|
||||||
{
|
|
||||||
sLog.outError("MaNGOSsoap: Error while acquiring lock, acc = %i, errno = %u", acc, errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
// alright, command finished
|
|
||||||
|
|
||||||
char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str());
|
char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str());
|
||||||
if (connection.hasCommandSucceeded())
|
if (connection.hasCommandSucceeded())
|
||||||
|
|
@ -159,7 +225,6 @@ void SOAPCommand::commandFinished(void* soapconnection, bool success)
|
||||||
{
|
{
|
||||||
SOAPCommand* con = (SOAPCommand*)soapconnection;
|
SOAPCommand* con = (SOAPCommand*)soapconnection;
|
||||||
con->setCommandSuccess(success);
|
con->setCommandSuccess(success);
|
||||||
con->pendingCommands.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
53
src/mangosd/SOAP/SoapThread.h
Normal file
53
src/mangosd/SOAP/SoapThread.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||||
|
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MANGOS_H_SOAPTHREAD
|
||||||
|
#define MANGOS_H_SOAPTHREAD
|
||||||
|
|
||||||
|
#include <ace/Task.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "soapH.h"
|
||||||
|
|
||||||
|
class SoapPool;
|
||||||
|
|
||||||
|
class SoapThread: public ACE_Task_Base
|
||||||
|
{
|
||||||
|
enum { SOAP_THREADS = 1 }; //TODO: configure in mangosd.conf
|
||||||
|
|
||||||
|
public:
|
||||||
|
SoapThread(uint16 port, const char* host) : host_(host), port_(port), pool_(NULL) {}
|
||||||
|
virtual ~SoapThread();
|
||||||
|
|
||||||
|
virtual int svc() override;
|
||||||
|
virtual int open(void*) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char *host_;
|
||||||
|
uint16 port_;
|
||||||
|
SoapPool *pool_;
|
||||||
|
struct soap soap_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -26,14 +26,14 @@
|
||||||
\ingroup mangosd
|
\ingroup mangosd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "WorldSocketMgr.h"
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include "WorldSocket.h"
|
||||||
|
#include "WorldSocketMgr.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
#include "WorldRunnable.h"
|
#include "WorldThread.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "ObjectAccessor.h"
|
#include "ObjectAccessor.h"
|
||||||
#include "MapManager.h"
|
#include "MapManager.h"
|
||||||
|
|
||||||
#include "Database/DatabaseEnv.h"
|
#include "Database/DatabaseEnv.h"
|
||||||
|
|
||||||
#ifdef ENABLE_ELUNA
|
#ifdef ENABLE_ELUNA
|
||||||
|
|
@ -43,32 +43,45 @@
|
||||||
#define WORLD_SLEEP_CONST 50
|
#define WORLD_SLEEP_CONST 50
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include "ServiceWin32.h"
|
#include "ServiceWin32.h"
|
||||||
extern int m_ServiceStatus;
|
extern int m_ServiceStatus;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Heartbeat for the World
|
WorldThread::WorldThread(uint16 port, const char* host) : listen_addr(port, host)
|
||||||
void WorldRunnable::run()
|
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int WorldThread::open(void* unused)
|
||||||
|
{
|
||||||
|
if (sWorldSocketMgr->StartNetwork(listen_addr) == -1)
|
||||||
|
{
|
||||||
|
sLog.outError("Failed to start network");
|
||||||
|
Log::WaitBeforeContinueIfNeed();
|
||||||
|
World::StopNow(ERROR_EXIT_CODE);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
#ifdef ENABLE_ELUNA
|
#ifdef ENABLE_ELUNA
|
||||||
sEluna->OnStartup();
|
sEluna->OnStartup();
|
||||||
#endif /* ENABLE_ELUNA */
|
#endif /* ENABLE_ELUNA */
|
||||||
|
|
||||||
uint32 realCurrTime = 0;
|
activate();
|
||||||
uint32 realPrevTime = WorldTimer::tick();
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST
|
/// Heartbeat for the World
|
||||||
|
int WorldThread::svc()
|
||||||
|
{
|
||||||
|
uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST
|
||||||
|
sLog.outString("World Updater Thread started (%dms min update interval)", WORLD_SLEEP_CONST);
|
||||||
|
|
||||||
///- While we have not World::m_stopEvent, update the world
|
///- While we have not World::m_stopEvent, update the world
|
||||||
while (!World::IsStopped())
|
while (!World::IsStopped())
|
||||||
{
|
{
|
||||||
++World::m_worldLoopCounter;
|
++World::m_worldLoopCounter;
|
||||||
realCurrTime = WorldTimer::getMSTime();
|
|
||||||
|
|
||||||
uint32 diff = WorldTimer::tick();
|
uint32 diff = WorldTimer::tick();
|
||||||
|
|
||||||
sWorld.Update(diff);
|
sWorld.Update(diff);
|
||||||
realPrevTime = realCurrTime;
|
|
||||||
|
|
||||||
// diff (D0) include time of previous sleep (d0) + tick time (t0)
|
// diff (D0) include time of previous sleep (d0) + tick time (t0)
|
||||||
// we want that next d1 + t1 == WORLD_SLEEP_CONST
|
// we want that next d1 + t1 == WORLD_SLEEP_CONST
|
||||||
|
|
@ -83,25 +96,21 @@ void WorldRunnable::run()
|
||||||
{
|
{
|
||||||
prevSleepTime = 0;
|
prevSleepTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (m_ServiceStatus == 0)
|
if (m_ServiceStatus == 0) //service stopped
|
||||||
{
|
{
|
||||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (m_ServiceStatus == 2)
|
while (m_ServiceStatus == 2) //service paused
|
||||||
Sleep(1000);
|
Sleep(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_ELUNA
|
#ifdef ENABLE_ELUNA
|
||||||
sEluna->OnShutdown();
|
sEluna->OnShutdown();
|
||||||
#endif /* ENABLE_ELUNA */
|
#endif /* ENABLE_ELUNA */
|
||||||
|
|
||||||
sWorld.KickAll(); // save and kick all players
|
sWorld.KickAll(); // save and kick all players
|
||||||
sWorld.UpdateSessions(1); // real players unload required UpdateSessions call
|
sWorld.UpdateSessions(1); // real players unload required UpdateSessions call
|
||||||
|
|
||||||
sWorldSocketMgr->StopNetwork();
|
sWorldSocketMgr->StopNetwork();
|
||||||
|
|
||||||
sMapMgr.UnloadAll(); // unload all grids (including locked in memory)
|
sMapMgr.UnloadAll(); // unload all grids (including locked in memory)
|
||||||
|
|
@ -111,4 +120,7 @@ void WorldRunnable::run()
|
||||||
// and must be unloaded before the DB, since it can access the DB.
|
// and must be unloaded before the DB, since it can access the DB.
|
||||||
Eluna::Uninitialize();
|
Eluna::Uninitialize();
|
||||||
#endif /* ENABLE_ELUNA */
|
#endif /* ENABLE_ELUNA */
|
||||||
|
|
||||||
|
sLog.outString("World Updater Thread stopped");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -26,24 +26,25 @@
|
||||||
/// @{
|
/// @{
|
||||||
/// \file
|
/// \file
|
||||||
|
|
||||||
#ifndef MANGOS_H_WORLDRUNNABLE
|
#ifndef MANGOS_H_WORLDTHREAD
|
||||||
#define MANGOS_H_WORLDRUNNABLE
|
#define MANGOS_H_WORLDTHREAD
|
||||||
|
|
||||||
|
#include <ace/INET_Addr.h>
|
||||||
|
#include <ace/Task.h>
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Threading.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Heartbeat thread for the World
|
* @brief Heartbeat thread for the World
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class WorldRunnable : public ACE_Based::Runnable
|
class WorldThread : public ACE_Task_Base
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
explicit WorldThread(uint16 port, const char* host);
|
||||||
* @brief
|
virtual int open(void*) override;
|
||||||
*
|
virtual int svc();
|
||||||
*/
|
private:
|
||||||
void run() override;
|
ACE_INET_Addr listen_addr;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
/// @}
|
/// @}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
[MangosdConf]
|
[MangosdConf]
|
||||||
ConfVersion=2016031901
|
ConfVersion=2017021100
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# CONNECTIONS AND DIRECTORIES
|
# CONNECTIONS AND DIRECTORIES
|
||||||
|
|
@ -417,7 +417,7 @@ RaLogFile = "world-remote-access.log"
|
||||||
WardenLogFile = "warden.log"
|
WardenLogFile = "warden.log"
|
||||||
WardenLogTimestamp = 0
|
WardenLogTimestamp = 0
|
||||||
LogColors = "13 7 11 9"
|
LogColors = "13 7 11 9"
|
||||||
SD2ErrorLogFile = "scriptdev2-errors.log"
|
SD3ErrorLogFile = "scriptdev3-errors.log"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# SERVER SETTINGS
|
# SERVER SETTINGS
|
||||||
|
|
@ -1610,8 +1610,9 @@ OutdoorPvp.GHEnabled = 1
|
||||||
# NETWORK CONFIG
|
# NETWORK CONFIG
|
||||||
#
|
#
|
||||||
# Network.Threads
|
# Network.Threads
|
||||||
# Number of threads for network, recommend 1 thread per 1000 connections.
|
# Number of threads for network queue handling, we recommend a minimum of 3,
|
||||||
# Default: 1
|
# additional threads will assist with greater numbers of players.
|
||||||
|
# Default: 3
|
||||||
#
|
#
|
||||||
# Network.OutKBuff
|
# Network.OutKBuff
|
||||||
# The size of the output kernel buffer used ( SO_SNDBUF socket option, tcp manual ).
|
# The size of the output kernel buffer used ( SO_SNDBUF socket option, tcp manual ).
|
||||||
|
|
@ -1633,7 +1634,7 @@ OutdoorPvp.GHEnabled = 1
|
||||||
#
|
#
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
Network.Threads = 1
|
Network.Threads = 3
|
||||||
Network.OutKBuff = -1
|
Network.OutKBuff = -1
|
||||||
Network.OutUBuff = 65536
|
Network.OutUBuff = 65536
|
||||||
Network.TcpNodelay = 1
|
Network.TcpNodelay = 1
|
||||||
|
|
|
||||||
585
src/mangosd/mangosd.cpp
Normal file
585
src/mangosd/mangosd.cpp
Normal file
|
|
@ -0,0 +1,585 @@
|
||||||
|
/**
|
||||||
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||||
|
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// \addtogroup mangosd Mangos Daemon
|
||||||
|
/// @{
|
||||||
|
/// \file
|
||||||
|
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
#include <openssl/crypto.h>
|
||||||
|
#include <ace/Version.h>
|
||||||
|
#include <ace/Get_Opt.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Database/DatabaseEnv.h"
|
||||||
|
#include "Config/Config.h"
|
||||||
|
#include "ProgressBar.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "SystemConfig.h"
|
||||||
|
#include "AuctionHouseBot.h"
|
||||||
|
#include "revision.h"
|
||||||
|
#include "World.h"
|
||||||
|
#include "Util.h"
|
||||||
|
#include "DBCStores.h"
|
||||||
|
#include "MassMailMgr.h"
|
||||||
|
#include "ScriptMgr.h"
|
||||||
|
|
||||||
|
#include "WorldThread.h"
|
||||||
|
#include "CliThread.h"
|
||||||
|
#include "AFThread.h"
|
||||||
|
#include "RAThread.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_SOAP
|
||||||
|
#include "SOAP/SoapThread.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "ServiceWin32.h"
|
||||||
|
|
||||||
|
char serviceName[] = "MaNGOS"; // service short name
|
||||||
|
char serviceLongName[] = "MaNGOS World Service"; // service long name
|
||||||
|
char serviceDescription[] = "MaNGOS World Service - no description available";
|
||||||
|
|
||||||
|
int m_ServiceStatus = -1;
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include "PosixDaemon.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//*******************************************************************************************************//
|
||||||
|
DatabaseType WorldDatabase; ///< Accessor to the world database
|
||||||
|
DatabaseType CharacterDatabase; ///< Accessor to the character database
|
||||||
|
DatabaseType LoginDatabase; ///< Accessor to the realm/login database
|
||||||
|
|
||||||
|
uint32 realmID = 0; ///< Id of the realm
|
||||||
|
//*******************************************************************************************************//
|
||||||
|
|
||||||
|
|
||||||
|
/// Clear 'online' status for all accounts with characters in this realm
|
||||||
|
static void clear_online_accounts()
|
||||||
|
{
|
||||||
|
// Cleanup online status for characters hosted at current realm
|
||||||
|
/// \todo Only accounts with characters logged on *this* realm should have online status reset. Move the online column from 'account' to 'realmcharacters'?
|
||||||
|
LoginDatabase.PExecute("UPDATE account SET active_realm_id = 0, os = '' WHERE active_realm_id = '%u'", realmID);
|
||||||
|
|
||||||
|
CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0");
|
||||||
|
|
||||||
|
// Battleground instance ids reset at server restart
|
||||||
|
CharacterDatabase.Execute("UPDATE character_battleground_data SET instance_id = 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Initialize connection to the databases
|
||||||
|
static bool start_db()
|
||||||
|
{
|
||||||
|
///- Get world database info from configuration file
|
||||||
|
std::string dbstring = sConfig.GetStringDefault("WorldDatabaseInfo", "");
|
||||||
|
int nConnections = sConfig.GetIntDefault("WorldDatabaseConnections", 1);
|
||||||
|
if (dbstring.empty())
|
||||||
|
{
|
||||||
|
sLog.outError("Database not specified in configuration file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sLog.outString("World Database total connections: %i", nConnections + 1);
|
||||||
|
|
||||||
|
///- Initialise the world database
|
||||||
|
if (!WorldDatabase.Initialize(dbstring.c_str(), nConnections))
|
||||||
|
{
|
||||||
|
sLog.outError("Can not connect to world database %s", dbstring.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Check the World database version
|
||||||
|
if(!WorldDatabase.CheckDatabaseVersion(DATABASE_WORLD))
|
||||||
|
{
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo", "");
|
||||||
|
nConnections = sConfig.GetIntDefault("CharacterDatabaseConnections", 1);
|
||||||
|
if (dbstring.empty())
|
||||||
|
{
|
||||||
|
sLog.outError("Character Database not specified in configuration file");
|
||||||
|
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sLog.outString("Character Database total connections: %i", nConnections + 1);
|
||||||
|
|
||||||
|
///- Initialise the Character database
|
||||||
|
if (!CharacterDatabase.Initialize(dbstring.c_str(), nConnections))
|
||||||
|
{
|
||||||
|
sLog.outError("Can not connect to Character database %s", dbstring.c_str());
|
||||||
|
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Check the Character database version
|
||||||
|
if (!CharacterDatabase.CheckDatabaseVersion(DATABASE_CHARACTER))
|
||||||
|
{
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
CharacterDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Get login database info from configuration file
|
||||||
|
dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
|
||||||
|
nConnections = sConfig.GetIntDefault("LoginDatabaseConnections", 1);
|
||||||
|
if (dbstring.empty())
|
||||||
|
{
|
||||||
|
sLog.outError("Login database not specified in configuration file");
|
||||||
|
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
CharacterDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Initialise the login database
|
||||||
|
sLog.outString("Login Database total connections: %i", nConnections + 1);
|
||||||
|
if (!LoginDatabase.Initialize(dbstring.c_str(), nConnections))
|
||||||
|
{
|
||||||
|
sLog.outError("Can not connect to login database %s", dbstring.c_str());
|
||||||
|
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
CharacterDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Check the Realm database version
|
||||||
|
if (!LoginDatabase.CheckDatabaseVersion(DATABASE_REALMD))
|
||||||
|
{
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
CharacterDatabase.HaltDelayThread();
|
||||||
|
LoginDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sLog.outString();
|
||||||
|
|
||||||
|
///- Get the realm Id from the configuration file
|
||||||
|
realmID = sConfig.GetIntDefault("RealmID", 0);
|
||||||
|
if (!realmID)
|
||||||
|
{
|
||||||
|
sLog.outError("Realm ID not defined in configuration file");
|
||||||
|
|
||||||
|
///- Wait for already started DB delay threads to end
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
CharacterDatabase.HaltDelayThread();
|
||||||
|
LoginDatabase.HaltDelayThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sLog.outString("Realm running as realm ID %d", realmID);
|
||||||
|
sLog.outString();
|
||||||
|
|
||||||
|
///- Clean the database before starting
|
||||||
|
clear_online_accounts();
|
||||||
|
|
||||||
|
sWorld.LoadDBVersion();
|
||||||
|
|
||||||
|
sLog.outString("Using World DB: %s", sWorld.GetDBVersion());
|
||||||
|
sLog.outString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle termination signals
|
||||||
|
static void on_signal(int s)
|
||||||
|
{
|
||||||
|
switch (s)
|
||||||
|
{
|
||||||
|
case SIGINT:
|
||||||
|
World::StopNow(RESTART_EXIT_CODE);
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
#ifdef _WIN32
|
||||||
|
case SIGBREAK:
|
||||||
|
#endif
|
||||||
|
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(s, on_signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define hook for all termination signals
|
||||||
|
static void hook_signals()
|
||||||
|
{
|
||||||
|
signal(SIGINT, on_signal);
|
||||||
|
signal(SIGTERM, on_signal);
|
||||||
|
#ifdef _WIN32
|
||||||
|
signal(SIGBREAK, on_signal);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unhook the signals before leaving
|
||||||
|
static void unhook_signals()
|
||||||
|
{
|
||||||
|
signal(SIGINT, 0);
|
||||||
|
signal(SIGTERM, 0);
|
||||||
|
#ifdef _WIN32
|
||||||
|
signal(SIGBREAK, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Print out the usage string for this program on the console.
|
||||||
|
static void usage(const char* prog)
|
||||||
|
{
|
||||||
|
sLog.outString("Usage: \n %s [<options>]\n"
|
||||||
|
" -v, --version print version and exist\n\r"
|
||||||
|
" -c <config_file> use config_file as configuration file\n\r"
|
||||||
|
" -a, --ahbot <config_file> use config_file as ahbot configuration file\n\r"
|
||||||
|
#ifdef WIN32
|
||||||
|
" Running as service functions:\n\r"
|
||||||
|
" -s run run as service\n\r"
|
||||||
|
" -s install install service\n\r"
|
||||||
|
" -s uninstall uninstall service\n\r"
|
||||||
|
#else
|
||||||
|
" Running as daemon functions:\n\r"
|
||||||
|
" -s run run as daemon\n\r"
|
||||||
|
" -s stop stop daemon\n\r"
|
||||||
|
#endif
|
||||||
|
, prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print out the core banner
|
||||||
|
static void print_banner()
|
||||||
|
{
|
||||||
|
sLog.outString("<Ctrl-C> to stop.\n"
|
||||||
|
" __ __ _ _ ___ ___ ___ \n"
|
||||||
|
" | \\/ |__ _| \\| |/ __|/ _ \\/ __| \n"
|
||||||
|
" | |\\/| / _` | .` | (_ | (_) \\__ \\ \n"
|
||||||
|
" |_| |_\\__,_|_|\\_|\\___|\\___/|___/ \n"
|
||||||
|
" _____ _ \n"
|
||||||
|
" For help and support please visit: |_ _| |_ _ _ ___ ___ \n"
|
||||||
|
" Website: https://getmangos.eu | | | ' \\| '_/ -_) -_) \n"
|
||||||
|
" Forum / Wiki: https://getmangos.eu |_| |_||_|_| \\___\\___| \n"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Launch the mangos server
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
///- Command line parsing
|
||||||
|
char const* cfg_file = MANGOSD_CONFIG_LOCATION;
|
||||||
|
|
||||||
|
char const* options = ":a:c:s:";
|
||||||
|
|
||||||
|
ACE_Get_Opt cmd_opts(argc, argv, options);
|
||||||
|
cmd_opts.long_option("version", 'v', ACE_Get_Opt::NO_ARG);
|
||||||
|
cmd_opts.long_option("ahbot", 'a', ACE_Get_Opt::ARG_REQUIRED);
|
||||||
|
|
||||||
|
char serviceDaemonMode = '\0';
|
||||||
|
|
||||||
|
int option;
|
||||||
|
while ((option = cmd_opts()) != EOF)
|
||||||
|
{
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
case 'a':
|
||||||
|
sAuctionBotConfig.SetConfigFileName(cmd_opts.opt_arg());
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
cfg_file = cmd_opts.opt_arg();
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
printf("%s\n", REVISION_NR);
|
||||||
|
return 0;
|
||||||
|
case 's':
|
||||||
|
{
|
||||||
|
const char* mode = cmd_opts.opt_arg();
|
||||||
|
|
||||||
|
if (!strcmp(mode, "run"))
|
||||||
|
{ serviceDaemonMode = 'r'; }
|
||||||
|
#ifdef WIN32
|
||||||
|
else if (!strcmp(mode, "install"))
|
||||||
|
{ serviceDaemonMode = 'i'; }
|
||||||
|
else if (!strcmp(mode, "uninstall"))
|
||||||
|
{ serviceDaemonMode = 'u'; }
|
||||||
|
#else
|
||||||
|
else if (!strcmp(mode, "stop"))
|
||||||
|
{ serviceDaemonMode = 's'; }
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sLog.outError("Runtime-Error: -%c unsupported argument %s", cmd_opts.opt_opt(), mode);
|
||||||
|
usage(argv[0]);
|
||||||
|
Log::WaitBeforeContinueIfNeed();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ':':
|
||||||
|
sLog.outError("Runtime-Error: -%c option requires an input argument", cmd_opts.opt_opt());
|
||||||
|
usage(argv[0]);
|
||||||
|
Log::WaitBeforeContinueIfNeed();
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
sLog.outError("Runtime-Error: bad format of commandline arguments");
|
||||||
|
usage(argv[0]);
|
||||||
|
Log::WaitBeforeContinueIfNeed();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32 // windows service command need execute before config read
|
||||||
|
switch (serviceDaemonMode)
|
||||||
|
{
|
||||||
|
case 'i':
|
||||||
|
if (WinServiceInstall())
|
||||||
|
{ sLog.outString("Installing service"); }
|
||||||
|
return 1;
|
||||||
|
case 'u':
|
||||||
|
if (WinServiceUninstall())
|
||||||
|
{ sLog.outString("Uninstalling service"); }
|
||||||
|
return 1;
|
||||||
|
case 'r':
|
||||||
|
WinServiceRun();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!sConfig.SetSource(cfg_file))
|
||||||
|
{
|
||||||
|
sLog.outError("Could not find configuration file %s.", cfg_file);
|
||||||
|
Log::WaitBeforeContinueIfNeed();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32 // posix daemon commands need apply after config read
|
||||||
|
switch (serviceDaemonMode)
|
||||||
|
{
|
||||||
|
case 'r':
|
||||||
|
startDaemon();
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
stopDaemon();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sLog.outString("%s [world-daemon]", REVISION_NR);
|
||||||
|
print_banner();
|
||||||
|
sLog.outString("Using configuration file %s.", cfg_file);
|
||||||
|
|
||||||
|
DETAIL_LOG("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||||
|
if (SSLeay() < 0x009080bfL)
|
||||||
|
{
|
||||||
|
DETAIL_LOG("WARNING: Outdated version of OpenSSL lib. Logins to server may not work!");
|
||||||
|
DETAIL_LOG("WARNING: Minimal required version [OpenSSL 0.9.8k]");
|
||||||
|
}
|
||||||
|
|
||||||
|
DETAIL_LOG("Using ACE: %s", ACE_VERSION);
|
||||||
|
|
||||||
|
///- Set progress bars show mode
|
||||||
|
BarGoLink::SetOutputState(sConfig.GetBoolDefault("ShowProgressBars", true));
|
||||||
|
|
||||||
|
/// worldd PID file creation
|
||||||
|
std::string pidfile = sConfig.GetStringDefault("PidFile", "");
|
||||||
|
if (!pidfile.empty())
|
||||||
|
{
|
||||||
|
uint32 pid = CreatePIDFile(pidfile);
|
||||||
|
if (!pid)
|
||||||
|
{
|
||||||
|
sLog.outError("Can not create PID file %s.\n", pidfile.c_str());
|
||||||
|
Log::WaitBeforeContinueIfNeed();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sLog.outString("Daemon PID: %u\n", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Start the databases
|
||||||
|
if (!start_db())
|
||||||
|
{
|
||||||
|
Log::WaitBeforeContinueIfNeed();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
///- Set Realm to Offline, if crash happens. Only used once.
|
||||||
|
LoginDatabase.DirectPExecute("UPDATE realmlist SET realmflags = realmflags | %u WHERE id = '%u'", REALM_FLAG_OFFLINE, realmID);
|
||||||
|
|
||||||
|
initMTRandTSS();
|
||||||
|
|
||||||
|
///- Initialize the World
|
||||||
|
sWorld.SetInitialWorldSettings();
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
detachDaemon();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// set realmbuilds depend on mangosd expected builds, and set server online
|
||||||
|
std::string builds = AcceptableClientBuildsListStr();
|
||||||
|
LoginDatabase.escape_string(builds);
|
||||||
|
LoginDatabase.DirectPExecute("UPDATE realmlist SET realmflags = realmflags & ~(%u), population = 0, realmbuilds = '%s' WHERE id = '%u'", REALM_FLAG_OFFLINE, builds.c_str(), realmID);
|
||||||
|
|
||||||
|
// server loaded successfully => enable async DB requests
|
||||||
|
// this is done to forbid any async transactions during server startup!
|
||||||
|
|
||||||
|
WorldDatabase.ThreadStart();
|
||||||
|
|
||||||
|
CharacterDatabase.AllowAsyncTransactions();
|
||||||
|
WorldDatabase.AllowAsyncTransactions();
|
||||||
|
LoginDatabase.AllowAsyncTransactions();
|
||||||
|
|
||||||
|
///- Catch termination signals
|
||||||
|
hook_signals();
|
||||||
|
|
||||||
|
|
||||||
|
//************************************************************************************************************************
|
||||||
|
// 1. Start the World thread
|
||||||
|
//************************************************************************************************************************
|
||||||
|
|
||||||
|
std::string host = sConfig.GetStringDefault("BindIP", "0.0.0.0");
|
||||||
|
uint16 port = sWorld.getConfig(CONFIG_UINT32_PORT_WORLD);
|
||||||
|
|
||||||
|
WorldThread* worldThread = new WorldThread(port, host.c_str());
|
||||||
|
worldThread->open(0);
|
||||||
|
|
||||||
|
|
||||||
|
//************************************************************************************************************************
|
||||||
|
// 2. Start the remote access listener thread
|
||||||
|
//************************************************************************************************************************
|
||||||
|
RAThread* raThread = NULL;
|
||||||
|
if (sConfig.GetBoolDefault("Ra.Enable", false))
|
||||||
|
{
|
||||||
|
port = sConfig.GetIntDefault("Ra.Port", 3443);
|
||||||
|
host = sConfig.GetStringDefault("Ra.IP", "0.0.0.0");
|
||||||
|
|
||||||
|
raThread = new RAThread(port, host.c_str());
|
||||||
|
raThread->open(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//************************************************************************************************************************
|
||||||
|
// 3. Start the SOAP listener thread, if enabled
|
||||||
|
//************************************************************************************************************************
|
||||||
|
#ifdef ENABLE_SOAP
|
||||||
|
SoapThread* soapThread = NULL;
|
||||||
|
if (sConfig.GetBoolDefault("SOAP.Enabled", false))
|
||||||
|
{
|
||||||
|
host = sConfig.GetStringDefault("SOAP.IP", "127.0.0.1");
|
||||||
|
port = sConfig.GetIntDefault("SOAP.Port", 7878);
|
||||||
|
soapThread = new SoapThread(port, host.c_str());
|
||||||
|
soapThread->open(0);
|
||||||
|
}
|
||||||
|
#else /* ENABLE_SOAP */
|
||||||
|
if (sConfig.GetBoolDefault("SOAP.Enabled", false))
|
||||||
|
{
|
||||||
|
sLog.outError("SOAP is enabled but wasn't included during compilation, not activating it.");
|
||||||
|
}
|
||||||
|
#endif /* ENABLE_SOAP */
|
||||||
|
|
||||||
|
|
||||||
|
//************************************************************************************************************************
|
||||||
|
// 4. Start the freeze catcher thread
|
||||||
|
//************************************************************************************************************************
|
||||||
|
AntiFreezeThread* freezeThread = new AntiFreezeThread(1000 * sConfig.GetIntDefault("MaxCoreStuckTime", 0));
|
||||||
|
freezeThread->open(NULL);
|
||||||
|
|
||||||
|
|
||||||
|
//************************************************************************************************************************
|
||||||
|
// 5. Start the console thread
|
||||||
|
//************************************************************************************************************************
|
||||||
|
CliThread* cliThread = NULL;
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
||||||
|
#else
|
||||||
|
if (sConfig.GetBoolDefault("Console.Enable", true))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
///- Launch CliRunnable thread
|
||||||
|
cliThread = new CliThread(sConfig.GetBoolDefault("BeepAtStart", true));
|
||||||
|
cliThread->activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
worldThread->wait();
|
||||||
|
|
||||||
|
if (cliThread)
|
||||||
|
{
|
||||||
|
cliThread->cli_shutdown();
|
||||||
|
delete cliThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
ACE_Thread_Manager::instance()->wait();
|
||||||
|
sLog.outString("Halting process...");
|
||||||
|
|
||||||
|
///- Stop freeze protection before shutdown tasks
|
||||||
|
if (freezeThread)
|
||||||
|
delete freezeThread;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SOAP
|
||||||
|
if (soapThread)
|
||||||
|
delete soapThread;
|
||||||
|
#endif
|
||||||
|
if (raThread)
|
||||||
|
delete raThread;
|
||||||
|
|
||||||
|
delete worldThread;
|
||||||
|
|
||||||
|
///- Remove signal handling before leaving
|
||||||
|
unhook_signals();
|
||||||
|
|
||||||
|
///- Set server offline in realmlist
|
||||||
|
LoginDatabase.DirectPExecute("UPDATE realmlist SET realmflags = realmflags | %u WHERE id = '%u'", REALM_FLAG_OFFLINE, realmID);
|
||||||
|
|
||||||
|
///- Clean account database before leaving
|
||||||
|
clear_online_accounts();
|
||||||
|
|
||||||
|
// send all still queued mass mails (before DB connections shutdown)
|
||||||
|
sMassMailMgr.Update(true);
|
||||||
|
|
||||||
|
///- Wait for DB delay threads to end
|
||||||
|
CharacterDatabase.HaltDelayThread();
|
||||||
|
WorldDatabase.HaltDelayThread();
|
||||||
|
LoginDatabase.HaltDelayThread();
|
||||||
|
|
||||||
|
deleteMTRandTSS();
|
||||||
|
|
||||||
|
// This is done to make sure that we cleanup our so file before it's
|
||||||
|
// unloaded automatically, since the ~ScriptMgr() is called to late
|
||||||
|
// as it's allocated with static storage.
|
||||||
|
sScriptMgr.UnloadScriptLibrary();
|
||||||
|
|
||||||
|
///- Exit the process with specified return value
|
||||||
|
int code = World::GetExitCode();
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sLog.outString("Bye!");
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
/// @}
|
||||||
|
|
@ -101,7 +101,7 @@ class WorldTimer
|
||||||
* @param savetime
|
* @param savetime
|
||||||
* @return uint32
|
* @return uint32
|
||||||
*/
|
*/
|
||||||
static uint32 getMSTime_internal(bool savetime = false);
|
static uint32 getMSTime_internal();
|
||||||
|
|
||||||
static uint32 m_iTime; /**< TODO */
|
static uint32 m_iTime; /**< TODO */
|
||||||
static uint32 m_iPrevTime; /**< TODO */
|
static uint32 m_iPrevTime; /**< TODO */
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005-2015 MaNGOS project <http://getmangos.eu>
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
#include <ace/INET_Addr.h>
|
#include <ace/INET_Addr.h>
|
||||||
|
|
||||||
typedef ACE_TSS<MTRand> MTRandTSS;
|
typedef ACE_TSS<MTRand> MTRandTSS;
|
||||||
static MTRandTSS mtRand;
|
static MTRandTSS *mtRand;
|
||||||
|
|
||||||
static ACE_Time_Value g_SystemTickTime = ACE_OS::gettimeofday();
|
static ACE_Time_Value g_SystemTickTime = ACE_OS::gettimeofday();
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ uint32 WorldTimer::tick()
|
||||||
m_iPrevTime = m_iTime;
|
m_iPrevTime = m_iTime;
|
||||||
|
|
||||||
// get the new one and don't forget to persist current system time in m_SystemTickTime
|
// get the new one and don't forget to persist current system time in m_SystemTickTime
|
||||||
m_iTime = WorldTimer::getMSTime_internal(true);
|
m_iTime = WorldTimer::getMSTime_internal();
|
||||||
|
|
||||||
// return tick diff
|
// return tick diff
|
||||||
return getMSTimeDiff(m_iPrevTime, m_iTime);
|
return getMSTimeDiff(m_iPrevTime, m_iTime);
|
||||||
|
|
@ -58,14 +58,14 @@ uint32 WorldTimer::getMSTime()
|
||||||
return getMSTime_internal();
|
return getMSTime_internal();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 WorldTimer::getMSTime_internal(bool savetime /*= false*/)
|
uint32 WorldTimer::getMSTime_internal()
|
||||||
{
|
{
|
||||||
// get current time
|
// get current time
|
||||||
const ACE_Time_Value currTime = ACE_OS::gettimeofday();
|
const ACE_Time_Value currTime = ACE_OS::gettimeofday();
|
||||||
// calculate time diff between two world ticks
|
// calculate time diff between two world ticks
|
||||||
// special case: curr_time < old_time - we suppose that our time has not ticked at all
|
// special case: curr_time < old_time - we suppose that our time has not ticked at all
|
||||||
// this should be constant value otherwise it is possible that our time can start ticking backwards until next world tick!!!
|
// this should be constant value otherwise it is possible that our time can start ticking backwards until next world tick!!!
|
||||||
ACE_UINT64 diff = 0;
|
uint64 diff = 0;
|
||||||
(currTime - g_SystemTickTime).msec(diff);
|
(currTime - g_SystemTickTime).msec(diff);
|
||||||
|
|
||||||
// lets calculate current world time
|
// lets calculate current world time
|
||||||
|
|
@ -74,44 +74,54 @@ uint32 WorldTimer::getMSTime_internal(bool savetime /*= false*/)
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
void initMTRandTSS()
|
||||||
|
{
|
||||||
|
mtRand = new ACE_TSS<MTRand>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteMTRandTSS()
|
||||||
|
{
|
||||||
|
delete mtRand;
|
||||||
|
}
|
||||||
|
|
||||||
int32 irand(int32 min, int32 max)
|
int32 irand(int32 min, int32 max)
|
||||||
{
|
{
|
||||||
return int32(mtRand->randInt(max - min)) + min;
|
return int32((*mtRand)->randInt(max - min)) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 urand(uint32 min, uint32 max)
|
uint32 urand(uint32 min, uint32 max)
|
||||||
{
|
{
|
||||||
return mtRand->randInt(max - min) + min;
|
return (*mtRand)->randInt(max - min) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
float frand(float min, float max)
|
float frand(float min, float max)
|
||||||
{
|
{
|
||||||
return mtRand->randExc(max - min) + min;
|
return (*mtRand)->randExc(max - min) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 rand32()
|
int32 rand32()
|
||||||
{
|
{
|
||||||
return mtRand->randInt();
|
return (*mtRand)->randInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
double rand_norm(void)
|
double rand_norm(void)
|
||||||
{
|
{
|
||||||
return mtRand->randExc();
|
return (*mtRand)->randExc();
|
||||||
}
|
}
|
||||||
|
|
||||||
float rand_norm_f(void)
|
float rand_norm_f(void)
|
||||||
{
|
{
|
||||||
return (float)mtRand->randExc();
|
return (float)(*mtRand)->randExc();
|
||||||
}
|
}
|
||||||
|
|
||||||
double rand_chance(void)
|
double rand_chance(void)
|
||||||
{
|
{
|
||||||
return mtRand->randExc(100.0);
|
return (*mtRand)->randExc(100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float rand_chance_f(void)
|
float rand_chance_f(void)
|
||||||
{
|
{
|
||||||
return (float)mtRand->randExc(100.0);
|
return (float)(*mtRand)->randExc(100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tokens StrSplit(const std::string& src, const std::string& sep)
|
Tokens StrSplit(const std::string& src, const std::string& sep)
|
||||||
|
|
@ -479,26 +489,26 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
|
||||||
{
|
{
|
||||||
// supported only Cyrillic cases
|
// supported only Cyrillic cases
|
||||||
if (wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5)
|
if (wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5)
|
||||||
return wname;
|
{ return wname; }
|
||||||
|
|
||||||
// Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently)
|
// Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently)
|
||||||
|
|
||||||
static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430), wchar_t(0x0000) };
|
static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430), wchar_t(0x0000)};
|
||||||
static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E), wchar_t(0x0000) };
|
static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E), wchar_t(0x0000)};
|
||||||
static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F), wchar_t(0x0000) };
|
static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F), wchar_t(0x0000)};
|
||||||
static wchar_t const ie_End[] = { wchar_t(1), wchar_t(0x0435), wchar_t(0x0000) };
|
static wchar_t const ie_End[] = { wchar_t(1), wchar_t(0x0435), wchar_t(0x0000)};
|
||||||
static wchar_t const i_End[] = { wchar_t(1), wchar_t(0x0438), wchar_t(0x0000) };
|
static wchar_t const i_End[] = { wchar_t(1), wchar_t(0x0438), wchar_t(0x0000)};
|
||||||
static wchar_t const yeru_End[] = { wchar_t(1), wchar_t(0x044B), wchar_t(0x0000) };
|
static wchar_t const yeru_End[] = { wchar_t(1), wchar_t(0x044B), wchar_t(0x0000)};
|
||||||
static wchar_t const u_End[] = { wchar_t(1), wchar_t(0x0443), wchar_t(0x0000) };
|
static wchar_t const u_End[] = { wchar_t(1), wchar_t(0x0443), wchar_t(0x0000)};
|
||||||
static wchar_t const yu_End[] = { wchar_t(1), wchar_t(0x044E), wchar_t(0x0000) };
|
static wchar_t const yu_End[] = { wchar_t(1), wchar_t(0x044E), wchar_t(0x0000)};
|
||||||
static wchar_t const oj_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x0439), wchar_t(0x0000) };
|
static wchar_t const oj_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x0439), wchar_t(0x0000)};
|
||||||
static wchar_t const ie_j_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x0439), wchar_t(0x0000) };
|
static wchar_t const ie_j_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x0439), wchar_t(0x0000)};
|
||||||
static wchar_t const io_j_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x0439), wchar_t(0x0000) };
|
static wchar_t const io_j_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x0439), wchar_t(0x0000)};
|
||||||
static wchar_t const o_m_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x043C), wchar_t(0x0000) };
|
static wchar_t const o_m_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x043C), wchar_t(0x0000)};
|
||||||
static wchar_t const io_m_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x043C), wchar_t(0x0000) };
|
static wchar_t const io_m_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x043C), wchar_t(0x0000)};
|
||||||
static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x043C), wchar_t(0x0000) };
|
static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x043C), wchar_t(0x0000)};
|
||||||
static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C), wchar_t(0x0000) };
|
static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C), wchar_t(0x0000)};
|
||||||
static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439), wchar_t(0x0000) };
|
static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439), wchar_t(0x0000)};
|
||||||
|
|
||||||
static wchar_t const* const dropEnds[6][8] =
|
static wchar_t const* const dropEnds[6][8] =
|
||||||
{
|
{
|
||||||
|
|
@ -515,7 +525,7 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
|
||||||
size_t len = size_t((*itr)[-1]); // get length from string size field
|
size_t len = size_t((*itr)[-1]); // get length from string size field
|
||||||
|
|
||||||
if (wname.substr(wname.size() - len, len) == *itr)
|
if (wname.substr(wname.size() - len, len) == *itr)
|
||||||
return wname.substr(0, wname.size() - len);
|
{ return wname.substr(0, wname.size() - len); }
|
||||||
}
|
}
|
||||||
|
|
||||||
return wname;
|
return wname;
|
||||||
|
|
@ -569,14 +579,6 @@ bool Utf8FitTo(const std::string& str, std::wstring search)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void utf8printf(FILE* out, const char* str, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, str);
|
|
||||||
vutf8printf(out, str, &ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void vutf8printf(FILE* out, const char* str, va_list* ap)
|
void vutf8printf(FILE* out, const char* str, va_list* ap)
|
||||||
{
|
{
|
||||||
#if PLATFORM == PLATFORM_WINDOWS
|
#if PLATFORM == PLATFORM_WINDOWS
|
||||||
|
|
@ -661,4 +663,29 @@ void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= fals
|
||||||
char buffer[3] = { str[i], str[i + 1], '\0' };
|
char buffer[3] = { str[i], str[i + 1], '\0' };
|
||||||
out[j++] = strtoul(buffer, NULL, 16);
|
out[j++] = strtoul(buffer, NULL, 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void utf8print(void* /*arg*/, const char* str)
|
||||||
|
{
|
||||||
|
#if PLATFORM == PLATFORM_WINDOWS
|
||||||
|
wchar_t wtemp_buf[6000];
|
||||||
|
size_t wtemp_len = 6000 - 1;
|
||||||
|
if (!Utf8toWStr(str, strlen(str), wtemp_buf, wtemp_len))
|
||||||
|
{ return; }
|
||||||
|
|
||||||
|
char temp_buf[6000];
|
||||||
|
CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len + 1);
|
||||||
|
printf("%s", temp_buf);
|
||||||
|
#else
|
||||||
|
printf("%s", str);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void utf8printf(FILE* out, const char* str, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, str);
|
||||||
|
vutf8printf(out, str, &ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005-2015 MaNGOS project <http://getmangos.eu>
|
* Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -109,6 +109,20 @@ inline uint32 secsToTimeBitFields(time_t secs)
|
||||||
return (lt->tm_year - 100) << 24 | lt->tm_mon << 20 | (lt->tm_mday - 1) << 14 | lt->tm_wday << 11 | lt->tm_hour << 6 | lt->tm_min;
|
return (lt->tm_year - 100) << 24 | lt->tm_mon << 20 | (lt->tm_mday - 1) << 14 | lt->tm_wday << 11 | lt->tm_hour << 6 | lt->tm_min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the TSS for MersenneTwister
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void initMTRandTSS();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cleanups the TSS for MersenneTwister
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void deleteMTRandTSS();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a random number in the range min..max; (max-min) must be smaller than 32768.
|
* @brief Return a random number in the range min..max; (max-min) must be smaller than 32768.
|
||||||
*
|
*
|
||||||
|
|
@ -508,7 +522,7 @@ inline bool isBasicLatinString(const std::wstring &wstr, bool numericOrSpace)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < wstr.size(); ++i)
|
for (size_t i = 0; i < wstr.size(); ++i)
|
||||||
if (!isBasicLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
if (!isBasicLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||||
return false;
|
{ return false; }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -523,7 +537,7 @@ inline bool isExtendedLatinString(const std::wstring &wstr, bool numericOrSpace)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < wstr.size(); ++i)
|
for (size_t i = 0; i < wstr.size(); ++i)
|
||||||
if (!isExtendedLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
if (!isExtendedLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||||
return false;
|
{ return false; }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,7 +552,7 @@ inline bool isCyrillicString(const std::wstring &wstr, bool numericOrSpace)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < wstr.size(); ++i)
|
for (size_t i = 0; i < wstr.size(); ++i)
|
||||||
if (!isCyrillicCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
if (!isCyrillicCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||||
return false;
|
{ return false; }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -553,7 +567,7 @@ inline bool isEastAsianString(const std::wstring &wstr, bool numericOrSpace)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < wstr.size(); ++i)
|
for (size_t i = 0; i < wstr.size(); ++i)
|
||||||
if (!isEastAsianCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
if (!isEastAsianCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||||
return false;
|
{ return false; }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -699,6 +713,12 @@ bool Utf8FitTo(const std::string& str, std::wstring search);
|
||||||
* @param str...
|
* @param str...
|
||||||
*/
|
*/
|
||||||
void utf8printf(FILE* out, const char* str, ...);
|
void utf8printf(FILE* out, const char* str, ...);
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
*/
|
||||||
|
void utf8print(void* /*arg*/, const char* str);
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue