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
|
|
@ -35,33 +35,10 @@
|
|||
#include "Config/Config.h"
|
||||
#include "Util.h"
|
||||
#include "AccountMgr.h"
|
||||
#include "CliRunnable.h"
|
||||
#include "MapManager.h"
|
||||
#include "Player.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
|
||||
/// \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)
|
||||
|
|
@ -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>
|
||||
*/
|
||||
|
||||
#ifndef _WORLDSOCKET_H
|
||||
#define _WORLDSOCKET_H
|
||||
#ifndef MANGOS_H_WORLDSOCKET
|
||||
#define MANGOS_H_WORLDSOCKET
|
||||
|
||||
#include <ace/Basic_Types.h>
|
||||
#include <ace/Synch_Traits.h>
|
||||
|
|
@ -53,9 +53,10 @@
|
|||
class ACE_Message_Block;
|
||||
class WorldPacket;
|
||||
class WorldSession;
|
||||
class WorldSocket;
|
||||
|
||||
/// Handler that can communicate over stream sockets.
|
||||
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
|
||||
typedef ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR > WorldAcceptor;
|
||||
|
||||
/**
|
||||
* WorldSocket.
|
||||
|
|
@ -93,16 +94,13 @@ typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
|
|||
* notification.
|
||||
*
|
||||
*/
|
||||
|
||||
class WorldSocket : protected WorldHandler
|
||||
{
|
||||
public:
|
||||
/// Declare some friends
|
||||
friend class ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR >;
|
||||
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.
|
||||
typedef ACE_Thread_Mutex LockType;
|
||||
|
|
|
|||
|
|
@ -27,16 +27,14 @@
|
|||
* \author Derex <derex101@gmail.com>
|
||||
*/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Log.h"
|
||||
#include "Config/Config.h"
|
||||
#include "WorldSocket.h"
|
||||
#include "WorldSocketMgr.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/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/netinet/os_tcp.h>
|
||||
#include <ace/os_include/sys/os_types.h>
|
||||
|
|
@ -44,136 +42,33 @@
|
|||
|
||||
#include <set>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Common.h"
|
||||
#include "Config/Config.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#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
|
||||
WorldSocketMgr::WorldSocketMgr()
|
||||
: m_SockOutKBuff(-1), m_SockOutUBuff(65536), m_UseNoDelay(true),
|
||||
acceptor_(NULL),reactor_(NULL),
|
||||
sockets_()
|
||||
{
|
||||
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)
|
||||
WorldSocketMgr::~WorldSocketMgr()
|
||||
{
|
||||
if (reactor_) delete reactor_;
|
||||
if (acceptor_) delete acceptor_;
|
||||
}
|
||||
|
||||
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);
|
||||
int WorldSocketMgr::svc()
|
||||
{
|
||||
DEBUG_LOG("Starting Network Thread");
|
||||
|
||||
SocketSet::iterator i, t;
|
||||
|
||||
while (!m_Reactor->reactor_event_loop_done())
|
||||
while (!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 (reactor_->run_reactor_event_loop(interval) == -1)
|
||||
{ break; }
|
||||
|
||||
if (m_Reactor->run_reactor_event_loop(interval) == -1)
|
||||
break;
|
||||
|
||||
AddNewSockets();
|
||||
|
||||
for (i = m_Sockets.begin(); i != m_Sockets.end();)
|
||||
for (i = sockets_->begin(); i != sockets_->end();)
|
||||
{
|
||||
if ((*i)->Update() == -1)
|
||||
{
|
||||
|
|
@ -181,138 +76,65 @@ class ReactorRunnable : protected ACE_Task_Base
|
|||
++i;
|
||||
(*t)->CloseSocket();
|
||||
(*t)->RemoveReference();
|
||||
--m_Connections;
|
||||
m_Sockets.erase(t);
|
||||
sockets_->erase(t);
|
||||
}
|
||||
else
|
||||
++i;
|
||||
{ ++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()
|
||||
{
|
||||
delete[] m_NetThreads;
|
||||
delete m_Acceptor;
|
||||
}
|
||||
|
||||
int WorldSocketMgr::StartReactiveIO(ACE_UINT16 port, const char* address)
|
||||
{
|
||||
m_UseNoDelay = sConfig.GetBoolDefault("Network.TcpNodelay", true);
|
||||
|
||||
int WorldSocketMgr::StartNetwork(ACE_INET_Addr& addr)
|
||||
{
|
||||
int num_threads = sConfig.GetIntDefault("Network.Threads", 1);
|
||||
|
||||
if (num_threads <= 0)
|
||||
{
|
||||
sLog.outError("Network.Threads is wrong in your config file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_NetThreadsCount = static_cast<size_t>(num_threads + 1);
|
||||
|
||||
m_NetThreads = new ReactorRunnable[m_NetThreadsCount];
|
||||
|
||||
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);
|
||||
|
||||
if (m_SockOutUBuff <= 0)
|
||||
{
|
||||
sLog.outError("Network.OutUBuff is wrong in your config file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
WorldSocket::Acceptor* acc = new WorldSocket::Acceptor;
|
||||
m_Acceptor = acc;
|
||||
// -1 means use default
|
||||
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");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_NetThreadsCount; ++i)
|
||||
m_NetThreads[i].Start();
|
||||
|
||||
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)
|
||||
if (activate(THR_NEW_LWP | THR_JOINABLE, num_threads) == -1)
|
||||
return -1;
|
||||
|
||||
sLog.outString("Max allowed socket connections: %d", ACE::max_handles());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WorldSocketMgr::StopNetwork()
|
||||
{
|
||||
if (m_Acceptor)
|
||||
{
|
||||
WorldSocket::Acceptor* acc = dynamic_cast<WorldSocket::Acceptor*>(m_Acceptor);
|
||||
|
||||
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();
|
||||
}
|
||||
if (acceptor_) acceptor_->close();
|
||||
if (reactor_) reactor_->end_reactor_event_loop();
|
||||
wait();
|
||||
}
|
||||
|
||||
int WorldSocketMgr::OnSocketOpen(WorldSocket* sock)
|
||||
|
|
@ -341,19 +163,9 @@ int WorldSocketMgr::OnSocketOpen(WorldSocket* sock)
|
|||
|
||||
sock->m_OutBufferSize = static_cast<size_t>(m_SockOutUBuff);
|
||||
|
||||
// we skip the Acceptor Thread
|
||||
size_t min = 1;
|
||||
sock->AddReference();
|
||||
sock->reactor(reactor_);
|
||||
sockets_->insert(sock); //no need for synch here, due to ACE_TSS
|
||||
|
||||
MANGOS_ASSERT(m_NetThreadsCount >= 1);
|
||||
|
||||
for (size_t i = 1; i < m_NetThreadsCount; ++i)
|
||||
if (m_NetThreads[i].Connections() < m_NetThreads[min].Connections())
|
||||
min = i;
|
||||
|
||||
return m_NetThreads[min].AddSocket(sock);
|
||||
}
|
||||
|
||||
WorldSocketMgr* WorldSocketMgr::Instance()
|
||||
{
|
||||
return ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>::instance();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,62 +28,51 @@
|
|||
* \author Derex <derex101@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __WORLDSOCKETMGR_H
|
||||
#define __WORLDSOCKETMGR_H
|
||||
#ifndef MANGOS_H_WORLDSOCKETMGR
|
||||
|
||||
#define MANGOS_H_WORLDSOCKETMGR
|
||||
|
||||
#include <ace/Basic_Types.h>
|
||||
#include <ace/Singleton.h>
|
||||
#include <ace/Thread_Mutex.h>
|
||||
|
||||
#include <string>
|
||||
#include <ace/TSS_T.h>
|
||||
#include <ace/INET_Addr.h>
|
||||
#include <ace/Task.h>
|
||||
#include <ace/Acceptor.h>
|
||||
|
||||
class WorldSocket;
|
||||
class ReactorRunnable;
|
||||
class ACE_Event_Handler;
|
||||
|
||||
/// Manages all sockets connected to peers and network threads
|
||||
class WorldSocketMgr
|
||||
/// This is a pool of threads designed to be used by an ACE_TP_Reactor.
|
||||
/// Manages all sockets connected to peers
|
||||
|
||||
class WorldSocketMgr : public ACE_Task_Base
|
||||
{
|
||||
public:
|
||||
friend class WorldSocket;
|
||||
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 .
|
||||
friend class WorldSocket;
|
||||
public:
|
||||
int StartNetwork(ACE_INET_Addr& addr);
|
||||
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:
|
||||
int OnSocketOpen(WorldSocket* sock);
|
||||
int StartReactiveIO(ACE_UINT16 port, const char* address);
|
||||
virtual int svc();
|
||||
|
||||
WorldSocketMgr();
|
||||
virtual ~WorldSocketMgr();
|
||||
|
||||
ReactorRunnable* m_NetThreads;
|
||||
size_t m_NetThreadsCount;
|
||||
|
||||
private:
|
||||
int m_SockOutKBuff;
|
||||
int m_SockOutUBuff;
|
||||
bool m_UseNoDelay;
|
||||
|
||||
std::string m_addr;
|
||||
ACE_UINT16 m_port;
|
||||
ACE_Reactor *reactor_;
|
||||
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
|
||||
/// @}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@
|
|||
#ifndef MANGOS_MASS_MAIL_MGR_H
|
||||
#define MANGOS_MASS_MAIL_MGR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Mail.h"
|
||||
#include "Policies/Singleton.h"
|
||||
|
|
|
|||
|
|
@ -95,7 +95,8 @@ extern void LoadGameObjectModelList();
|
|||
|
||||
volatile bool World::m_stopEvent = false;
|
||||
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_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE;
|
||||
|
|
|
|||
|
|
@ -496,7 +496,7 @@ typedef UNORDERED_MAP<uint32, WorldSession*> SessionMap;
|
|||
class World
|
||||
{
|
||||
public:
|
||||
static volatile uint32 m_worldLoopCounter;
|
||||
static ACE_Atomic_Op<ACE_Thread_Mutex, uint32> m_worldLoopCounter;
|
||||
|
||||
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)
|
||||
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
|
||||
${SRC_GRP_COMM}
|
||||
${SRC_GRP_RA}
|
||||
${SRC_GRP_MAIN}
|
||||
)
|
||||
|
||||
|
|
@ -50,6 +40,7 @@ endif()
|
|||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src/shared
|
||||
${CMAKE_SOURCE_DIR}/src/shared/Config
|
||||
${CMAKE_SOURCE_DIR}/src/shared/Common
|
||||
${CMAKE_SOURCE_DIR}/src/shared/Threading
|
||||
${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
|
||||
|
||||
#ifndef MANGOS_H_CLIRUNNABLE
|
||||
#define MANGOS_H_CLIRUNNABLE
|
||||
#ifndef MANGOS_H_CLITHREAD
|
||||
#define MANGOS_H_CLITHREAD
|
||||
|
||||
#include "Common.h"
|
||||
#include "Threading.h"
|
||||
#include "ace/Task.h"
|
||||
|
||||
/**
|
||||
* @brief Command Line Interface handling thread
|
||||
*
|
||||
*/
|
||||
class CliRunnable : public ACE_Based::Runnable
|
||||
class CliThread : public ACE_Task_Base
|
||||
{
|
||||
enum { BUFFSIZE = 256 };
|
||||
public:
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void run() override;
|
||||
CliThread(bool);
|
||||
virtual int svc() override;
|
||||
void cli_shutdown();
|
||||
private:
|
||||
char buffer_[BUFFSIZE];
|
||||
bool beep_;
|
||||
};
|
||||
#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
|
||||
|
||||
#ifndef MANGOS_H_MASTER
|
||||
#define MANGOS_H_MASTER
|
||||
#ifndef MANGOS_H_RATHREAD
|
||||
#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 "Policies/Singleton.h"
|
||||
|
||||
/**
|
||||
* @brief Start the server
|
||||
*
|
||||
*/
|
||||
class Master
|
||||
|
||||
class RASocket;
|
||||
class ACE_Reactor;
|
||||
|
||||
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:
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool _StartDB();
|
||||
ACE_Reactor *m_Reactor;
|
||||
RAAcceptor *m_Acceptor;
|
||||
ACE_INET_Addr listen_addr;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void _HookSignals();
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void _UnhookSignals();
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param s
|
||||
*/
|
||||
static void _OnSignal(int s);
|
||||
public:
|
||||
explicit RAThread(uint16 port, const char* host);
|
||||
virtual ~RAThread();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void clearOnlineAccounts();
|
||||
virtual int open(void* unused) override;
|
||||
virtual int svc() override;
|
||||
};
|
||||
|
||||
#define sMaster MaNGOS::Singleton<Master>::Instance()
|
||||
#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.
|
||||
*/
|
||||
|
||||
#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
|
||||
{
|
||||
// create pool
|
||||
SOAPWorkingThread pool;
|
||||
pool.activate(THR_NEW_LWP | THR_JOINABLE, POOL_SIZE);
|
||||
public:
|
||||
void appendToPrintBuffer(const char* msg)
|
||||
{ m_printBuffer += msg; }
|
||||
|
||||
struct soap soap;
|
||||
int m, s;
|
||||
soap_init(&soap);
|
||||
soap_set_imode(&soap, SOAP_C_UTFSTRING);
|
||||
soap_set_omode(&soap, SOAP_C_UTFSTRING);
|
||||
m = soap_bind(&soap, m_host.c_str(), m_port, 100);
|
||||
void setCommandSuccess(bool val)
|
||||
{ m_success = val; }
|
||||
|
||||
// check every 3 seconds if world ended
|
||||
soap.accept_timeout = 3;
|
||||
bool hasCommandSucceeded()
|
||||
{ return m_success; }
|
||||
|
||||
soap.recv_timeout = 5;
|
||||
soap.send_timeout = 5;
|
||||
if (m < 0)
|
||||
static void print(void* callbackArg, const char* msg)
|
||||
{
|
||||
sLog.outError("MaNGOSsoap: couldn't bind to %s:%d", m_host.c_str(), m_port);
|
||||
exit(-1);
|
||||
((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg);
|
||||
}
|
||||
|
||||
sLog.outString("MaNGOSsoap: bound to http://%s:%d", m_host.c_str(), m_port);
|
||||
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
|
||||
pool_ = new SoapPool;
|
||||
pool_->activate(THR_NEW_LWP | THR_JOINABLE, SOAP_THREADS);
|
||||
|
||||
int m;
|
||||
soap_init(&soap_);
|
||||
soap_set_imode(&soap_, SOAP_C_UTFSTRING);
|
||||
soap_set_omode(&soap_, SOAP_C_UTFSTRING);
|
||||
m = soap_bind(&soap_, host_, port_, 100);
|
||||
|
||||
if (m < 0)
|
||||
{
|
||||
sLog.outError("SoapThread: couldn't bind to %s:%d", host_, port_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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())
|
||||
{
|
||||
s = soap_accept(&soap);
|
||||
s = soap_accept(&soap_);
|
||||
|
||||
if (s < 0)
|
||||
{
|
||||
|
|
@ -62,38 +146,28 @@ void MaNGOSsoapRunnable::run()
|
|||
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_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:
|
||||
|
||||
int ns1__executeCommand(char* command, char** result);
|
||||
*/
|
||||
|
||||
int ns1__executeCommand(soap* soap, char* command, char** result)
|
||||
{
|
||||
// security check
|
||||
|
|
@ -135,15 +209,7 @@ int ns1__executeCommand(soap* soap, char* command, char** result)
|
|||
sWorld.QueueCliCommand(cmd);
|
||||
}
|
||||
|
||||
// wait for callback to complete command
|
||||
|
||||
int acc = connection.pendingCommands.acquire();
|
||||
if (acc)
|
||||
{
|
||||
sLog.outError("MaNGOSsoap: Error while acquiring lock, acc = %i, errno = %u", acc, errno);
|
||||
}
|
||||
|
||||
// alright, command finished
|
||||
ACE_OS::sleep(1);
|
||||
|
||||
char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str());
|
||||
if (connection.hasCommandSucceeded())
|
||||
|
|
@ -159,7 +225,6 @@ void SOAPCommand::commandFinished(void* soapconnection, bool success)
|
|||
{
|
||||
SOAPCommand* con = (SOAPCommand*)soapconnection;
|
||||
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
|
||||
*/
|
||||
|
||||
#include "WorldSocketMgr.h"
|
||||
#include "Common.h"
|
||||
#include "WorldSocket.h"
|
||||
#include "WorldSocketMgr.h"
|
||||
#include "World.h"
|
||||
#include "WorldRunnable.h"
|
||||
#include "WorldThread.h"
|
||||
#include "Timer.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "MapManager.h"
|
||||
|
||||
#include "Database/DatabaseEnv.h"
|
||||
|
||||
#ifdef ENABLE_ELUNA
|
||||
|
|
@ -43,32 +43,45 @@
|
|||
#define WORLD_SLEEP_CONST 50
|
||||
|
||||
#ifdef WIN32
|
||||
#include "ServiceWin32.h"
|
||||
extern int m_ServiceStatus;
|
||||
#include "ServiceWin32.h"
|
||||
extern int m_ServiceStatus;
|
||||
#endif
|
||||
|
||||
/// Heartbeat for the World
|
||||
void WorldRunnable::run()
|
||||
WorldThread::WorldThread(uint16 port, const char* host) : listen_addr(port, host)
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
sEluna->OnStartup();
|
||||
#endif /* ENABLE_ELUNA */
|
||||
|
||||
uint32 realCurrTime = 0;
|
||||
uint32 realPrevTime = WorldTimer::tick();
|
||||
activate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// 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 (!World::IsStopped())
|
||||
{
|
||||
++World::m_worldLoopCounter;
|
||||
realCurrTime = WorldTimer::getMSTime();
|
||||
|
||||
uint32 diff = WorldTimer::tick();
|
||||
|
||||
sWorld.Update(diff);
|
||||
realPrevTime = realCurrTime;
|
||||
|
||||
// diff (D0) include time of previous sleep (d0) + tick time (t0)
|
||||
// we want that next d1 + t1 == WORLD_SLEEP_CONST
|
||||
|
|
@ -83,25 +96,21 @@ void WorldRunnable::run()
|
|||
{
|
||||
prevSleepTime = 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_ServiceStatus == 0)
|
||||
if (m_ServiceStatus == 0) //service stopped
|
||||
{
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
}
|
||||
|
||||
while (m_ServiceStatus == 2)
|
||||
while (m_ServiceStatus == 2) //service paused
|
||||
Sleep(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_ELUNA
|
||||
sEluna->OnShutdown();
|
||||
#endif /* ENABLE_ELUNA */
|
||||
|
||||
sWorld.KickAll(); // save and kick all players
|
||||
sWorld.UpdateSessions(1); // real players unload required UpdateSessions call
|
||||
|
||||
sWorldSocketMgr->StopNetwork();
|
||||
|
||||
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.
|
||||
Eluna::Uninitialize();
|
||||
#endif /* ENABLE_ELUNA */
|
||||
|
||||
sLog.outString("World Updater Thread stopped");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -26,24 +26,25 @@
|
|||
/// @{
|
||||
/// \file
|
||||
|
||||
#ifndef MANGOS_H_WORLDRUNNABLE
|
||||
#define MANGOS_H_WORLDRUNNABLE
|
||||
#ifndef MANGOS_H_WORLDTHREAD
|
||||
#define MANGOS_H_WORLDTHREAD
|
||||
|
||||
#include <ace/INET_Addr.h>
|
||||
#include <ace/Task.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Threading.h"
|
||||
|
||||
/**
|
||||
* @brief Heartbeat thread for the World
|
||||
*
|
||||
*/
|
||||
class WorldRunnable : public ACE_Based::Runnable
|
||||
class WorldThread : public ACE_Task_Base
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void run() override;
|
||||
explicit WorldThread(uint16 port, const char* host);
|
||||
virtual int open(void*) override;
|
||||
virtual int svc();
|
||||
private:
|
||||
ACE_INET_Addr listen_addr;
|
||||
};
|
||||
#endif
|
||||
/// @}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
################################################################################
|
||||
|
||||
[MangosdConf]
|
||||
ConfVersion=2016031901
|
||||
ConfVersion=2017021100
|
||||
|
||||
################################################################################
|
||||
# CONNECTIONS AND DIRECTORIES
|
||||
|
|
@ -417,7 +417,7 @@ RaLogFile = "world-remote-access.log"
|
|||
WardenLogFile = "warden.log"
|
||||
WardenLogTimestamp = 0
|
||||
LogColors = "13 7 11 9"
|
||||
SD2ErrorLogFile = "scriptdev2-errors.log"
|
||||
SD3ErrorLogFile = "scriptdev3-errors.log"
|
||||
|
||||
################################################################################
|
||||
# SERVER SETTINGS
|
||||
|
|
@ -1610,8 +1610,9 @@ OutdoorPvp.GHEnabled = 1
|
|||
# NETWORK CONFIG
|
||||
#
|
||||
# Network.Threads
|
||||
# Number of threads for network, recommend 1 thread per 1000 connections.
|
||||
# Default: 1
|
||||
# Number of threads for network queue handling, we recommend a minimum of 3,
|
||||
# additional threads will assist with greater numbers of players.
|
||||
# Default: 3
|
||||
#
|
||||
# Network.OutKBuff
|
||||
# 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.OutUBuff = 65536
|
||||
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
|
||||
* @return uint32
|
||||
*/
|
||||
static uint32 getMSTime_internal(bool savetime = false);
|
||||
static uint32 getMSTime_internal();
|
||||
|
||||
static uint32 m_iTime; /**< TODO */
|
||||
static uint32 m_iPrevTime; /**< TODO */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* 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-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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
#include <ace/INET_Addr.h>
|
||||
|
||||
typedef ACE_TSS<MTRand> MTRandTSS;
|
||||
static MTRandTSS mtRand;
|
||||
static MTRandTSS *mtRand;
|
||||
|
||||
static ACE_Time_Value g_SystemTickTime = ACE_OS::gettimeofday();
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ uint32 WorldTimer::tick()
|
|||
m_iPrevTime = m_iTime;
|
||||
|
||||
// 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 getMSTimeDiff(m_iPrevTime, m_iTime);
|
||||
|
|
@ -58,14 +58,14 @@ uint32 WorldTimer::getMSTime()
|
|||
return getMSTime_internal();
|
||||
}
|
||||
|
||||
uint32 WorldTimer::getMSTime_internal(bool savetime /*= false*/)
|
||||
uint32 WorldTimer::getMSTime_internal()
|
||||
{
|
||||
// get current time
|
||||
const ACE_Time_Value currTime = ACE_OS::gettimeofday();
|
||||
// calculate time diff between two world ticks
|
||||
// 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!!!
|
||||
ACE_UINT64 diff = 0;
|
||||
uint64 diff = 0;
|
||||
(currTime - g_SystemTickTime).msec(diff);
|
||||
|
||||
// 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)
|
||||
{
|
||||
return int32(mtRand->randInt(max - min)) + min;
|
||||
return int32((*mtRand)->randInt(max - min)) + min;
|
||||
}
|
||||
|
||||
uint32 urand(uint32 min, uint32 max)
|
||||
{
|
||||
return mtRand->randInt(max - min) + min;
|
||||
return (*mtRand)->randInt(max - min) + min;
|
||||
}
|
||||
|
||||
float frand(float min, float max)
|
||||
{
|
||||
return mtRand->randExc(max - min) + min;
|
||||
return (*mtRand)->randExc(max - min) + min;
|
||||
}
|
||||
|
||||
int32 rand32()
|
||||
{
|
||||
return mtRand->randInt();
|
||||
return (*mtRand)->randInt();
|
||||
}
|
||||
|
||||
double rand_norm(void)
|
||||
{
|
||||
return mtRand->randExc();
|
||||
return (*mtRand)->randExc();
|
||||
}
|
||||
|
||||
float rand_norm_f(void)
|
||||
{
|
||||
return (float)mtRand->randExc();
|
||||
return (float)(*mtRand)->randExc();
|
||||
}
|
||||
|
||||
double rand_chance(void)
|
||||
{
|
||||
return mtRand->randExc(100.0);
|
||||
return (*mtRand)->randExc(100.0);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -479,26 +489,26 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
|
|||
{
|
||||
// supported only Cyrillic cases
|
||||
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)
|
||||
|
||||
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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* 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
|
||||
|
||||
if (wname.substr(wname.size() - len, len) == *itr)
|
||||
return wname.substr(0, wname.size() - len);
|
||||
{ return wname.substr(0, wname.size() - len); }
|
||||
}
|
||||
|
||||
return wname;
|
||||
|
|
@ -569,14 +579,6 @@ bool Utf8FitTo(const std::string& str, std::wstring search)
|
|||
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)
|
||||
{
|
||||
#if PLATFORM == PLATFORM_WINDOWS
|
||||
|
|
@ -662,3 +664,28 @@ void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= fals
|
|||
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
|
||||
* 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
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
|
|
@ -508,7 +522,7 @@ inline bool isBasicLatinString(const std::wstring &wstr, bool numericOrSpace)
|
|||
{
|
||||
for (size_t i = 0; i < wstr.size(); ++i)
|
||||
if (!isBasicLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||
return false;
|
||||
{ return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -523,7 +537,7 @@ inline bool isExtendedLatinString(const std::wstring &wstr, bool numericOrSpace)
|
|||
{
|
||||
for (size_t i = 0; i < wstr.size(); ++i)
|
||||
if (!isExtendedLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||
return false;
|
||||
{ return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -538,7 +552,7 @@ inline bool isCyrillicString(const std::wstring &wstr, bool numericOrSpace)
|
|||
{
|
||||
for (size_t i = 0; i < wstr.size(); ++i)
|
||||
if (!isCyrillicCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||
return false;
|
||||
{ return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +567,7 @@ inline bool isEastAsianString(const std::wstring &wstr, bool numericOrSpace)
|
|||
{
|
||||
for (size_t i = 0; i < wstr.size(); ++i)
|
||||
if (!isEastAsianCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
|
||||
return false;
|
||||
{ return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -699,6 +713,12 @@ bool Utf8FitTo(const std::string& str, std::wstring search);
|
|||
* @param str...
|
||||
*/
|
||||
void utf8printf(FILE* out, const char* str, ...);
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param str
|
||||
*/
|
||||
void utf8print(void* /*arg*/, const char* str);
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue