Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
Master.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
9
10#ifdef _WIN32
11#include <winsock2.h>
12#endif
13#include <mysql.h>
14#include <csignal>
15#include <filesystem>
16#include <memory>
17
18#include "Common.h"
24#include "SystemConfig.h"
25#include "World.h"
26#include "WorldRunnable.h"
27#include "WorldSocket.h"
28#include "WorldSocketMgr.h"
29
30#include "AuthSocket.h"
31#include "CliRunnable.h"
32#include "Log.h"
33#include "Master.h"
34#include "Platform/TimeUtils.h"
35#include "RARunnable.h"
36#include "RealmList.h"
37#include "SFSoap.h"
38#include "Timer.h"
40#include "Util.h"
41
42#include "BigNumber.h"
43
44#ifdef _WIN32
45#include "ServiceWin32.h"
46extern int m_ServiceStatus;
47#endif
48
49#ifdef __linux__
50#include <sched.h>
51#include <sys/resource.h>
52#define PROCESS_HIGH_PRIORITY -15 // [-20, 19], default is 0
53#endif
54
55namespace
56{
57 Skyfire::Database::SetupOptions LoadCharacterDatabaseSetupOptions()
58 {
60 sConfigMgr->GetBoolDefault("CharacterDatabase.AutoSetup", false),
61 sConfigMgr->GetBoolDefault("CharacterDatabase.AutoCreate", false),
62 sConfigMgr->GetBoolDefault("CharacterDatabase.AutoBaseline", false),
63 sConfigMgr->GetStringDefault("CharacterDatabase.SqlPath", ""));
64
66 sConfigMgr->GetBoolDefault("CharacterDatabase.AllowUpdateHashMismatch", true);
67 options.ImportPendingUpdates =
68 sConfigMgr->GetBoolDefault("CharacterDatabase.ImportPendingUpdates", false);
69
70 return options;
71 }
72
73 Skyfire::Database::SetupOptions LoadWorldDatabaseSetupOptions()
74 {
76 sConfigMgr->GetBoolDefault("WorldDatabase.AutoSetup", false),
77 sConfigMgr->GetBoolDefault("WorldDatabase.AutoCreate", false),
78 sConfigMgr->GetBoolDefault("WorldDatabase.AutoBaseline", false),
79 sConfigMgr->GetStringDefault("WorldDatabase.SqlPath", ""),
80 sConfigMgr->GetStringDefault("WorldDatabase.BaseSqlFile", ""));
81
83 sConfigMgr->GetBoolDefault("WorldDatabase.AllowUpdateHashMismatch", true);
84 options.ImportPendingUpdates =
85 sConfigMgr->GetBoolDefault("WorldDatabase.ImportPendingUpdates", false);
86
87 return options;
88 }
89
90 Skyfire::Database::SetupRuntimeContext GetCharacterSetupRuntimeContext()
91 {
92 return
93 {
94 "server.worldserver",
95 "CharacterDatabase",
96 "character",
97 "a character",
98 "Character",
99 "Failed while executing character setup SQL"
100 };
101 }
102
103 Skyfire::Database::SetupRuntimeContext GetWorldSetupRuntimeContext()
104 {
105 return
106 {
107 "server.worldserver",
108 "WorldDatabase",
109 "world",
110 "a world",
111 "World",
112 "Failed while executing world setup SQL"
113 };
114 }
115
116 bool RunCharacterDatabaseSetup(MySQLConnectionInfo const& connectionInfo,
117 Skyfire::Database::SetupOptions const& options)
118 {
119 if (options.SqlPath.empty())
120 {
121 if (options.AutoSetup)
122 {
123 SF_LOG_ERROR("server.worldserver", "CharacterDatabase.AutoSetup requires CharacterDatabase.SqlPath.");
124 return false;
125 }
126
127 SF_LOG_INFO("server.worldserver",
128 "CharacterDatabase.SqlPath is not configured; character database update check skipped.");
129 return true;
130 }
131
132 if (connectionInfo._database.empty())
133 {
134 SF_LOG_ERROR("server.worldserver",
135 "CharacterDatabase setup and update checks require a character database name.");
136 return false;
137 }
138
139 Skyfire::Database::SetupRuntimeContext context = GetCharacterSetupRuntimeContext();
140 if (!Skyfire::Database::EnsureDatabaseExists(connectionInfo, options, context))
141 return false;
142
143 MYSQL* setupConnectionRaw = NULL;
144 if (!Skyfire::Database::ConnectToMySQLServer(connectionInfo, connectionInfo._database.c_str(),
145 setupConnectionRaw, context))
146 return false;
147
148 std::unique_ptr<MYSQL, decltype(&mysql_close)> setupConnection(setupConnectionRaw, mysql_close);
149
150 std::filesystem::path baseSqlPath = Skyfire::Database::GetDatabaseBaseSqlPath(options);
151 bool baseSqlExists = std::filesystem::exists(baseSqlPath);
152 std::vector<Skyfire::Database::SqlUpdateFile> updates = Skyfire::Database::DiscoverSqlUpdates(options);
153
155 if (!Skyfire::Database::LoadDatabaseSetupState(setupConnection.get(), options, state, context))
156 return false;
157
159 Skyfire::Database::BuildCharacterDatabaseSetupPlan(options, state, baseSqlExists, updates);
160 if (!plan.IsValid())
161 {
162 SF_LOG_ERROR("server.worldserver", "%s", plan.Error.c_str());
163 return false;
164 }
165 Skyfire::Database::LogSetupPlan(plan, updates.size(), false, context);
166
167 if (plan.ShouldInstallBase)
168 {
169 std::string baseSql;
170 SF_LOG_INFO("server.worldserver", "Installing character database base SQL from %s.",
171 baseSqlPath.string().c_str());
172 if (!Skyfire::Database::ExecuteSqlFile(setupConnection.get(), baseSqlPath, baseSql, context))
173 return false;
174 }
175
176 if (!Skyfire::Database::EnsureSetupTrackingTables(setupConnection.get(), context))
177 return false;
178
179 if (!Skyfire::Database::BaselineSetupUpdates(setupConnection.get(), options, plan, context))
180 return false;
181
182 if (!Skyfire::Database::ApplyPendingSetupUpdates(setupConnection.get(), options, plan, context))
183 return false;
184
185 SF_LOG_INFO("server.worldserver",
186 "Character database setup/update complete. Base installed: %s, updates applied: %u, updates baselined: %u.",
187 plan.ShouldInstallBase ? "yes" : "no", uint32(plan.PendingUpdates.size()),
188 uint32(plan.BaselineUpdates.size()));
189 return true;
190 }
191
192 bool RunWorldDatabaseSetup(MySQLConnectionInfo const& connectionInfo,
193 Skyfire::Database::SetupOptions const& options)
194 {
195 if (options.SqlPath.empty())
196 {
197 if (options.AutoSetup)
198 {
199 SF_LOG_ERROR("server.worldserver", "WorldDatabase.AutoSetup requires WorldDatabase.SqlPath.");
200 return false;
201 }
202
203 SF_LOG_INFO("server.worldserver",
204 "WorldDatabase.SqlPath is not configured; world database update check skipped.");
205 return true;
206 }
207
208 if (connectionInfo._database.empty())
209 {
210 SF_LOG_ERROR("server.worldserver", "WorldDatabase setup and update checks require a world database name.");
211 return false;
212 }
213
214 Skyfire::Database::SetupRuntimeContext context = GetWorldSetupRuntimeContext();
215 if (!Skyfire::Database::EnsureDatabaseExists(connectionInfo, options, context))
216 return false;
217
218 MYSQL* setupConnectionRaw = NULL;
219 if (!Skyfire::Database::ConnectToMySQLServer(connectionInfo, connectionInfo._database.c_str(),
220 setupConnectionRaw, context))
221 return false;
222
223 std::unique_ptr<MYSQL, decltype(&mysql_close)> setupConnection(setupConnectionRaw, mysql_close);
224
225 std::filesystem::path externalBaseSqlPath = options.ExternalBaseFile;
226 externalBaseSqlPath.make_preferred();
227 bool externalBaseSqlExists = !options.ExternalBaseFile.empty() && std::filesystem::exists(externalBaseSqlPath);
228
229 bool requiredBaseSqlExists = true;
230 std::vector<std::filesystem::path> requiredBaseSqlPaths;
231 for (std::string const& baseFileName : options.RequiredBaseFileNames)
232 {
233 std::filesystem::path requiredBaseSqlPath = Skyfire::Database::GetDatabaseBaseSqlPath(options, baseFileName);
234 requiredBaseSqlPaths.push_back(requiredBaseSqlPath);
235 requiredBaseSqlExists = requiredBaseSqlExists && std::filesystem::exists(requiredBaseSqlPath);
236 }
237
238 std::vector<Skyfire::Database::SqlUpdateFile> updates = Skyfire::Database::DiscoverSqlUpdates(options);
239
241 if (!Skyfire::Database::LoadDatabaseSetupState(setupConnection.get(), options, state, context))
242 return false;
243
245 externalBaseSqlExists, requiredBaseSqlExists, updates);
246 if (!plan.IsValid())
247 {
248 SF_LOG_ERROR("server.worldserver", "%s", plan.Error.c_str());
249 return false;
250 }
251 bool applyRequiredSql = plan.ShouldInstallBase && !requiredBaseSqlPaths.empty();
252 Skyfire::Database::LogSetupPlan(plan, updates.size(), applyRequiredSql, context);
253
254 if (plan.ShouldInstallBase)
255 {
256 std::string baseSql;
257 SF_LOG_INFO("server.worldserver", "Installing world database base SQL from %s.",
258 externalBaseSqlPath.string().c_str());
259 if (!Skyfire::Database::ExecuteSqlFile(setupConnection.get(), externalBaseSqlPath, baseSql, context))
260 return false;
261 }
262
263 if (applyRequiredSql)
264 {
265 for (std::filesystem::path const& requiredBaseSqlPath : requiredBaseSqlPaths)
266 {
267 std::string requiredBaseSql;
268 SF_LOG_INFO("server.worldserver", "Applying world database required SQL from %s.",
269 requiredBaseSqlPath.string().c_str());
270 if (!Skyfire::Database::ExecuteSqlFile(setupConnection.get(), requiredBaseSqlPath, requiredBaseSql,
271 context))
272 return false;
273 }
274 }
275
276 if (!Skyfire::Database::EnsureSetupTrackingTables(setupConnection.get(), context))
277 return false;
278
279 if (!Skyfire::Database::BaselineSetupUpdates(setupConnection.get(), options, plan, context))
280 return false;
281
282 if (!Skyfire::Database::ApplyPendingSetupUpdates(setupConnection.get(), options, plan, context))
283 return false;
284
285 SF_LOG_INFO("server.worldserver",
286 "World database setup/update complete. Base installed: %s, updates applied: %u, updates baselined: %u.",
287 plan.ShouldInstallBase ? "yes" : "no", uint32(plan.PendingUpdates.size()),
288 uint32(plan.BaselineUpdates.size()));
289 return true;
290 }
291}
292
294{
295 switch (sigNum)
296 {
297 case SIGINT:
299 break;
300 case SIGTERM:
301#ifdef _WIN32
302 case SIGBREAK:
303 if (m_ServiceStatus != 1)
304#endif
306 break;
307 }
308}
309
311{
312private:
316public:
318
320
321 void Run()
322 {
323 if (!_delaytime)
324 return;
325
326 SF_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", _delaytime / 1000);
327 _loops = 0;
328 _lastChange = 0;
329 while (!World::IsStopped())
330 {
332 uint32 curtime = getMSTime();
333 // normal work
334 uint32 worldLoopCounter = World::m_worldLoopCounter;
335 if (_loops != worldLoopCounter)
336 {
337 _lastChange = curtime;
338 _loops = worldLoopCounter;
339 }
340 // possible freeze
341 else if (getMSTimeDiff(_lastChange, curtime) > _delaytime)
342 {
343 SF_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
344 ASSERT(false);
345 }
346 }
347 SF_LOG_INFO("server.worldserver", "Anti-freeze thread exiting without problems.");
348 }
349};
350
353{
354 BigNumber seed1;
355 seed1.SetRand(16 * 8);
356 SF_LOG_INFO("server.worldserver", "worldserver-daemon. revision: % s", SKYFIRE_VER_PRODUCTVERSION_STR);
357 SF_LOG_INFO("server.worldserver", "<Ctrl-C> to stop.\n");
358
359 SF_LOG_INFO("server.worldserver", " ______ __ __ __ __ ______ __ ______ ______ ");
360 SF_LOG_INFO("server.worldserver", " /\\ ___\\/\\ \\/ / /\\ \\_\\ \\/\\ ___/\\ \\/\\ == \\/\\ ___\\ ");
361 SF_LOG_INFO("server.worldserver", " \\ \\___ \\ \\ _'-\\ \\____ \\ \\ __\\ \\ \\ \\ __<\\ \\ __\\ ");
362 SF_LOG_INFO("server.worldserver", " \\/\\_____\\ \\_\\ \\_\\/\\_____\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_____\\ ");
363 SF_LOG_INFO("server.worldserver", " \\/_____/\\/_/\\/_/\\/_____/\\/_/ \\/_/\\/_/ /_/\\/_____/ ");
364 SF_LOG_INFO("server.worldserver", " %s Open-sourced Game Emulation", SKYFIRE_VER_LEGALCOPYRIGHT_STR);
365 SF_LOG_INFO("server.worldserver", " <http://www.projectskyfire.org/> \n");
366
368 uint32 confVersion = sConfigMgr->GetIntDefault("ConfVersion", 0);
369 if (confVersion < SKYFIREWORLD_CONFIG_VERSION)
370 {
371 SF_LOG_INFO("server.worldserver", "*****************************************************************************");
372 SF_LOG_INFO("server.worldserver", " WARNING: Your worldserver.conf version indicates your conf file is out of date!");
373 SF_LOG_INFO("server.worldserver", " Please check for updates, as your current default values may cause");
374 SF_LOG_INFO("server.worldserver", " strange behavior.");
375 SF_LOG_INFO("server.worldserver", "*****************************************************************************");
376 }
377
379 std::string pidFile = sConfigMgr->GetStringDefault("PidFile", "");
380 if (!pidFile.empty())
381 {
382 if (uint32 pid = CreatePIDFile(pidFile))
383 SF_LOG_INFO("server.worldserver", "Daemon PID: %u\n", pid);
384 else
385 {
386 SF_LOG_ERROR("server.worldserver", "Cannot create PID file %s.\n", pidFile.c_str());
387 return 1;
388 }
389 }
390
392 if (!_StartDB())
393 return 1;
394
395 // set server offline (not connectable)
396 for (std::map<uint32, std::string>::const_iterator itr = realmNameStore.begin(); itr != realmNameStore.end(); ++itr)
397 {
398 LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_INVALID, itr->first);
399 }
400
402 sWorld->SetInitialWorldSettings();
403
405 std::signal(SIGINT, WorldServerSignalHandler);
406 std::signal(SIGTERM, WorldServerSignalHandler);
407#ifdef _WIN32
408 std::signal(SIGBREAK, WorldServerSignalHandler);
409#endif
410
413 if (worldRunner.Start([] { WorldRunnable().Run(); }) == -1)
414 {
415 SF_LOG_ERROR("server.worldserver", "Failed to start world task");
416 _StopDB();
417 return 1;
418 }
419
421 if (raRunner.Start([] { RARunnable().Run(); }) == -1)
422 {
423 SF_LOG_ERROR("server.worldserver", "Failed to start RA task");
425 worldRunner.Join();
426 _StopDB();
427 return 1;
428 }
429
430 std::unique_ptr<Skyfire::Asio::IoContextTaskRunner> cliRunner;
431
432#ifdef _WIN32
433 if (sConfigMgr->GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
434#else
435 if (sConfigMgr->GetBoolDefault("Console.Enable", true))
436#endif
437 {
439 cliRunner.reset(new Skyfire::Asio::IoContextTaskRunner);
440 if (cliRunner->Start([] { CliRunnable().Run(); }) == -1)
441 {
442 SF_LOG_ERROR("server.worldserver", "Failed to start CLI task");
443 cliRunner.reset();
444 }
445 }
446
447#if defined(_WIN32) || defined(__linux__)
449 uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0);
450 bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false);
451
452#ifdef _WIN32 // Windows
453
454 HANDLE hProcess = GetCurrentProcess();
455
456 if (affinity > 0)
457 {
458 ULONG_PTR appAff;
459 ULONG_PTR sysAff;
460
461 if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
462 {
463 ULONG_PTR currentAffinity = affinity & appAff; // remove non accessible processors
464
465 if (!currentAffinity)
466 SF_LOG_ERROR("server.worldserver", "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the worldserver. Accessible processors bitmask (hex): %x", affinity, appAff);
467 else if (SetProcessAffinityMask(hProcess, currentAffinity))
468 SF_LOG_INFO("server.worldserver", "Using processors (bitmask, hex): %x", currentAffinity);
469 else
470 SF_LOG_ERROR("server.worldserver", "Can't set used processors (hex): %x", currentAffinity);
471 }
472 }
473
474 if (highPriority)
475 {
476 if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
477 SF_LOG_INFO("server.worldserver", "worldserver process priority class set to HIGH");
478 else
479 SF_LOG_ERROR("server.worldserver", "Can't set worldserver process priority class.");
480 }
481#else // Linux
482
483 if (affinity > 0)
484 {
485 cpu_set_t mask;
486 CPU_ZERO(&mask);
487
488 for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i)
489 if (affinity & (1 << i))
490 CPU_SET(i, &mask);
491
492 if (sched_setaffinity(0, sizeof(mask), &mask))
493 SF_LOG_ERROR("server.worldserver", "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno));
494 else
495 {
496 CPU_ZERO(&mask);
497 sched_getaffinity(0, sizeof(mask), &mask);
498 SF_LOG_INFO("server.worldserver", "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask));
499 }
500 }
501
502 if (highPriority)
503 {
504 if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY))
505 SF_LOG_ERROR("server.worldserver", "Can't set worldserver process priority class, error: %s", strerror(errno));
506 else
507 SF_LOG_INFO("server.worldserver", "worldserver process priority class set to %i", getpriority(PRIO_PROCESS, 0));
508 }
509
510#endif
511#endif
512
513 //Start soap serving thread
514 SFSoapService soapService;
515
516 if (sConfigMgr->GetBoolDefault("SOAP.Enabled", false))
517 {
518 soapService.Start(sConfigMgr->GetStringDefault("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetIntDefault("SOAP.Port", 7878)));
519 }
520
522 Skyfire::Asio::IoContextTaskRunner freezeDetectorRunner;
523 if (uint32 freezeDelay = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0))
524 {
526 fdr.SetDelayTime(freezeDelay * 1000);
527 if (freezeDetectorRunner.Start([fdr]() mutable { fdr.Run(); }) == -1)
528 SF_LOG_ERROR("server.worldserver", "Failed to start anti-freeze task");
529 }
530
532 uint16 worldPort = uint16(sWorld->getIntConfig(WorldIntConfigs::CONFIG_PORT_WORLD));
533 std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
534
535 if (sWorldSocketMgr->StartNetwork(worldPort, bindIp.c_str()) == -1)
536 {
537 SF_LOG_ERROR("server.worldserver", "Failed to start network");
539 // go down and shutdown the server
540 }
541
542 // set server online (allow connecting now)
543 for (std::map<uint32, std::string>::const_iterator itr = realmNameStore.begin(); itr != realmNameStore.end(); ++itr)
544 {
545 LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, itr->first);
546 }
547
548 SF_LOG_INFO("server.worldserver", " % s (worldserver-daemon) ready...", SKYFIRE_VER_PRODUCTVERSION_STR);
549
550 // when the main thread closes the singletons get unloaded
551 // since worldrunnable uses them, it will crash if unloaded after master
552 worldRunner.Join();
553
554 raRunner.Join();
555
556 freezeDetectorRunner.Join();
557
558 soapService.Join();
559
560 // set server offline
561 for (std::map<uint32, std::string>::const_iterator itr = realmNameStore.begin(); itr != realmNameStore.end(); ++itr)
562 {
563 LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, itr->first);
564 }
565
568
569 _StopDB();
570
571 SF_LOG_INFO("server.worldserver", "Halting process...");
572
573 if (cliRunner)
574 {
575#ifdef _WIN32
576
577 // this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API)
578 //_exit(1);
579 // send keyboard input to safely unblock the CLI thread
580 INPUT_RECORD b[4];
581 HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
582 b[0].EventType = KEY_EVENT;
583 b[0].Event.KeyEvent.bKeyDown = TRUE;
584 b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
585 b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
586 b[0].Event.KeyEvent.wRepeatCount = 1;
587
588 b[1].EventType = KEY_EVENT;
589 b[1].Event.KeyEvent.bKeyDown = FALSE;
590 b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
591 b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
592 b[1].Event.KeyEvent.wRepeatCount = 1;
593
594 b[2].EventType = KEY_EVENT;
595 b[2].Event.KeyEvent.bKeyDown = TRUE;
596 b[2].Event.KeyEvent.dwControlKeyState = 0;
597 b[2].Event.KeyEvent.uChar.AsciiChar = '\r';
598 b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
599 b[2].Event.KeyEvent.wRepeatCount = 1;
600 b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
601
602 b[3].EventType = KEY_EVENT;
603 b[3].Event.KeyEvent.bKeyDown = FALSE;
604 b[3].Event.KeyEvent.dwControlKeyState = 0;
605 b[3].Event.KeyEvent.uChar.AsciiChar = '\r';
606 b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
607 b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
608 b[3].Event.KeyEvent.wRepeatCount = 1;
609 DWORD numb;
610 WriteConsoleInput(hStdIn, b, 4, &numb);
611
612 cliRunner->Join();
613
614#else
615
616 cliRunner->Join();
617
618#endif
619 }
620
621 // for some unknown reason, unloading scripts here and not in worldrunnable
622 // fixes a memory leak related to detaching threads from the module
623 //UnloadScriptingModule();
624
625 // Exit the process with specified return value
626 return World::GetExitCode();
627}
628
631{
633
634 std::string dbString;
635 uint8 asyncThreads, synchThreads;
636
637 if (_noUseConfigDatabaseInfo == false)
638 {
639 dbString = sConfigMgr->GetStringDefault("WorldDatabaseInfo", "");
640 if (dbString.empty())
641 {
642 SF_LOG_ERROR("server.worldserver", "World database not specified in configuration file");
643 return false;
644 }
645 }
646
647 asyncThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.WorkerThreads", 1));
648 if (asyncThreads < 1 || asyncThreads > 32)
649 {
650 SF_LOG_ERROR("server.worldserver", "World database: invalid number of worker threads specified. "
651 "Please pick a value between 1 and 32.");
652 return false;
653 }
654
655 synchThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.SynchThreads", 1));
656
657 Skyfire::Database::SetupOptions worldSetupOptions = LoadWorldDatabaseSetupOptions();
658 MySQLConnectionInfo worldSetupConnectionInfo = _noUseConfigDatabaseInfo == false
659 ? MySQLConnectionInfo(dbString)
660 : MySQLConnectionInfo(_dbHost, _dbPort, _dbUser, _dbPassword, _worldDB);
661
662 if (!RunWorldDatabaseSetup(worldSetupConnectionInfo, worldSetupOptions))
663 return false;
664
665 if (_noUseConfigDatabaseInfo == false)
666 {
667
669 if (!WorldDatabase.Open(dbString, asyncThreads, synchThreads))
670 {
671 SF_LOG_ERROR("server.worldserver", "Cannot connect to world database %s", dbString.c_str());
672 return false;
673 }
674 }
675 else
676 {
677 if (!WorldDatabase.Open(_dbHost, _dbPort, _dbUser, _dbPassword, _worldDB, asyncThreads, synchThreads))
678 {
679 SF_LOG_ERROR("server.worldserver", "Cannot connect to world database %s, %s, %s, %s, %s", _dbHost, _dbPort, _dbUser, _dbPassword, _worldDB);
680 return false;
681 }
682 }
683
684 if (_noUseConfigDatabaseInfo == false)
685 {
687 dbString = sConfigMgr->GetStringDefault("CharacterDatabaseInfo", "");
688 if (dbString.empty())
689 {
690 SF_LOG_ERROR("server.worldserver", "Character database not specified in configuration file");
691 return false;
692 }
693 }
694
695 asyncThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.WorkerThreads", 1));
696 if (asyncThreads < 1 || asyncThreads > 32)
697 {
698 SF_LOG_ERROR("server.worldserver", "Character database: invalid number of worker threads specified. "
699 "Please pick a value between 1 and 32.");
700 return false;
701 }
702
703 synchThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.SynchThreads", 2));
704
705 Skyfire::Database::SetupOptions characterSetupOptions = LoadCharacterDatabaseSetupOptions();
706 MySQLConnectionInfo characterSetupConnectionInfo = _noUseConfigDatabaseInfo == false
707 ? MySQLConnectionInfo(dbString)
708 : MySQLConnectionInfo(_dbHost, _dbPort, _dbUser, _dbPassword, _charactersDB);
709
710 if (!RunCharacterDatabaseSetup(characterSetupConnectionInfo, characterSetupOptions))
711 return false;
712
713 if (_noUseConfigDatabaseInfo == false)
714 {
716 if (!CharacterDatabase.Open(dbString, asyncThreads, synchThreads))
717 {
718 SF_LOG_ERROR("server.worldserver", "Cannot connect to Character database%s, %s", dbString.c_str());
719 return false;
720 }
721 }
722 else
723 {
725 if (!CharacterDatabase.Open(_dbHost, _dbPort, _dbUser, _dbPassword, _charactersDB, asyncThreads, synchThreads))
726 {
727 SF_LOG_ERROR("server.worldserver", "Cannot connect to Character database%s, %s, %s, %s, %s", _dbHost, _dbPort, _dbUser, _dbPassword, _charactersDB);
728 return false;
729 }
730 }
731
732 if (_noUseConfigDatabaseInfo == false)
733 {
735 dbString = sConfigMgr->GetStringDefault("LoginDatabaseInfo", "");
736 if (dbString.empty())
737 {
738 SF_LOG_ERROR("server.worldserver", "Login database not specified in configuration file");
739 return false;
740 }
741 }
742
743 asyncThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1));
744 if (asyncThreads < 1 || asyncThreads > 32)
745 {
746 SF_LOG_ERROR("server.worldserver", "Login database: invalid number of worker threads specified. "
747 "Please pick a value between 1 and 32.");
748 return false;
749 }
750
751 synchThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1));
752
753 if (_noUseConfigDatabaseInfo == false)
754 {
756 if (!LoginDatabase.Open(dbString, asyncThreads, synchThreads))
757 {
758 SF_LOG_ERROR("server.worldserver", "Cannot connect to login database %s", dbString.c_str());
759 return false;
760 }
761 }
762 else
763 {
764 if (!LoginDatabase.Open(_dbHost, _dbPort, _dbUser, _dbPassword, _authDB, asyncThreads, synchThreads))
765 {
766 SF_LOG_ERROR("server.worldserver", "Cannot connect to database%s %s, %s, %s, %s", _dbHost, _dbPort, _dbUser, _dbPassword, _authDB);
767 return false;
768 }
769 }
770
771 // Load realm names into a store
772 PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST);
773 stmt->setInt32(0, sConfigMgr->GetIntDefault("WorldServerPort", 8085));
774 PreparedQueryResult result = LoginDatabase.Query(stmt);
775 if (result)
776 {
777 do
778 {
779 Field* fields = result->Fetch();
780 realmNameStore[fields[0].GetUInt32()] = fields[1].GetString(); // Store the realm name into the store
781 } while (result->NextRow());
782 }
783 for (std::map<uint32, std::string>::const_iterator itr = realmNameStore.begin(); itr != realmNameStore.end(); ++itr)
784 {
785 SF_LOG_INFO("server.worldserver", "World running as realm ID %d", itr->first);
786 }
787
790
792 WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", SKYFIRE_VER_PRODUCTVERSION_STR, _HASH); // One-time query
793
794 sWorld->LoadDBVersion();
795
796 SF_LOG_INFO("server.worldserver", "Using World DB: %s", sWorld->GetDBVersion());
797 return true;
798}
799
801{
802 CharacterDatabase.Close();
803 WorldDatabase.Close();
804 LoginDatabase.Close();
805
807}
808
811{
812 // Reset online status for all accounts with characters on the current realm
813 for (std::map<uint32, std::string>::const_iterator itr = realmNameStore.begin(); itr != realmNameStore.end(); ++itr)
814 {
815 LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = %d)", itr->first);
816 }
817
818 // Reset online status for all characters
819 CharacterDatabase.DirectExecute("UPDATE characters SET online = 0 WHERE online <> 0");
820
821 // Battleground instance ids reset at server restart
822 CharacterDatabase.DirectExecute("UPDATE character_battleground_data SET instanceId = 0");
823}
#define sConfigMgr
Definition Config.h:64
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
std::uint16_t uint16
Definition Define.h:78
#define ASSERT
Definition Errors.h:29
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
#define SF_LOG_INFO(filterType__,...)
Definition Log.h:137
@ LOGIN_SEL_REALMLIST
void WorldServerSignalHandler(int sigNum)
Definition Master.cpp:293
Skyfire::AutoPtr< PreparedResultSet, Skyfire::Mutex > PreparedQueryResult
Definition QueryResult.h:94
@ REALM_FLAG_OFFLINE
Definition RealmList.h:17
@ REALM_FLAG_INVALID
Definition RealmList.h:16
#define SKYFIREWORLD_CONFIG_VERSION
uint32 getMSTime()
Definition Timer.h:12
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
Definition Timer.h:17
uint32 CreatePIDFile(const std::string &filename)
create PID file
Definition Util.cpp:253
void SetRand(int32 numbits)
Definition BigNumber.cpp:70
Definition Field.h:16
std::string GetString() const
Definition Field.h:228
uint32 GetUInt32() const
Definition Field.h:105
void SetDelayTime(uint32 t)
Definition Master.cpp:319
const char * _authDB
Definition Master.h:42
const char * _dbUser
Definition Master.h:40
bool _StartDB()
Initialize connection to the databases.
Definition Master.cpp:630
void ClearOnlineAccounts()
Clear 'online' status for all accounts with characters in this realm.
Definition Master.cpp:810
const char * _worldDB
Definition Master.h:44
bool _noUseConfigDatabaseInfo
Definition Master.h:37
const char * _dbPort
Definition Master.h:39
int Run()
Main function.
Definition Master.cpp:352
const char * _dbHost
Definition Master.h:38
const char * _dbPassword
Definition Master.h:41
const char * _charactersDB
Definition Master.h:43
void _StopDB()
Definition Master.cpp:800
static void Library_Init()
static void Library_End()
void setInt32(const uint8 index, const int32 value)
void Join()
Definition SFSoap.h:66
bool Start(const std::string &host, uint16 port)
Definition SFSoap.h:48
static uint8 GetExitCode()
Definition World.h:673
static void StopNow(uint8 exitcode)
Definition World.h:674
static bool IsStopped()
Definition World.h:675
static std::atomic< uint32 > m_worldLoopCounter
Definition World.h:559
CharacterDatabaseWorkerPool CharacterDatabase
Accessor to the character database.
Definition Main.cpp:41
WorldDatabaseWorkerPool WorldDatabase
Accessor to the world database.
Definition Main.cpp:40
#define sWorldSocketMgr
#define sWorld
Definition World.h:910
RealmNameMap realmNameStore
Definition Main.cpp:72
@ CONFIG_PORT_WORLD
Definition World.h:205
@ RESTART_EXIT_CODE
Definition World.h:66
@ SHUTDOWN_EXIT_CODE
Definition World.h:64
@ ERROR_EXIT_CODE
Definition World.h:65
std::vector< SqlUpdateFile > DiscoverSqlUpdates(SetupOptions const &options)
SetupPlan BuildWorldDatabaseSetupPlan(SetupOptions const &options, SetupState const &state, bool externalBaseSqlExists, bool requiredBaseSqlExists, std::vector< SqlUpdateFile > const &updates)
bool EnsureDatabaseExists(MySQLConnectionInfo const &connectionInfo, SetupOptions const &options, SetupRuntimeContext const &context)
bool LoadDatabaseSetupState(MYSQL *setupConnection, SetupOptions const &options, SetupState &state, SetupRuntimeContext const &context)
bool ConnectToMySQLServer(MySQLConnectionInfo const &connectionInfo, char const *databaseName, MYSQL *&handle, SetupRuntimeContext const &context)
bool ExecuteSqlFile(MYSQL *setupConnection, std::filesystem::path const &path, std::string &contents, SetupRuntimeContext const &context)
SetupOptions MakeWorldDatabaseSetupOptions(bool autoSetup, bool autoCreate, std::string sqlPath, std::string externalBaseFile)
SetupOptions MakeCharacterDatabaseSetupOptions(bool autoSetup, bool autoCreate, std::string sqlPath)
SetupPlan BuildCharacterDatabaseSetupPlan(SetupOptions const &options, SetupState const &state, bool baseSqlExists, std::vector< SqlUpdateFile > const &updates)
bool BaselineSetupUpdates(MYSQL *setupConnection, SetupOptions const &options, SetupPlan const &plan, SetupRuntimeContext const &context)
std::filesystem::path GetDatabaseBaseSqlPath(SetupOptions const &options)
void LogSetupPlan(SetupPlan const &plan, std::size_t discoveredUpdateCount, bool appliesRequiredSql, SetupRuntimeContext const &context)
bool EnsureSetupTrackingTables(MYSQL *setupConnection, SetupRuntimeContext const &context)
bool ApplyPendingSetupUpdates(MYSQL *setupConnection, SetupOptions const &options, SetupPlan const &plan, SetupRuntimeContext const &context)
void SleepForSeconds(uint32 seconds)
Definition TimeUtils.h:47
LoginDatabaseWorkerPool LoginDatabase
Definition Main.cpp:54
std::vector< std::string > RequiredBaseFileNames
std::vector< SqlUpdateFile > PendingUpdates
std::vector< SqlUpdateFile > BaselineUpdates