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:
H0zen 2017-02-11 22:27:21 +02:00 committed by Antz
parent dfe6c3ec3e
commit f959ae40b6
30 changed files with 1687 additions and 2126 deletions

View file

@ -35,33 +35,10 @@
#include "Config/Config.h" #include "Config/Config.h"
#include "Util.h" #include "Util.h"
#include "AccountMgr.h" #include "AccountMgr.h"
#include "CliRunnable.h"
#include "MapManager.h" #include "MapManager.h"
#include "Player.h" #include "Player.h"
#include "Chat.h" #include "Chat.h"
void utf8print(void* /*arg*/, const char* str)
{
#if PLATFORM == PLATFORM_WINDOWS
wchar_t wtemp_buf[6000];
size_t wtemp_len = 6000 - 1;
if (!Utf8toWStr(str, strlen(str), wtemp_buf, wtemp_len))
{ return; }
char temp_buf[6000];
CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len + 1);
printf("%s", temp_buf);
#else
printf("%s", str);
#endif
}
void commandFinished(void*, bool /*sucess*/)
{
printf("mangos>");
fflush(stdout);
}
/// Delete a user account and all associated characters in this realm /// Delete a user account and all associated characters in this realm
/// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account /// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account
bool ChatHandler::HandleAccountDeleteCommand(char* args) bool ChatHandler::HandleAccountDeleteCommand(char* args)
@ -579,83 +556,3 @@ bool ChatHandler::HandleServerLogLevelCommand(char* args)
} }
/// @} /// @}
#if (PLATFORM == PLATFORM_APPLE) || (PLATFORM == PLATFORM_UNIX)
// Non-blocking keypress detector, when return pressed, return 1, else always return 0
int kb_hit_return()
{
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
#endif
/// %Thread start
void CliRunnable::run()
{
///- Init new SQL thread for the world database (one connection call enough)
WorldDatabase.ThreadStart(); // let thread do safe mySQL requests
char commandbuf[256];
///- Display the list of available CLI functions then beep
sLog.outString();
if (sConfig.GetBoolDefault("BeepAtStart", true))
{ printf("\a"); } // \a = Alert
// print this here the first time
// later it will be printed after command queue updates
printf("mangos>");
///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
while (!World::IsStopped())
{
fflush(stdout);
#if (PLATFORM == PLATFORM_APPLE) || (PLATFORM == PLATFORM_UNIX)
while (!kb_hit_return() && !World::IsStopped())
// With this, we limit CLI to 10commands/second
{ usleep(100); }
if (World::IsStopped())
{ break; }
#endif
char* command_str = fgets(commandbuf, sizeof(commandbuf), stdin);
if (command_str != NULL)
{
for (int x = 0; command_str[x]; ++x)
if (command_str[x] == '\r' || command_str[x] == '\n')
{
command_str[x] = 0;
break;
}
if (!*command_str)
{
printf("mangos>");
continue;
}
std::string command;
if (!consoleToUtf8(command_str, command)) // convert from console encoding to utf8
{
printf("mangos>");
continue;
}
sWorld.QueueCliCommand(new CliCommandHolder(0, SEC_CONSOLE, NULL, command.c_str(), &utf8print, &commandFinished));
}
else if (feof(stdin))
{
World::StopNow(SHUTDOWN_EXIT_CODE);
}
}
///- End the database thread
WorldDatabase.ThreadEnd(); // free mySQL thread resources
}

View file

@ -28,8 +28,8 @@
* \author Derex <derex101@gmail.com> * \author Derex <derex101@gmail.com>
*/ */
#ifndef _WORLDSOCKET_H #ifndef MANGOS_H_WORLDSOCKET
#define _WORLDSOCKET_H #define MANGOS_H_WORLDSOCKET
#include <ace/Basic_Types.h> #include <ace/Basic_Types.h>
#include <ace/Synch_Traits.h> #include <ace/Synch_Traits.h>
@ -53,9 +53,10 @@
class ACE_Message_Block; class ACE_Message_Block;
class WorldPacket; class WorldPacket;
class WorldSession; class WorldSession;
class WorldSocket;
/// Handler that can communicate over stream sockets.
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler; typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
typedef ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR > WorldAcceptor;
/** /**
* WorldSocket. * WorldSocket.
@ -93,16 +94,13 @@ typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
* notification. * notification.
* *
*/ */
class WorldSocket : protected WorldHandler class WorldSocket : protected WorldHandler
{ {
public: public:
/// Declare some friends /// Declare some friends
friend class ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR >; friend class ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR >;
friend class WorldSocketMgr; friend class WorldSocketMgr;
friend class ReactorRunnable;
/// Declare the acceptor for this class
typedef ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR > Acceptor;
/// Mutex type used for various synchronizations. /// Mutex type used for various synchronizations.
typedef ACE_Thread_Mutex LockType; typedef ACE_Thread_Mutex LockType;

View file

@ -27,16 +27,14 @@
* \author Derex <derex101@gmail.com> * \author Derex <derex101@gmail.com>
*/ */
#include "Common.h"
#include "Log.h"
#include "Config/Config.h"
#include "WorldSocket.h"
#include "WorldSocketMgr.h" #include "WorldSocketMgr.h"
#include <ace/ACE.h> #include <ace/ACE.h>
#include <ace/Log_Msg.h>
#include <ace/Reactor.h>
#include <ace/Reactor_Impl.h>
#include <ace/TP_Reactor.h> #include <ace/TP_Reactor.h>
#include <ace/Dev_Poll_Reactor.h>
#include <ace/Guard_T.h>
#include <ace/Atomic_Op.h>
#include <ace/os_include/arpa/os_inet.h> #include <ace/os_include/arpa/os_inet.h>
#include <ace/os_include/netinet/os_tcp.h> #include <ace/os_include/netinet/os_tcp.h>
#include <ace/os_include/sys/os_types.h> #include <ace/os_include/sys/os_types.h>
@ -44,275 +42,99 @@
#include <set> #include <set>
#include "Log.h" WorldSocketMgr::WorldSocketMgr()
#include "Common.h" : m_SockOutKBuff(-1), m_SockOutUBuff(65536), m_UseNoDelay(true),
#include "Config/Config.h" acceptor_(NULL),reactor_(NULL),
#include "Database/DatabaseEnv.h" sockets_()
#include "WorldSocket.h"
#include "Opcodes.h"
/**
* This is a helper class to WorldSocketMgr ,that manages
* network threads, and assigning connections from acceptor thread
* to other network threads
*/
class ReactorRunnable : protected ACE_Task_Base
{ {
public:
ReactorRunnable() :
m_Reactor(0),
m_Connections(0),
m_ThreadId(-1)
{
ACE_Reactor_Impl* imp = 0;
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
imp = new ACE_Dev_Poll_Reactor();
imp->max_notify_iterations(128);
imp->restart(1);
#else
imp = new ACE_TP_Reactor();
imp->max_notify_iterations(128);
#endif
m_Reactor = new ACE_Reactor(imp, 1);
}
virtual ~ReactorRunnable()
{
Stop();
Wait();
delete m_Reactor;
}
void Stop()
{
m_Reactor->end_reactor_event_loop();
}
int Start()
{
if (m_ThreadId != -1)
return -1;
return (m_ThreadId = activate());
}
void Wait() { ACE_Task_Base::wait(); }
long Connections()
{
return static_cast<long>(m_Connections.value());
}
int AddSocket(WorldSocket* sock)
{
ACE_GUARD_RETURN(ACE_Thread_Mutex, Guard, m_NewSockets_Lock, -1);
++m_Connections;
sock->AddReference();
sock->reactor(m_Reactor);
m_NewSockets.insert(sock);
return 0;
}
ACE_Reactor* GetReactor()
{
return m_Reactor;
}
protected:
void AddNewSockets()
{
ACE_GUARD(ACE_Thread_Mutex, Guard, m_NewSockets_Lock);
if (m_NewSockets.empty())
return;
for (SocketSet::const_iterator i = m_NewSockets.begin(); i != m_NewSockets.end(); ++i)
{
WorldSocket* sock = (*i);
if (sock->IsClosed())
{
sock->RemoveReference();
--m_Connections;
}
else
m_Sockets.insert(sock);
}
m_NewSockets.clear();
}
virtual int svc()
{
DEBUG_LOG("Network Thread Starting");
WorldDatabase.ThreadStart();
MANGOS_ASSERT(m_Reactor);
SocketSet::iterator i, t;
while (!m_Reactor->reactor_event_loop_done())
{
// dont be too smart to move this outside the loop
// the run_reactor_event_loop will modify interval
ACE_Time_Value interval(0, 10000);
if (m_Reactor->run_reactor_event_loop(interval) == -1)
break;
AddNewSockets();
for (i = m_Sockets.begin(); i != m_Sockets.end();)
{
if ((*i)->Update() == -1)
{
t = i;
++i;
(*t)->CloseSocket();
(*t)->RemoveReference();
--m_Connections;
m_Sockets.erase(t);
}
else
++i;
}
}
WorldDatabase.ThreadEnd();
DEBUG_LOG("Network Thread Exitting");
return 0;
}
private:
typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> AtomicInt;
typedef std::set<WorldSocket*> SocketSet;
ACE_Reactor* m_Reactor;
AtomicInt m_Connections;
int m_ThreadId;
SocketSet m_Sockets;
SocketSet m_NewSockets;
ACE_Thread_Mutex m_NewSockets_Lock;
};
WorldSocketMgr::WorldSocketMgr():
m_NetThreads(0),
m_NetThreadsCount(0),
m_SockOutKBuff(-1),
m_SockOutUBuff(65536),
m_UseNoDelay(true),
m_Acceptor(0)
{
InitializeOpcodes();
} }
WorldSocketMgr::~WorldSocketMgr() WorldSocketMgr::~WorldSocketMgr()
{ {
delete[] m_NetThreads; if (reactor_) delete reactor_;
delete m_Acceptor; if (acceptor_) delete acceptor_;
} }
int WorldSocketMgr::StartReactiveIO(ACE_UINT16 port, const char* address)
int WorldSocketMgr::svc()
{ {
m_UseNoDelay = sConfig.GetBoolDefault("Network.TcpNodelay", true); DEBUG_LOG("Starting Network Thread");
SocketSet::iterator i, t;
while (!reactor_->reactor_event_loop_done())
{
ACE_Time_Value interval(0, 10000);
if (reactor_->run_reactor_event_loop(interval) == -1)
{ break; }
for (i = sockets_->begin(); i != sockets_->end();)
{
if ((*i)->Update() == -1)
{
t = i;
++i;
(*t)->CloseSocket();
(*t)->RemoveReference();
sockets_->erase(t);
}
else
{ ++i; }
}
}
DEBUG_LOG("Network Thread Exitting");
return 0;
}
int WorldSocketMgr::StartNetwork(ACE_INET_Addr& addr)
{
int num_threads = sConfig.GetIntDefault("Network.Threads", 1); int num_threads = sConfig.GetIntDefault("Network.Threads", 1);
if (num_threads <= 0) if (num_threads <= 0)
{ {
sLog.outError("Network.Threads is wrong in your config file"); sLog.outError("Network.Threads is wrong in your config file");
return -1; return -1;
} }
m_NetThreadsCount = static_cast<size_t>(num_threads + 1);
m_NetThreads = new ReactorRunnable[m_NetThreadsCount];
BASIC_LOG("Max allowed socket connections %d", ACE::max_handles());
// -1 means use default
m_SockOutKBuff = sConfig.GetIntDefault("Network.OutKBuff", -1);
m_SockOutUBuff = sConfig.GetIntDefault("Network.OutUBuff", 65536); m_SockOutUBuff = sConfig.GetIntDefault("Network.OutUBuff", 65536);
if (m_SockOutUBuff <= 0) if (m_SockOutUBuff <= 0)
{ {
sLog.outError("Network.OutUBuff is wrong in your config file"); sLog.outError("Network.OutUBuff is wrong in your config file");
return -1; return -1;
} }
WorldSocket::Acceptor* acc = new WorldSocket::Acceptor; // -1 means use default
m_Acceptor = acc; m_SockOutKBuff = sConfig.GetIntDefault("Network.OutKBuff", -1);
m_UseNoDelay = sConfig.GetBoolDefault("Network.TcpNodelay", true);
ACE_INET_Addr listen_addr(port, address);
if (acc->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1) ACE_Reactor_Impl* imp = 0;
imp = new ACE_TP_Reactor();
imp->max_notify_iterations(128);
reactor_ = new ACE_Reactor(imp, 1);
acceptor_ = new WorldAcceptor;
if (acceptor_->open(addr, reactor_, ACE_NONBLOCK) == -1)
{ {
sLog.outError("Failed to open acceptor, check if the port is free"); sLog.outError("Failed to open acceptor, check if the port is free");
return -1; return -1;
} }
for (size_t i = 0; i < m_NetThreadsCount; ++i) if (activate(THR_NEW_LWP | THR_JOINABLE, num_threads) == -1)
m_NetThreads[i].Start(); return -1;
return 0;
}
int WorldSocketMgr::StartNetwork(ACE_UINT16 port, std::string& address)
{
m_addr = address;
m_port = port;
if (!sLog.HasLogLevelOrHigher(LOG_LVL_DEBUG))
ACE_Log_Msg::instance()->priority_mask(LM_ERROR, ACE_Log_Msg::PROCESS);
if (StartReactiveIO(port, address.c_str()) == -1)
return -1;
sLog.outString("Max allowed socket connections: %d", ACE::max_handles());
return 0; return 0;
} }
void WorldSocketMgr::StopNetwork() void WorldSocketMgr::StopNetwork()
{ {
if (m_Acceptor) if (acceptor_) acceptor_->close();
{ if (reactor_) reactor_->end_reactor_event_loop();
WorldSocket::Acceptor* acc = dynamic_cast<WorldSocket::Acceptor*>(m_Acceptor); wait();
if (acc)
acc->close();
}
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Stop();
}
Wait();
}
void WorldSocketMgr::Wait()
{
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Wait();
}
} }
int WorldSocketMgr::OnSocketOpen(WorldSocket* sock) int WorldSocketMgr::OnSocketOpen(WorldSocket* sock)
@ -341,19 +163,9 @@ int WorldSocketMgr::OnSocketOpen(WorldSocket* sock)
sock->m_OutBufferSize = static_cast<size_t>(m_SockOutUBuff); sock->m_OutBufferSize = static_cast<size_t>(m_SockOutUBuff);
// we skip the Acceptor Thread sock->AddReference();
size_t min = 1; sock->reactor(reactor_);
sockets_->insert(sock); //no need for synch here, due to ACE_TSS
MANGOS_ASSERT(m_NetThreadsCount >= 1); return 0;
for (size_t i = 1; i < m_NetThreadsCount; ++i)
if (m_NetThreads[i].Connections() < m_NetThreads[min].Connections())
min = i;
return m_NetThreads[min].AddSocket(sock);
}
WorldSocketMgr* WorldSocketMgr::Instance()
{
return ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>::instance();
} }

View file

@ -28,62 +28,51 @@
* \author Derex <derex101@gmail.com> * \author Derex <derex101@gmail.com>
*/ */
#ifndef __WORLDSOCKETMGR_H #ifndef MANGOS_H_WORLDSOCKETMGR
#define __WORLDSOCKETMGR_H
#define MANGOS_H_WORLDSOCKETMGR
#include <ace/Basic_Types.h> #include <ace/Basic_Types.h>
#include <ace/Singleton.h> #include <ace/Singleton.h>
#include <ace/Thread_Mutex.h> #include <ace/TSS_T.h>
#include <ace/INET_Addr.h>
#include <string> #include <ace/Task.h>
#include <ace/Acceptor.h>
class WorldSocket; class WorldSocket;
class ReactorRunnable;
class ACE_Event_Handler;
/// Manages all sockets connected to peers and network threads /// This is a pool of threads designed to be used by an ACE_TP_Reactor.
class WorldSocketMgr /// Manages all sockets connected to peers
class WorldSocketMgr : public ACE_Task_Base
{ {
friend class ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>;
friend class WorldSocket;
public: public:
friend class WorldSocket; int StartNetwork(ACE_INET_Addr& addr);
friend class ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>;
/// Start network, listen at address:port .
int StartNetwork(ACE_UINT16 port, std::string& address);
/// Stops all network threads, It will wait for all running threads .
void StopNetwork(); void StopNetwork();
/// Wait untill all network threads have "joined" .
void Wait();
std::string& GetBindAddress() { return m_addr; }
ACE_UINT16 GetBindPort() { return m_port; }
/// Make this class singleton .
static WorldSocketMgr* Instance();
private: private:
int OnSocketOpen(WorldSocket* sock); int OnSocketOpen(WorldSocket* sock);
int StartReactiveIO(ACE_UINT16 port, const char* address); virtual int svc();
WorldSocketMgr(); WorldSocketMgr();
virtual ~WorldSocketMgr(); virtual ~WorldSocketMgr();
ReactorRunnable* m_NetThreads; private:
size_t m_NetThreadsCount;
int m_SockOutKBuff; int m_SockOutKBuff;
int m_SockOutUBuff; int m_SockOutUBuff;
bool m_UseNoDelay; bool m_UseNoDelay;
std::string m_addr; ACE_Reactor *reactor_;
ACE_UINT16 m_port; WorldAcceptor *acceptor_;
typedef std::set<WorldSocket*> SocketSet;
ACE_TSS<SocketSet> sockets_;
ACE_Event_Handler* m_Acceptor;
}; };
#define sWorldSocketMgr WorldSocketMgr::Instance() #define sWorldSocketMgr ACE_Singleton<WorldSocketMgr, ACE_Thread_Mutex>::instance()
#endif #endif
/// @} /// @}

View file

@ -40,6 +40,8 @@
#ifndef MANGOS_MASS_MAIL_MGR_H #ifndef MANGOS_MASS_MAIL_MGR_H
#define MANGOS_MASS_MAIL_MGR_H #define MANGOS_MASS_MAIL_MGR_H
#include <memory>
#include "Common.h" #include "Common.h"
#include "Mail.h" #include "Mail.h"
#include "Policies/Singleton.h" #include "Policies/Singleton.h"

View file

@ -95,7 +95,8 @@ extern void LoadGameObjectModelList();
volatile bool World::m_stopEvent = false; volatile bool World::m_stopEvent = false;
uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE;
volatile uint32 World::m_worldLoopCounter = 0;
ACE_Atomic_Op<ACE_Thread_Mutex, uint32> World::m_worldLoopCounter = 0;
float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE; float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE;
float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE; float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE;

View file

@ -496,7 +496,7 @@ typedef UNORDERED_MAP<uint32, WorldSession*> SessionMap;
class World class World
{ {
public: public:
static volatile uint32 m_worldLoopCounter; static ACE_Atomic_Op<ACE_Thread_Mutex, uint32> m_worldLoopCounter;
World(); World();
~World(); ~World();

View file

@ -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
View 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
View 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

View file

@ -23,17 +23,7 @@ set(EXECUTABLE_NAME mangosd)
file(GLOB SRC_GRP_MAIN *.cpp *.h) file(GLOB SRC_GRP_MAIN *.cpp *.h)
source_group("Main" FILES ${SRC_GRP_MAIN}) source_group("Main" FILES ${SRC_GRP_MAIN})
#Command Line Files
file(GLOB SRC_GRP_COMM Comm/*.cpp Comm/*.h)
source_group("Command Line" FILES ${SRC_GRP_COMM})
#Remote Access Files
file(GLOB SRC_GRP_RA RA/*.cpp RA/*.h)
source_group("Remote Access" FILES ${SRC_GRP_RA})
set(EXECUTABLE_SRCS set(EXECUTABLE_SRCS
${SRC_GRP_COMM}
${SRC_GRP_RA}
${SRC_GRP_MAIN} ${SRC_GRP_MAIN}
) )
@ -50,6 +40,7 @@ endif()
include_directories( include_directories(
${CMAKE_SOURCE_DIR}/src/shared ${CMAKE_SOURCE_DIR}/src/shared
${CMAKE_SOURCE_DIR}/src/shared/Config
${CMAKE_SOURCE_DIR}/src/shared/Common ${CMAKE_SOURCE_DIR}/src/shared/Common
${CMAKE_SOURCE_DIR}/src/shared/Threading ${CMAKE_SOURCE_DIR}/src/shared/Threading
${CMAKE_SOURCE_DIR}/src/shared/Utilities ${CMAKE_SOURCE_DIR}/src/shared/Utilities

141
src/mangosd/CliThread.cpp Normal file
View 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
}

View file

@ -26,24 +26,25 @@
/// @{ /// @{
/// \file /// \file
#ifndef MANGOS_H_CLIRUNNABLE #ifndef MANGOS_H_CLITHREAD
#define MANGOS_H_CLIRUNNABLE #define MANGOS_H_CLITHREAD
#include "Common.h" #include "ace/Task.h"
#include "Threading.h"
/** /**
* @brief Command Line Interface handling thread * @brief Command Line Interface handling thread
* *
*/ */
class CliRunnable : public ACE_Based::Runnable class CliThread : public ACE_Task_Base
{ {
enum { BUFFSIZE = 256 };
public: public:
/** CliThread(bool);
* @brief virtual int svc() override;
* void cli_shutdown();
*/ private:
void run() override; char buffer_[BUFFSIZE];
bool beep_;
}; };
#endif #endif
/// @} /// @}

View file

@ -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
}
/// @}

View file

@ -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
}

View file

@ -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;
}

View file

@ -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
View 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;
}

View file

@ -26,69 +26,36 @@
/// @{ /// @{
/// \file /// \file
#ifndef MANGOS_H_MASTER #ifndef MANGOS_H_RATHREAD
#define MANGOS_H_MASTER #define MANGOS_H_RATHREAD
#include <ace/SOCK_Acceptor.h>
#include <ace/Acceptor.h>
#include <ace/Task.h>
#include <ace/INET_Addr.h>
#include "Common.h" #include "Common.h"
#include "Policies/Singleton.h"
/**
* @brief Start the server class RASocket;
* class ACE_Reactor;
*/
class Master typedef ACE_Acceptor < RASocket, ACE_SOCK_ACCEPTOR > RAAcceptor;
class RAThread : public ACE_Task_Base
{ {
public:
/**
* @brief
*
*/
Master();
/**
* @brief
*
*/
~Master();
/**
* @brief
*
* @return int
*/
int Run();
static volatile uint32 m_masterLoopCounter; /**< TODO */
private: private:
/** ACE_Reactor *m_Reactor;
* @brief RAAcceptor *m_Acceptor;
* ACE_INET_Addr listen_addr;
* @return bool
*/
bool _StartDB();
/** public:
* @brief explicit RAThread(uint16 port, const char* host);
* virtual ~RAThread();
*/
void _HookSignals();
/**
* @brief
*
*/
void _UnhookSignals();
/**
* @brief
*
* @param s
*/
static void _OnSignal(int s);
/** virtual int open(void* unused) override;
* @brief virtual int svc() override;
*
*/
void clearOnlineAccounts();
}; };
#define sMaster MaNGOS::Singleton<Master>::Instance()
#endif #endif
/// @}

View file

@ -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

View file

@ -22,39 +22,123 @@
* and lore are copyrighted by Blizzard Entertainment, Inc. * and lore are copyrighted by Blizzard Entertainment, Inc.
*/ */
#include "MaNGOSsoap.h" #include <ace/OS.h>
#include <ace/Message_Block.h>
#define POOL_SIZE 5 #include "SoapThread.h"
#include "soapH.h"
#include "soapStub.h"
void MaNGOSsoapRunnable::run() #include "World.h"
#include "Log.h"
#include "AccountMgr.h"
/// WARNING! This code needs serious reviewing
struct SOAPCommand
{
public:
void appendToPrintBuffer(const char* msg)
{ m_printBuffer += msg; }
void setCommandSuccess(bool val)
{ m_success = val; }
bool hasCommandSucceeded()
{ return m_success; }
static void print(void* callbackArg, const char* msg)
{
((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg);
}
static void commandFinished(void* callbackArg, bool success);
bool m_success;
std::string m_printBuffer;
};
class SoapPool : public ACE_Task<ACE_MT_SYNCH>
{
public:
virtual int svc(void) override
{
while (1)
{
ACE_Message_Block* mb = 0;
if (this->getq(mb) == -1)
{
break;
}
// Process the message.
process_message(mb);
}
return 0;
}
private:
void process_message(ACE_Message_Block* mb)
{
struct soap* soap;
ACE_OS::memcpy(&soap, mb->rd_ptr(), sizeof(struct soap*));
mb->release();
soap_serve(soap);
soap_destroy(soap); // dealloc C++ data
soap_end(soap); // dealloc data and clean up
soap_done(soap); // detach soap struct
free(soap);
}
};
SoapThread::~SoapThread()
{
if(pool_)
delete pool_;
}
int SoapThread::open(void* unused)
{ {
// create pool // create pool
SOAPWorkingThread pool; pool_ = new SoapPool;
pool.activate(THR_NEW_LWP | THR_JOINABLE, POOL_SIZE); pool_->activate(THR_NEW_LWP | THR_JOINABLE, SOAP_THREADS);
struct soap soap; int m;
int m, s; soap_init(&soap_);
soap_init(&soap); soap_set_imode(&soap_, SOAP_C_UTFSTRING);
soap_set_imode(&soap, SOAP_C_UTFSTRING); soap_set_omode(&soap_, SOAP_C_UTFSTRING);
soap_set_omode(&soap, SOAP_C_UTFSTRING); m = soap_bind(&soap_, host_, port_, 100);
m = soap_bind(&soap, m_host.c_str(), m_port, 100);
// check every 3 seconds if world ended
soap.accept_timeout = 3;
soap.recv_timeout = 5;
soap.send_timeout = 5;
if (m < 0) if (m < 0)
{ {
sLog.outError("MaNGOSsoap: couldn't bind to %s:%d", m_host.c_str(), m_port); sLog.outError("SoapThread: couldn't bind to %s:%d", host_, port_);
exit(-1); return -1;
} }
sLog.outString("MaNGOSsoap: bound to http://%s:%d", m_host.c_str(), m_port); // check every 3 seconds if world ended
soap_.accept_timeout = 3;
soap_.recv_timeout = 5;
soap_.send_timeout = 5;
activate();
return 0;
}
int SoapThread::svc()
{
int s;
sLog.outString("SOAP Thread started (listening on %s:%d)", host_, port_);
while (!World::IsStopped()) while (!World::IsStopped())
{ {
s = soap_accept(&soap); s = soap_accept(&soap_);
if (s < 0) if (s < 0)
{ {
@ -62,38 +146,28 @@ void MaNGOSsoapRunnable::run()
continue; continue;
} }
DEBUG_LOG("MaNGOSsoap: accepted connection from IP=%d.%d.%d.%d", (int)(soap.ip >> 24) & 0xFF, (int)(soap.ip >> 16) & 0xFF, (int)(soap.ip >> 8) & 0xFF, (int)soap.ip & 0xFF); struct soap* thread_soap = soap_copy(&soap_);// make a safe copy
struct soap* thread_soap = soap_copy(&soap);// make a safe copy
ACE_Message_Block* mb = new ACE_Message_Block(sizeof(struct soap*)); ACE_Message_Block* mb = new ACE_Message_Block(sizeof(struct soap*));
ACE_OS::memcpy(mb->wr_ptr(), &thread_soap, sizeof(struct soap*)); ACE_OS::memcpy(mb->wr_ptr(), &thread_soap, sizeof(struct soap*));
pool.putq(mb); pool_->putq(mb);
} }
pool.msg_queue()->deactivate();
pool.wait();
soap_done(&soap); pool_->msg_queue()->deactivate();
pool_->wait();
soap_done(&soap_);
sLog.outString("SOAP Thread stopped");
return 0;
} }
void SOAPWorkingThread::process_message(ACE_Message_Block* mb)
{
ACE_TRACE(ACE_TEXT("SOAPWorkingThread::process_message"));
struct soap* soap;
ACE_OS::memcpy(&soap, mb->rd_ptr(), sizeof(struct soap*));
mb->release();
soap_serve(soap);
soap_destroy(soap); // dealloc C++ data
soap_end(soap); // dealloc data and clean up
soap_done(soap); // detach soap struct
free(soap);
}
/* /*
Code used for generating stubs: Code used for generating stubs:
int ns1__executeCommand(char* command, char** result); int ns1__executeCommand(char* command, char** result);
*/ */
int ns1__executeCommand(soap* soap, char* command, char** result) int ns1__executeCommand(soap* soap, char* command, char** result)
{ {
// security check // security check
@ -135,15 +209,7 @@ int ns1__executeCommand(soap* soap, char* command, char** result)
sWorld.QueueCliCommand(cmd); sWorld.QueueCliCommand(cmd);
} }
// wait for callback to complete command ACE_OS::sleep(1);
int acc = connection.pendingCommands.acquire();
if (acc)
{
sLog.outError("MaNGOSsoap: Error while acquiring lock, acc = %i, errno = %u", acc, errno);
}
// alright, command finished
char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str()); char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str());
if (connection.hasCommandSucceeded()) if (connection.hasCommandSucceeded())
@ -159,7 +225,6 @@ void SOAPCommand::commandFinished(void* soapconnection, bool success)
{ {
SOAPCommand* con = (SOAPCommand*)soapconnection; SOAPCommand* con = (SOAPCommand*)soapconnection;
con->setCommandSuccess(success); con->setCommandSuccess(success);
con->pendingCommands.release();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View 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

View file

@ -26,14 +26,14 @@
\ingroup mangosd \ingroup mangosd
*/ */
#include "WorldSocketMgr.h"
#include "Common.h" #include "Common.h"
#include "WorldSocket.h"
#include "WorldSocketMgr.h"
#include "World.h" #include "World.h"
#include "WorldRunnable.h" #include "WorldThread.h"
#include "Timer.h" #include "Timer.h"
#include "ObjectAccessor.h" #include "ObjectAccessor.h"
#include "MapManager.h" #include "MapManager.h"
#include "Database/DatabaseEnv.h" #include "Database/DatabaseEnv.h"
#ifdef ENABLE_ELUNA #ifdef ENABLE_ELUNA
@ -43,32 +43,45 @@
#define WORLD_SLEEP_CONST 50 #define WORLD_SLEEP_CONST 50
#ifdef WIN32 #ifdef WIN32
#include "ServiceWin32.h" #include "ServiceWin32.h"
extern int m_ServiceStatus; extern int m_ServiceStatus;
#endif #endif
/// Heartbeat for the World WorldThread::WorldThread(uint16 port, const char* host) : listen_addr(port, host)
void WorldRunnable::run()
{ {
}
int WorldThread::open(void* unused)
{
if (sWorldSocketMgr->StartNetwork(listen_addr) == -1)
{
sLog.outError("Failed to start network");
Log::WaitBeforeContinueIfNeed();
World::StopNow(ERROR_EXIT_CODE);
return -1;
}
#ifdef ENABLE_ELUNA #ifdef ENABLE_ELUNA
sEluna->OnStartup(); sEluna->OnStartup();
#endif /* ENABLE_ELUNA */ #endif /* ENABLE_ELUNA */
uint32 realCurrTime = 0; activate();
uint32 realPrevTime = WorldTimer::tick(); return 0;
}
uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST /// Heartbeat for the World
int WorldThread::svc()
{
uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST
sLog.outString("World Updater Thread started (%dms min update interval)", WORLD_SLEEP_CONST);
///- While we have not World::m_stopEvent, update the world ///- While we have not World::m_stopEvent, update the world
while (!World::IsStopped()) while (!World::IsStopped())
{ {
++World::m_worldLoopCounter; ++World::m_worldLoopCounter;
realCurrTime = WorldTimer::getMSTime();
uint32 diff = WorldTimer::tick(); uint32 diff = WorldTimer::tick();
sWorld.Update(diff); sWorld.Update(diff);
realPrevTime = realCurrTime;
// diff (D0) include time of previous sleep (d0) + tick time (t0) // diff (D0) include time of previous sleep (d0) + tick time (t0)
// we want that next d1 + t1 == WORLD_SLEEP_CONST // we want that next d1 + t1 == WORLD_SLEEP_CONST
@ -83,25 +96,21 @@ void WorldRunnable::run()
{ {
prevSleepTime = 0; prevSleepTime = 0;
} }
#ifdef _WIN32 #ifdef _WIN32
if (m_ServiceStatus == 0) if (m_ServiceStatus == 0) //service stopped
{ {
World::StopNow(SHUTDOWN_EXIT_CODE); World::StopNow(SHUTDOWN_EXIT_CODE);
} }
while (m_ServiceStatus == 2) while (m_ServiceStatus == 2) //service paused
Sleep(1000); Sleep(1000);
#endif #endif
} }
#ifdef ENABLE_ELUNA #ifdef ENABLE_ELUNA
sEluna->OnShutdown(); sEluna->OnShutdown();
#endif /* ENABLE_ELUNA */ #endif /* ENABLE_ELUNA */
sWorld.KickAll(); // save and kick all players sWorld.KickAll(); // save and kick all players
sWorld.UpdateSessions(1); // real players unload required UpdateSessions call sWorld.UpdateSessions(1); // real players unload required UpdateSessions call
sWorldSocketMgr->StopNetwork(); sWorldSocketMgr->StopNetwork();
sMapMgr.UnloadAll(); // unload all grids (including locked in memory) sMapMgr.UnloadAll(); // unload all grids (including locked in memory)
@ -111,4 +120,7 @@ void WorldRunnable::run()
// and must be unloaded before the DB, since it can access the DB. // and must be unloaded before the DB, since it can access the DB.
Eluna::Uninitialize(); Eluna::Uninitialize();
#endif /* ENABLE_ELUNA */ #endif /* ENABLE_ELUNA */
sLog.outString("World Updater Thread stopped");
return 0;
} }

View file

@ -26,24 +26,25 @@
/// @{ /// @{
/// \file /// \file
#ifndef MANGOS_H_WORLDRUNNABLE #ifndef MANGOS_H_WORLDTHREAD
#define MANGOS_H_WORLDRUNNABLE #define MANGOS_H_WORLDTHREAD
#include <ace/INET_Addr.h>
#include <ace/Task.h>
#include "Common.h" #include "Common.h"
#include "Threading.h"
/** /**
* @brief Heartbeat thread for the World * @brief Heartbeat thread for the World
* *
*/ */
class WorldRunnable : public ACE_Based::Runnable class WorldThread : public ACE_Task_Base
{ {
public: public:
/** explicit WorldThread(uint16 port, const char* host);
* @brief virtual int open(void*) override;
* virtual int svc();
*/ private:
void run() override; ACE_INET_Addr listen_addr;
}; };
#endif #endif
/// @} /// @}

View file

@ -3,7 +3,7 @@
################################################################################ ################################################################################
[MangosdConf] [MangosdConf]
ConfVersion=2016031901 ConfVersion=2017021100
################################################################################ ################################################################################
# CONNECTIONS AND DIRECTORIES # CONNECTIONS AND DIRECTORIES
@ -417,7 +417,7 @@ RaLogFile = "world-remote-access.log"
WardenLogFile = "warden.log" WardenLogFile = "warden.log"
WardenLogTimestamp = 0 WardenLogTimestamp = 0
LogColors = "13 7 11 9" LogColors = "13 7 11 9"
SD2ErrorLogFile = "scriptdev2-errors.log" SD3ErrorLogFile = "scriptdev3-errors.log"
################################################################################ ################################################################################
# SERVER SETTINGS # SERVER SETTINGS
@ -1610,8 +1610,9 @@ OutdoorPvp.GHEnabled = 1
# NETWORK CONFIG # NETWORK CONFIG
# #
# Network.Threads # Network.Threads
# Number of threads for network, recommend 1 thread per 1000 connections. # Number of threads for network queue handling, we recommend a minimum of 3,
# Default: 1 # additional threads will assist with greater numbers of players.
# Default: 3
# #
# Network.OutKBuff # Network.OutKBuff
# The size of the output kernel buffer used ( SO_SNDBUF socket option, tcp manual ). # The size of the output kernel buffer used ( SO_SNDBUF socket option, tcp manual ).
@ -1633,7 +1634,7 @@ OutdoorPvp.GHEnabled = 1
# #
################################################################################ ################################################################################
Network.Threads = 1 Network.Threads = 3
Network.OutKBuff = -1 Network.OutKBuff = -1
Network.OutUBuff = 65536 Network.OutUBuff = 65536
Network.TcpNodelay = 1 Network.TcpNodelay = 1

585
src/mangosd/mangosd.cpp Normal file
View 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;
}
/// @}

View file

@ -101,7 +101,7 @@ class WorldTimer
* @param savetime * @param savetime
* @return uint32 * @return uint32
*/ */
static uint32 getMSTime_internal(bool savetime = false); static uint32 getMSTime_internal();
static uint32 m_iTime; /**< TODO */ static uint32 m_iTime; /**< TODO */
static uint32 m_iPrevTime; /**< TODO */ static uint32 m_iPrevTime; /**< TODO */

View file

@ -2,7 +2,7 @@
* MaNGOS is a full featured server for World of Warcraft, supporting * MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8 * the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
* *
* Copyright (C) 2005-2015 MaNGOS project <http://getmangos.eu> * Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -31,7 +31,7 @@
#include <ace/INET_Addr.h> #include <ace/INET_Addr.h>
typedef ACE_TSS<MTRand> MTRandTSS; typedef ACE_TSS<MTRand> MTRandTSS;
static MTRandTSS mtRand; static MTRandTSS *mtRand;
static ACE_Time_Value g_SystemTickTime = ACE_OS::gettimeofday(); static ACE_Time_Value g_SystemTickTime = ACE_OS::gettimeofday();
@ -47,7 +47,7 @@ uint32 WorldTimer::tick()
m_iPrevTime = m_iTime; m_iPrevTime = m_iTime;
// get the new one and don't forget to persist current system time in m_SystemTickTime // get the new one and don't forget to persist current system time in m_SystemTickTime
m_iTime = WorldTimer::getMSTime_internal(true); m_iTime = WorldTimer::getMSTime_internal();
// return tick diff // return tick diff
return getMSTimeDiff(m_iPrevTime, m_iTime); return getMSTimeDiff(m_iPrevTime, m_iTime);
@ -58,14 +58,14 @@ uint32 WorldTimer::getMSTime()
return getMSTime_internal(); return getMSTime_internal();
} }
uint32 WorldTimer::getMSTime_internal(bool savetime /*= false*/) uint32 WorldTimer::getMSTime_internal()
{ {
// get current time // get current time
const ACE_Time_Value currTime = ACE_OS::gettimeofday(); const ACE_Time_Value currTime = ACE_OS::gettimeofday();
// calculate time diff between two world ticks // calculate time diff between two world ticks
// special case: curr_time < old_time - we suppose that our time has not ticked at all // special case: curr_time < old_time - we suppose that our time has not ticked at all
// this should be constant value otherwise it is possible that our time can start ticking backwards until next world tick!!! // this should be constant value otherwise it is possible that our time can start ticking backwards until next world tick!!!
ACE_UINT64 diff = 0; uint64 diff = 0;
(currTime - g_SystemTickTime).msec(diff); (currTime - g_SystemTickTime).msec(diff);
// lets calculate current world time // lets calculate current world time
@ -74,44 +74,54 @@ uint32 WorldTimer::getMSTime_internal(bool savetime /*= false*/)
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void initMTRandTSS()
{
mtRand = new ACE_TSS<MTRand>();
}
void deleteMTRandTSS()
{
delete mtRand;
}
int32 irand(int32 min, int32 max) int32 irand(int32 min, int32 max)
{ {
return int32(mtRand->randInt(max - min)) + min; return int32((*mtRand)->randInt(max - min)) + min;
} }
uint32 urand(uint32 min, uint32 max) uint32 urand(uint32 min, uint32 max)
{ {
return mtRand->randInt(max - min) + min; return (*mtRand)->randInt(max - min) + min;
} }
float frand(float min, float max) float frand(float min, float max)
{ {
return mtRand->randExc(max - min) + min; return (*mtRand)->randExc(max - min) + min;
} }
int32 rand32() int32 rand32()
{ {
return mtRand->randInt(); return (*mtRand)->randInt();
} }
double rand_norm(void) double rand_norm(void)
{ {
return mtRand->randExc(); return (*mtRand)->randExc();
} }
float rand_norm_f(void) float rand_norm_f(void)
{ {
return (float)mtRand->randExc(); return (float)(*mtRand)->randExc();
} }
double rand_chance(void) double rand_chance(void)
{ {
return mtRand->randExc(100.0); return (*mtRand)->randExc(100.0);
} }
float rand_chance_f(void) float rand_chance_f(void)
{ {
return (float)mtRand->randExc(100.0); return (float)(*mtRand)->randExc(100.0);
} }
Tokens StrSplit(const std::string& src, const std::string& sep) Tokens StrSplit(const std::string& src, const std::string& sep)
@ -479,26 +489,26 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
{ {
// supported only Cyrillic cases // supported only Cyrillic cases
if (wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5) if (wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5)
return wname; { return wname; }
// Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently) // Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently)
static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430), wchar_t(0x0000) }; static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430), wchar_t(0x0000)};
static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E), wchar_t(0x0000) }; static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E), wchar_t(0x0000)};
static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F), wchar_t(0x0000) }; static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F), wchar_t(0x0000)};
static wchar_t const ie_End[] = { wchar_t(1), wchar_t(0x0435), wchar_t(0x0000) }; static wchar_t const ie_End[] = { wchar_t(1), wchar_t(0x0435), wchar_t(0x0000)};
static wchar_t const i_End[] = { wchar_t(1), wchar_t(0x0438), wchar_t(0x0000) }; static wchar_t const i_End[] = { wchar_t(1), wchar_t(0x0438), wchar_t(0x0000)};
static wchar_t const yeru_End[] = { wchar_t(1), wchar_t(0x044B), wchar_t(0x0000) }; static wchar_t const yeru_End[] = { wchar_t(1), wchar_t(0x044B), wchar_t(0x0000)};
static wchar_t const u_End[] = { wchar_t(1), wchar_t(0x0443), wchar_t(0x0000) }; static wchar_t const u_End[] = { wchar_t(1), wchar_t(0x0443), wchar_t(0x0000)};
static wchar_t const yu_End[] = { wchar_t(1), wchar_t(0x044E), wchar_t(0x0000) }; static wchar_t const yu_End[] = { wchar_t(1), wchar_t(0x044E), wchar_t(0x0000)};
static wchar_t const oj_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x0439), wchar_t(0x0000) }; static wchar_t const oj_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x0439), wchar_t(0x0000)};
static wchar_t const ie_j_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x0439), wchar_t(0x0000) }; static wchar_t const ie_j_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x0439), wchar_t(0x0000)};
static wchar_t const io_j_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x0439), wchar_t(0x0000) }; static wchar_t const io_j_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x0439), wchar_t(0x0000)};
static wchar_t const o_m_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x043C), wchar_t(0x0000) }; static wchar_t const o_m_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x043C), wchar_t(0x0000)};
static wchar_t const io_m_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x043C), wchar_t(0x0000) }; static wchar_t const io_m_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x043C), wchar_t(0x0000)};
static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x043C), wchar_t(0x0000) }; static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x043C), wchar_t(0x0000)};
static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C), wchar_t(0x0000) }; static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C), wchar_t(0x0000)};
static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439), wchar_t(0x0000) }; static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439), wchar_t(0x0000)};
static wchar_t const* const dropEnds[6][8] = static wchar_t const* const dropEnds[6][8] =
{ {
@ -515,7 +525,7 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
size_t len = size_t((*itr)[-1]); // get length from string size field size_t len = size_t((*itr)[-1]); // get length from string size field
if (wname.substr(wname.size() - len, len) == *itr) if (wname.substr(wname.size() - len, len) == *itr)
return wname.substr(0, wname.size() - len); { return wname.substr(0, wname.size() - len); }
} }
return wname; return wname;
@ -569,14 +579,6 @@ bool Utf8FitTo(const std::string& str, std::wstring search)
return true; return true;
} }
void utf8printf(FILE* out, const char* str, ...)
{
va_list ap;
va_start(ap, str);
vutf8printf(out, str, &ap);
va_end(ap);
}
void vutf8printf(FILE* out, const char* str, va_list* ap) void vutf8printf(FILE* out, const char* str, va_list* ap)
{ {
#if PLATFORM == PLATFORM_WINDOWS #if PLATFORM == PLATFORM_WINDOWS
@ -662,3 +664,28 @@ void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= fals
out[j++] = strtoul(buffer, NULL, 16); out[j++] = strtoul(buffer, NULL, 16);
} }
} }
void utf8print(void* /*arg*/, const char* str)
{
#if PLATFORM == PLATFORM_WINDOWS
wchar_t wtemp_buf[6000];
size_t wtemp_len = 6000 - 1;
if (!Utf8toWStr(str, strlen(str), wtemp_buf, wtemp_len))
{ return; }
char temp_buf[6000];
CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len + 1);
printf("%s", temp_buf);
#else
printf("%s", str);
#endif
}
void utf8printf(FILE* out, const char* str, ...)
{
va_list ap;
va_start(ap, str);
vutf8printf(out, str, &ap);
va_end(ap);
}

View file

@ -2,7 +2,7 @@
* MaNGOS is a full featured server for World of Warcraft, supporting * MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8 * the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
* *
* Copyright (C) 2005-2015 MaNGOS project <http://getmangos.eu> * Copyright (C) 2005-2017 MaNGOS project <https://getmangos.eu>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -109,6 +109,20 @@ inline uint32 secsToTimeBitFields(time_t secs)
return (lt->tm_year - 100) << 24 | lt->tm_mon << 20 | (lt->tm_mday - 1) << 14 | lt->tm_wday << 11 | lt->tm_hour << 6 | lt->tm_min; return (lt->tm_year - 100) << 24 | lt->tm_mon << 20 | (lt->tm_mday - 1) << 14 | lt->tm_wday << 11 | lt->tm_hour << 6 | lt->tm_min;
} }
/**
* @brief Initializes the TSS for MersenneTwister
*
*
*/
void initMTRandTSS();
/**
* @brief Cleanups the TSS for MersenneTwister
*
*
*/
void deleteMTRandTSS();
/** /**
* @brief Return a random number in the range min..max; (max-min) must be smaller than 32768. * @brief Return a random number in the range min..max; (max-min) must be smaller than 32768.
* *
@ -508,7 +522,7 @@ inline bool isBasicLatinString(const std::wstring &wstr, bool numericOrSpace)
{ {
for (size_t i = 0; i < wstr.size(); ++i) for (size_t i = 0; i < wstr.size(); ++i)
if (!isBasicLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) if (!isBasicLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
return false; { return false; }
return true; return true;
} }
@ -523,7 +537,7 @@ inline bool isExtendedLatinString(const std::wstring &wstr, bool numericOrSpace)
{ {
for (size_t i = 0; i < wstr.size(); ++i) for (size_t i = 0; i < wstr.size(); ++i)
if (!isExtendedLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) if (!isExtendedLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
return false; { return false; }
return true; return true;
} }
@ -538,7 +552,7 @@ inline bool isCyrillicString(const std::wstring &wstr, bool numericOrSpace)
{ {
for (size_t i = 0; i < wstr.size(); ++i) for (size_t i = 0; i < wstr.size(); ++i)
if (!isCyrillicCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) if (!isCyrillicCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
return false; { return false; }
return true; return true;
} }
@ -553,7 +567,7 @@ inline bool isEastAsianString(const std::wstring &wstr, bool numericOrSpace)
{ {
for (size_t i = 0; i < wstr.size(); ++i) for (size_t i = 0; i < wstr.size(); ++i)
if (!isEastAsianCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) if (!isEastAsianCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
return false; { return false; }
return true; return true;
} }
@ -699,6 +713,12 @@ bool Utf8FitTo(const std::string& str, std::wstring search);
* @param str... * @param str...
*/ */
void utf8printf(FILE* out, const char* str, ...); void utf8printf(FILE* out, const char* str, ...);
/**
* @brief
*
* @param str
*/
void utf8print(void* /*arg*/, const char* str);
/** /**
* @brief * @brief
* *