[9478] Reimplement Aura::IsNeedVisibleSlot
[getmangos.git] / src / mangosd / Master.cpp
blob0395d34fb94ea8d0630913ebe985140ff0cd83e8
1 /*
2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /** \file
20 \ingroup mangosd
23 #include <ace/OS_NS_signal.h>
25 #include "WorldSocketMgr.h"
26 #include "Common.h"
27 #include "Master.h"
28 #include "WorldSocket.h"
29 #include "WorldRunnable.h"
30 #include "World.h"
31 #include "Log.h"
32 #include "Timer.h"
33 #include "Policies/SingletonImp.h"
34 #include "SystemConfig.h"
35 #include "Config/ConfigEnv.h"
36 #include "Database/DatabaseEnv.h"
37 #include "CliRunnable.h"
38 #include "RASocket.h"
39 #include "ScriptCalls.h"
40 #include "Util.h"
41 #include "revision_sql.h"
42 #include "MaNGOSsoap.h"
44 #include "sockets/TcpSocket.h"
45 #include "sockets/Utility.h"
46 #include "sockets/Parse.h"
47 #include "sockets/Socket.h"
48 #include "sockets/SocketHandler.h"
49 #include "sockets/ListenSocket.h"
51 #ifdef WIN32
52 #include "ServiceWin32.h"
53 extern int m_ServiceStatus;
54 #endif
56 INSTANTIATE_SINGLETON_1( Master );
58 volatile uint32 Master::m_masterLoopCounter = 0;
60 class FreezeDetectorRunnable : public ACE_Based::Runnable
62 public:
63 FreezeDetectorRunnable() { _delaytime = 0; }
64 uint32 m_loops, m_lastchange;
65 uint32 w_loops, w_lastchange;
66 uint32 _delaytime;
67 void SetDelayTime(uint32 t) { _delaytime = t; }
68 void run(void)
70 if(!_delaytime)
71 return;
72 sLog.outString("Starting up anti-freeze thread (%u seconds max stuck time)...",_delaytime/1000);
73 m_loops = 0;
74 w_loops = 0;
75 m_lastchange = 0;
76 w_lastchange = 0;
77 while(!World::IsStopped())
79 ACE_Based::Thread::Sleep(1000);
81 uint32 curtime = getMSTime();
82 //DEBUG_LOG("anti-freeze: time=%u, counters=[%u; %u]",curtime,Master::m_masterLoopCounter,World::m_worldLoopCounter);
84 // There is no Master anymore
85 // TODO: clear the rest of the code
86 // // normal work
87 // if(m_loops != Master::m_masterLoopCounter)
88 // {
89 // m_lastchange = curtime;
90 // m_loops = Master::m_masterLoopCounter;
91 // }
92 // // possible freeze
93 // else if(getMSTimeDiff(m_lastchange,curtime) > _delaytime)
94 // {
95 // sLog.outError("Main/Sockets Thread hangs, kicking out server!");
96 // *((uint32 volatile*)NULL) = 0; // bang crash
97 // }
99 // normal work
100 if(w_loops != World::m_worldLoopCounter)
102 w_lastchange = curtime;
103 w_loops = World::m_worldLoopCounter;
105 // possible freeze
106 else if(getMSTimeDiff(w_lastchange,curtime) > _delaytime)
108 sLog.outError("World Thread hangs, kicking out server!");
109 *((uint32 volatile*)NULL) = 0; // bang crash
112 sLog.outString("Anti-freeze thread exiting without problems.");
116 class RARunnable : public ACE_Based::Runnable
118 public:
119 uint32 numLoops, loopCounter;
121 RARunnable ()
123 uint32 socketSelecttime = sWorld.getConfig (CONFIG_UINT32_SOCKET_SELECTTIME);
124 numLoops = (sConfig.GetIntDefault ("MaxPingTime", 30) * (MINUTE * 1000000 / socketSelecttime));
125 loopCounter = 0;
128 void checkping ()
130 // ping if need
131 if ((++loopCounter) == numLoops)
133 loopCounter = 0;
134 sLog.outDetail ("Ping MySQL to keep connection alive");
135 delete WorldDatabase.Query ("SELECT 1 FROM command LIMIT 1");
136 delete loginDatabase.Query ("SELECT 1 FROM realmlist LIMIT 1");
137 delete CharacterDatabase.Query ("SELECT 1 FROM bugreport LIMIT 1");
141 void run ()
143 SocketHandler h;
145 // Launch the RA listener socket
146 ListenSocket<RASocket> RAListenSocket (h);
147 bool usera = sConfig.GetBoolDefault ("Ra.Enable", false);
149 if (usera)
151 port_t raport = sConfig.GetIntDefault ("Ra.Port", 3443);
152 std::string stringip = sConfig.GetStringDefault ("Ra.IP", "0.0.0.0");
153 ipaddr_t raip;
154 if (!Utility::u2ip (stringip, raip))
155 sLog.outError ("MaNGOS RA can not bind to ip %s", stringip.c_str ());
156 else if (RAListenSocket.Bind (raip, raport))
157 sLog.outError ("MaNGOS RA can not bind to port %d on %s", raport, stringip.c_str ());
158 else
160 h.Add (&RAListenSocket);
162 sLog.outString ("Starting Remote access listner on port %d on %s", raport, stringip.c_str ());
166 // Socket Selet time is in microseconds , not miliseconds!!
167 uint32 socketSelecttime = sWorld.getConfig (CONFIG_UINT32_SOCKET_SELECTTIME);
169 // if use ra spend time waiting for io, if not use ra ,just sleep
170 if (usera)
172 while (!World::IsStopped())
174 h.Select (0, socketSelecttime);
175 checkping ();
178 else
180 while (!World::IsStopped())
182 ACE_Based::Thread::Sleep(static_cast<unsigned long> (socketSelecttime / 1000));
183 checkping ();
189 Master::Master()
193 Master::~Master()
197 /// Main function
198 int Master::Run()
200 /// worldd PID file creation
201 std::string pidfile = sConfig.GetStringDefault("PidFile", "");
202 if(!pidfile.empty())
204 uint32 pid = CreatePIDFile(pidfile);
205 if( !pid )
207 sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() );
208 return 1;
211 sLog.outString( "Daemon PID: %u\n", pid );
214 ///- Start the databases
215 if (!_StartDB())
216 return 1;
218 ///- Initialize the World
219 sWorld.SetInitialWorldSettings();
221 ///- Catch termination signals
222 _HookSignals();
224 ///- Launch WorldRunnable thread
225 ACE_Based::Thread world_thread(new WorldRunnable);
226 world_thread.setPriority(ACE_Based::Highest);
228 // set realmbuilds depend on mangosd expected builds, and set server online
230 std::string builds = AcceptableClientBuildsListStr();
231 loginDatabase.escape_string(builds);
232 loginDatabase.PExecute("UPDATE realmlist SET color = 0, population = 0, realmbuilds = '%s' WHERE id = '%d'", builds.c_str(), realmID);
235 ACE_Based::Thread* cliThread = NULL;
237 #ifdef WIN32
238 if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
239 #else
240 if (sConfig.GetBoolDefault("Console.Enable", true))
241 #endif
243 ///- Launch CliRunnable thread
244 cliThread = new ACE_Based::Thread(new CliRunnable);
247 ACE_Based::Thread rar_thread(new RARunnable);
249 ///- Handle affinity for multiple processors and process priority on Windows
250 #ifdef WIN32
252 HANDLE hProcess = GetCurrentProcess();
254 uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
255 if(Aff > 0)
257 ULONG_PTR appAff;
258 ULONG_PTR sysAff;
260 if(GetProcessAffinityMask(hProcess,&appAff,&sysAff))
262 ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
264 if(!curAff )
266 sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for mangosd. Accessible processors bitmask (hex): %x",Aff,appAff);
268 else
270 if(SetProcessAffinityMask(hProcess,curAff))
271 sLog.outString("Using processors (bitmask, hex): %x", curAff);
272 else
273 sLog.outError("Can't set used processors (hex): %x",curAff);
276 sLog.outString();
279 bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
281 // if(Prio && (m_ServiceStatus == -1)/* need set to default process priority class in service mode*/)
282 if(Prio)
284 if(SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS))
285 sLog.outString("mangosd process priority class set to HIGH");
286 else
287 sLog.outError("ERROR: Can't set mangosd process priority class.");
288 sLog.outString();
291 #endif
293 ///- Start soap serving thread
294 ACE_Based::Thread* soap_thread = NULL;
296 if(sConfig.GetBoolDefault("SOAP.Enabled", false))
298 MaNGOSsoapRunnable *runnable = new MaNGOSsoapRunnable();
300 runnable->setListenArguments(sConfig.GetStringDefault("SOAP.IP", "127.0.0.1"), sConfig.GetIntDefault("SOAP.Port", 7878));
301 soap_thread = new ACE_Based::Thread(runnable);
305 uint32 realCurrTime, realPrevTime;
306 realCurrTime = realPrevTime = getMSTime();
308 ///- Start up freeze catcher thread
309 ACE_Based::Thread* freeze_thread = NULL;
310 if(uint32 freeze_delay = sConfig.GetIntDefault("MaxCoreStuckTime", 0))
312 FreezeDetectorRunnable *fdr = new FreezeDetectorRunnable();
313 fdr->SetDelayTime(freeze_delay*1000);
314 freeze_thread = new ACE_Based::Thread(fdr);
315 freeze_thread->setPriority(ACE_Based::Highest);
318 ///- Launch the world listener socket
319 port_t wsport = sWorld.getConfig (CONFIG_UINT32_PORT_WORLD);
320 std::string bind_ip = sConfig.GetStringDefault ("BindIP", "0.0.0.0");
322 if (sWorldSocketMgr->StartNetwork (wsport, bind_ip.c_str ()) == -1)
324 sLog.outError ("Failed to start network");
325 World::StopNow(ERROR_EXIT_CODE);
326 // go down and shutdown the server
329 sWorldSocketMgr->Wait ();
331 ///- Stop freeze protection before shutdown tasks
332 if (freeze_thread)
334 freeze_thread->destroy();
335 delete freeze_thread;
338 ///- Stop soap thread
339 if(soap_thread)
341 soap_thread->wait();
342 soap_thread->destroy();
343 delete soap_thread;
346 ///- Set server offline in realmlist
347 loginDatabase.PExecute("UPDATE realmlist SET color = 2 WHERE id = '%d'",realmID);
349 ///- Remove signal handling before leaving
350 _UnhookSignals();
352 // when the main thread closes the singletons get unloaded
353 // since worldrunnable uses them, it will crash if unloaded after master
354 world_thread.wait();
355 rar_thread.wait ();
357 ///- Clean account database before leaving
358 clearOnlineAccounts();
360 ///- Wait for DB delay threads to end
361 CharacterDatabase.HaltDelayThread();
362 WorldDatabase.HaltDelayThread();
363 loginDatabase.HaltDelayThread();
365 sLog.outString( "Halting process..." );
367 if (cliThread)
369 #ifdef WIN32
371 // this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API)
372 //_exit(1);
373 // send keyboard input to safely unblock the CLI thread
374 INPUT_RECORD b[5];
375 HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
376 b[0].EventType = KEY_EVENT;
377 b[0].Event.KeyEvent.bKeyDown = TRUE;
378 b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
379 b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
380 b[0].Event.KeyEvent.wRepeatCount = 1;
382 b[1].EventType = KEY_EVENT;
383 b[1].Event.KeyEvent.bKeyDown = FALSE;
384 b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
385 b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
386 b[1].Event.KeyEvent.wRepeatCount = 1;
388 b[2].EventType = KEY_EVENT;
389 b[2].Event.KeyEvent.bKeyDown = TRUE;
390 b[2].Event.KeyEvent.dwControlKeyState = 0;
391 b[2].Event.KeyEvent.uChar.AsciiChar = '\r';
392 b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
393 b[2].Event.KeyEvent.wRepeatCount = 1;
394 b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
396 b[3].EventType = KEY_EVENT;
397 b[3].Event.KeyEvent.bKeyDown = FALSE;
398 b[3].Event.KeyEvent.dwControlKeyState = 0;
399 b[3].Event.KeyEvent.uChar.AsciiChar = '\r';
400 b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
401 b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
402 b[3].Event.KeyEvent.wRepeatCount = 1;
403 DWORD numb;
404 BOOL ret = WriteConsoleInput(hStdIn, b, 4, &numb);
406 cliThread->wait();
408 #else
410 cliThread->destroy();
412 #endif
414 delete cliThread;
417 // for some unknown reason, unloading scripts here and not in worldrunnable
418 // fixes a memory leak related to detaching threads from the module
419 UnloadScriptingModule();
421 ///- Exit the process with specified return value
422 return World::GetExitCode();
425 /// Initialize connection to the databases
426 bool Master::_StartDB()
428 ///- Get world database info from configuration file
429 std::string dbstring = sConfig.GetStringDefault("WorldDatabaseInfo", "");
430 if(dbstring.empty())
432 sLog.outError("Database not specified in configuration file");
433 return false;
435 sLog.outString("World Database: %s", dbstring.c_str());
437 ///- Initialise the world database
438 if(!WorldDatabase.Initialize(dbstring.c_str()))
440 sLog.outError("Cannot connect to world database %s",dbstring.c_str());
441 return false;
444 if(!WorldDatabase.CheckRequiredField("db_version",REVISION_DB_MANGOS))
446 ///- Wait for already started DB delay threads to end
447 WorldDatabase.HaltDelayThread();
448 return false;
451 dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo", "");
452 if(dbstring.empty())
454 sLog.outError("Character Database not specified in configuration file");
456 ///- Wait for already started DB delay threads to end
457 WorldDatabase.HaltDelayThread();
458 return false;
460 sLog.outString("Character Database: %s", dbstring.c_str());
462 ///- Initialise the Character database
463 if(!CharacterDatabase.Initialize(dbstring.c_str()))
465 sLog.outError("Cannot connect to Character database %s",dbstring.c_str());
467 ///- Wait for already started DB delay threads to end
468 WorldDatabase.HaltDelayThread();
469 return false;
472 if(!CharacterDatabase.CheckRequiredField("character_db_version",REVISION_DB_CHARACTERS))
474 ///- Wait for already started DB delay threads to end
475 WorldDatabase.HaltDelayThread();
476 CharacterDatabase.HaltDelayThread();
477 return false;
480 ///- Get login database info from configuration file
481 dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
482 if(dbstring.empty())
484 sLog.outError("Login database not specified in configuration file");
486 ///- Wait for already started DB delay threads to end
487 WorldDatabase.HaltDelayThread();
488 CharacterDatabase.HaltDelayThread();
489 return false;
492 ///- Initialise the login database
493 sLog.outString("Login Database: %s", dbstring.c_str() );
494 if(!loginDatabase.Initialize(dbstring.c_str()))
496 sLog.outError("Cannot connect to login database %s",dbstring.c_str());
498 ///- Wait for already started DB delay threads to end
499 WorldDatabase.HaltDelayThread();
500 CharacterDatabase.HaltDelayThread();
501 return false;
504 if(!loginDatabase.CheckRequiredField("realmd_db_version",REVISION_DB_REALMD))
506 ///- Wait for already started DB delay threads to end
507 WorldDatabase.HaltDelayThread();
508 CharacterDatabase.HaltDelayThread();
509 loginDatabase.HaltDelayThread();
510 return false;
513 ///- Get the realm Id from the configuration file
514 realmID = sConfig.GetIntDefault("RealmID", 0);
515 if(!realmID)
517 sLog.outError("Realm ID not defined in configuration file");
519 ///- Wait for already started DB delay threads to end
520 WorldDatabase.HaltDelayThread();
521 CharacterDatabase.HaltDelayThread();
522 loginDatabase.HaltDelayThread();
523 return false;
526 sLog.outString("Realm running as realm ID %d", realmID);
528 ///- Clean the database before starting
529 clearOnlineAccounts();
531 sWorld.LoadDBVersion();
533 sLog.outString("Using World DB: %s", sWorld.GetDBVersion());
534 sLog.outString("Using creature EventAI: %s", sWorld.GetCreatureEventAIVersion());
535 return true;
538 /// Clear 'online' status for all accounts with characters in this realm
539 void Master::clearOnlineAccounts()
541 // Cleanup online status for characters hosted at current realm
542 /// \todo Only accounts with characters logged on *this* realm should have online status reset. Move the online column from 'account' to 'realmcharacters'?
543 loginDatabase.PExecute("UPDATE account SET active_realm_id = 0 WHERE active_realm_id = '%d'", realmID);
545 CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0");
547 // Battleground instance ids reset at server restart
548 CharacterDatabase.Execute("UPDATE character_battleground_data SET instance_id = 0");
551 /// Handle termination signals
552 void Master::_OnSignal(int s)
554 switch (s)
556 case SIGINT:
557 World::StopNow(RESTART_EXIT_CODE);
558 break;
559 case SIGTERM:
560 #ifdef _WIN32
561 case SIGBREAK:
562 #endif
563 World::StopNow(SHUTDOWN_EXIT_CODE);
564 break;
567 signal(s, _OnSignal);
570 /// Define hook '_OnSignal' for all termination signals
571 void Master::_HookSignals()
573 signal(SIGINT, _OnSignal);
574 signal(SIGTERM, _OnSignal);
575 #ifdef _WIN32
576 signal(SIGBREAK, _OnSignal);
577 #endif
580 /// Unhook the signals before leaving
581 void Master::_UnhookSignals()
583 signal(SIGINT, 0);
584 signal(SIGTERM, 0);
585 #ifdef _WIN32
586 signal(SIGBREAK, 0);
587 #endif