OregonCore  revision 3611e8a-git
Your Favourite TBC server
Master.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <ace/OS_NS_signal.h>
19 
20 #include "WorldSocketMgr.h"
21 #include "Common.h"
22 #include "Master.h"
23 #include "WorldSocket.h"
24 #include "World.h"
25 #include "Log.h"
26 #include "Timer.h"
27 #include "SystemConfig.h"
28 #include "Config/Config.h"
29 #include "Database/DatabaseEnv.h"
30 #include "DBCStores.h"
31 #include "RARunnable.h"
32 #include "Util.h"
33 #include "OCSoap.h"
34 #include "Console.h"
35 #include "ObjectAccessor.h"
36 #include "MapManager.h"
37 #include "BattlegroundMgr.h"
38 #include "CreatureGroups.h"
39 #include "Database/DatabaseEnv.h"
40 
41 #ifdef _WIN32
42 #include "ServiceWin32.h"
43 extern int m_ServiceStatus;
44 #endif
45 
46 
47 #define WORLD_SLEEP_CONST 5
48 
50 
52 
54 {
55  public:
57  {
58  _delaytime = 0;
59  }
64  {
65  _delaytime = t;
66  }
67  void run(void)
68  {
69  if (!_delaytime)
70  return;
71  sLog.outString("Starting up anti-freeze thread (%u seconds max stuck time)...", _delaytime / 1000);
72  m_loops = 0;
73  w_loops = 0;
74  m_lastchange = 0;
75  w_lastchange = 0;
76  for (;;)
77  {
79  if (sWorld.IsStopped())
80  break;
81  uint32 curtime = getMSTime();
82  // normal work
83  if (w_loops != World::m_worldLoopCounter)
84  {
85  w_lastchange = curtime;
86  w_loops = World::m_worldLoopCounter;
87  }
88  // possible freeze
89  else if (getMSTimeDiff(w_lastchange, curtime) > _delaytime)
90  {
91  sLog.outError("World Thread is stuck. Terminating server!");
92  abort();
93  }
94  }
95  sLog.outString("Anti-freeze thread exiting without problems.");
96  }
97 };
98 
100 {
101 }
102 
104 {
105 }
106 
107 // Main function
108 int Master::Run(bool runTests)
109 {
110  int defaultStderr = dup(2);
111 
112  if (sConfig.GetBoolDefault("Console.Enable", true))
113  sConsole.Initialize();
114  sConsole.SetLoading(true);
115  sConsole.DrawLogo();
116 
117  // worldd PID file creation
118  std::string pidfile = sConfig.GetStringDefault("PidFile", "");
119  if (!pidfile.empty())
120  {
121  uint32 pid = CreatePIDFile(pidfile);
122  if (!pid)
123  {
124  sLog.outError("Cannot create PID file %s.\n", pidfile.c_str());
125  return 1;
126  }
127 
128  sLog.outString("Daemon PID: %u\n", pid);
129  }
130 
131  // Start the databases
132  _StartDB();
133 
134  // Initialize the World
135  sWorld.SetInitialWorldSettings();
136 
137  // set realmbuilds depend on OregonCore expected builds, and set server online
138  std::string builds = AcceptableClientBuildsListStr();
140  LoginDatabase.PExecute("UPDATE realmlist SET realmflags = realmflags & ~(%u), population = 0, realmbuilds = '%s' WHERE id = '%d'", REALM_FLAG_OFFLINE, builds.c_str(), realmID);
141 
142  sConsole.SetLoading(false);
143 
144  // Catch termination signals
145  _HookSignals();
146 
147  ACE_Based::Thread* cliThread = NULL;
148 
149  #ifdef _WIN32
150  if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
151  #else
152  if (sConfig.GetBoolDefault("Console.Enable", true))
153  #endif
154  {
155  // Launch CliRunnable thread
156  cliThread = new ACE_Based::Thread(new Console::CliRunnable);
157  }
158 
159  ACE_Based::Thread rar_thread(new RARunnable);
160 
161  // Handle affinity for multiple processors and process priority on Windows
162  #ifdef _WIN32
163  {
164  HANDLE hProcess = GetCurrentProcess();
165 
166  uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
167  if (Aff > 0)
168  {
169  ULONG_PTR appAff;
170  ULONG_PTR sysAff;
171 
172  if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
173  {
174  ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
175 
176  if (!curAff)
177  sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for OregonCore. Accessible processors bitmask (hex): %x", Aff, appAff);
178  else
179  {
180  if (SetProcessAffinityMask(hProcess, curAff))
181  sLog.outString("Using processors (bitmask, hex): %x", curAff);
182  else
183  sLog.outError("Can't set used processors (hex): %x", curAff);
184  }
185  }
186  sLog.outString();
187  }
188 
189  bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
190 
191  if (Prio)
192  {
193  if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
194  sLog.outString("OregonCore process priority class set to HIGH");
195  else
196  sLog.outError("ERROR: Can't set OregonCore process priority class.");
197  sLog.outString();
198  }
199  }
200  #endif
201 
202  // Start soap serving thread
203  ACE_Based::Thread* soap_thread = NULL;
204 
205  if (sConfig.GetBoolDefault("SOAP.Enabled", false))
206  {
207  OCSoapRunnable* runnable = new OCSoapRunnable();
208 
209  runnable->setListenArguments(sConfig.GetStringDefault("SOAP.IP", "127.0.0.1"), sConfig.GetIntDefault("SOAP.Port", 7878));
210  soap_thread = new ACE_Based::Thread(runnable);
211  }
212 
213  //uint32 socketSelecttime = sWorld.getConfig(CONFIG_SOCKET_SELECTTIME);
214 
215  // Start up freeze catcher thread
216  ACE_Based::Thread* freeze_thread = NULL;
217  if (uint32 freeze_delay = sConfig.GetIntDefault("MaxCoreStuckTime", 0))
218  {
220  fdr->SetDelayTime(freeze_delay * 1000);
221  freeze_thread = new ACE_Based::Thread(fdr);
222  freeze_thread->setPriority(ACE_Based::Highest);
223  }
224 
225  // Launch the world listener socket
226  uint16 wsport = sWorld.getConfig(CONFIG_PORT_WORLD);
227  std::string bind_ip = sConfig.GetStringDefault ("BindIP", "0.0.0.0");
228 
229  if (sWorldSocketMgr->StartNetwork (wsport, bind_ip.c_str ()) == -1)
230  {
231  sLog.outError("Failed to start network");
233  // go down and shutdown the server
234  // give other threads a chance to start-up so we can shutdown them safely
236  }
237 
238  // ----------------------------------------------------------------------------------------------------------------
239  //
240 
241  // Init new SQL thread for the world database
242  WorldDatabase.ThreadStart(); // let thread do safe mySQL requests (one connection call enough)
243  sWorld.InitResultQueue();
244 
245  // Run regression tests, then gracefully exit with particular exit code
246  if (runTests)
247  {
248  if (RunRegressionTests())
250  else
252  }
253 
254  // Run our World, we use main thread for this,
255  MainLoop();
256 
257  ObjectAccessor::Instance().SaveAllPlayers(); // save all players
258  sWorld.KickAll(); // kick all players
259  sWorld.UpdateSessions( 1 ); // real players unload required UpdateSessions call
260 
261  // unload battleground templates before different singletons destroyed
262  sBattlegroundMgr.DeleteAlllBattlegrounds();
263 
264  sWorldSocketMgr->StopNetwork();
265 
266  MapManager::Instance().UnloadAll(); // unload all grids (including locked in memory)
267 
268  // End the database thread
269  WorldDatabase.ThreadEnd(); // free mySQL thread resources
270 
271  //
272  // ----------------------------------------------------------------------------------------------------------------
273 
274  // Stop freeze protection before shutdown tasks
275  if (freeze_thread)
276  {
277  freeze_thread->kill(-1); // destroy
278  freeze_thread->wait();
279  delete freeze_thread;
280  }
281 
282  sWorldSocketMgr->Wait();
283 
284  // Stop soap thread
285  if (soap_thread)
286  {
287  soap_thread->wait();
288  delete soap_thread;
289  }
290 
291  // Set server offline in realmlist
292  LoginDatabase.PExecute("UPDATE realmlist SET realmflags = realmflags | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realmID);
293 
294  // when the main thread closes the singletons get unloaded
295  // since MainLoop uses them, it will crash if unloaded after master
296  rar_thread.wait ();
297 
298  // Clean account database before leaving
299  clearOnlineAccounts();
300 
301  // Wait for delay threads to end
305 
306  sLog.outString("Halting process...");
307 
308  if (cliThread)
309  {
310  cliThread->kill(SIGINT);
311  cliThread->wait();
312  delete cliThread;
313  }
314 
315  // we've been messing up with stderr (if Console.Enable was set),
316  // so we need to restore it back, to prevent SIGPIPEs after restart
317  dup2(defaultStderr, 2);
318  close(defaultStderr);
319 
320  // Remove signal handling before leaving
321  _UnhookSignals();
322 
323  // for some unknown reason, unloading scripts here and not in MainLoop
324  // fixes a memory leak related to detaching threads from the module
325  //UnloadScriptingModule();
326 
327  // Exit the process with specified return value
328  return World::GetExitCode();
329 }
330 
331 // Initialize connection to the databases
333 {
334  sConsole.SetLoadingLabel("Connecting to databases...");
335 
336  // Get world database info from configuration file
337  std::string dbstring = sConfig.GetStringDefault("WorldDatabaseInfo", "");
338  if (dbstring.empty())
339  sLog.outFatal("World database not specified in configuration file");
340 
341  // Initialise the world database
342  if (!WorldDatabase.Initialize(dbstring.c_str()))
343  sLog.outFatal("Cannot connect to world database %s", dbstring.c_str());
344 
345  // Get character database info from configuration file
346  dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo", "");
347  if (dbstring.empty())
348  sLog.outFatal("Character database not specified in configuration file");
349 
350  // Initialise the Character database
351  if (!CharacterDatabase.Initialize(dbstring.c_str()))
352  sLog.outFatal("Cannot connect to Character database %s", dbstring.c_str());
353 
354  // Get login database info from configuration file
355  dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
356  if (dbstring.empty())
357  sLog.outFatal("Login database not specified in configuration file");
358 
359  // Initialise the login database
360  if (!LoginDatabase.Initialize(dbstring.c_str()))
361  sLog.outFatal("Cannot connect to login database %s", dbstring.c_str());
362 
363  // Get the realm Id from the configuration file
364  realmID = sConfig.GetIntDefault("RealmID", 0);
365  if (!realmID)
366  sLog.outFatal("Realm ID not defined in configuration file");
367 
368  sLog.outString("Realm running as realm ID %d", realmID);
369 
370  // Clean the database before starting
371  clearOnlineAccounts();
372 
373  // Insert version info into DB
374  WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", _FULLVERSION, _REVISION);
375 
376  sWorld.LoadDBVersion();
377 
378  sLog.outString("Using World DB: %s", sWorld.GetDBVersion());
379 }
380 
381 // Clear 'online' status for all accounts with characters in this realm
383 {
384  // Cleanup online status for characters hosted at current realm
385  LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE online<>0");
386  CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0");
387 }
388 
389 // Handle termination signals
390 void Master::_OnSignal(int s)
391 {
392  switch (s)
393  {
394  case SIGINT:
396  break;
397  case SIGTERM:
398  #ifdef _WIN32
399  case SIGBREAK:
400  #endif
402  break;
403  }
404 
405  signal(s, _OnSignal);
406 }
407 
408 // Define hook '_OnSignal' for all termination signals
410 {
411  signal(SIGINT, _OnSignal);
412  signal(SIGTERM, _OnSignal);
413  #ifdef _WIN32
414  signal(SIGBREAK, _OnSignal);
415  #endif
416 }
417 
418 // Unhook the signals before leaving
420 {
421  signal(SIGINT, 0);
422  signal(SIGTERM, 0);
423  #ifdef _WIN32
424  signal(SIGBREAK, 0);
425  #endif
426 }
427 
429 {
430  RegressionTestSuite suite;
431  return suite.RunAll();
432 }
433 
434 // Heartbeat for the World
436 {
437  uint32 realCurrTime = 0;
438  uint32 realPrevTime = getMSTime();
439 
440  uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST
441 
442  // While we have not World::m_stopEvent, update the world
443  while (!World::IsStopped())
444  {
446  realCurrTime = getMSTime();
447 
448  uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime);
449 
450  sWorld.Update(diff);
451  realPrevTime = realCurrTime;
452 
453  // diff (D0) include time of previous sleep (d0) + tick time (t0)
454  // we want that next d1 + t1 == WORLD_SLEEP_CONST
455  // we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement
456  // d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0
457  if (diff <= WORLD_SLEEP_CONST + prevSleepTime)
458  {
459  prevSleepTime = WORLD_SLEEP_CONST + prevSleepTime - diff;
460  ACE_Based::Thread::Sleep(prevSleepTime);
461  }
462  else
463  prevSleepTime = 0;
464  }
465 }
#define dup2
Definition: Common.h:136
#define sConfig
Definition: Config.h:52
#define sWorldSocketMgr
static volatile uint32 m_masterLoopCounter
Definition: Master.h:32
std::string AcceptableClientBuildsListStr()
Definition: DBCStores.cpp:168
DatabaseType WorldDatabase
Accessor to the world database.
Definition: Main.cpp:53
static void _OnSignal(int s)
Definition: Master.cpp:390
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
Definition: Timer.h:78
void ThreadEnd()
Definition: Database.cpp:196
#define sConsole
Definition: Console.h:99
bool Execute(const char *sql)
Definition: Database.cpp:420
uint32 getMSTime()
Definition: Timer.h:32
Definition: Master.h:26
#define sLog
Log class singleton.
Definition: Log.h:187
#define WORLD_SLEEP_CONST
Definition: Master.cpp:47
static bool IsStopped()
Definition: World.h:638
unsigned long escape_string(char *to, const char *from, unsigned long length)
Definition: Database.cpp:212
void clearOnlineAccounts()
Definition: Master.cpp:382
bool kill(int signal)
Definition: Threading.cpp:157
void SetDelayTime(uint32 t)
Definition: Master.cpp:63
static void Sleep(unsigned long msecs)
Definition: Threading.cpp:237
static uint8 GetExitCode()
Definition: World.h:628
DatabaseType LoginDatabase
Accessor to the realm/login database.
Definition: Main.cpp:55
void MainLoop()
Definition: Master.cpp:435
bool Initialize(const char *infoString)
Definition: Database.cpp:75
void setPriority(Priority type)
Definition: Threading.cpp:229
void ThreadStart()
Definition: Database.cpp:191
bool PExecute(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:441
void UnloadAll()
Definition: MapManager.cpp:272
#define _FULLVERSION
Definition: SystemConfig.h:40
void HaltDelayThread()
Definition: Database.cpp:645
INSTANTIATE_SINGLETON_1(Master)
void _UnhookSignals()
Definition: Master.cpp:419
uint32 realmID
Id of the realm.
Definition: Main.cpp:57
~Master()
Definition: Master.cpp:103
#define sBattlegroundMgr
void setListenArguments(std::string host, uint16 port)
Definition: OCSoap.h:38
uint32 CreatePIDFile(const std::string &filename)
Definition: Util.cpp:242
void _HookSignals()
Definition: Master.cpp:409
static volatile uint32 m_worldLoopCounter
Definition: World.h:460
void _StartDB()
Definition: Master.cpp:332
#define sWorld
Definition: World.h:860
bool RunRegressionTests()
Definition: Master.cpp:428
DatabaseType CharacterDatabase
Accessor to the character database.
Definition: Main.cpp:54
ACE_UINT16 uint16
Definition: Define.h:72
Master()
Definition: Master.cpp:99
ACE_UINT32 uint32
Definition: Define.h:71
static void StopNow(uint8 exitcode)
Definition: World.h:632
int Run(bool runTests)
Definition: Master.cpp:108