5 #include "tclap/CmdLine.h" 18 std::string to_string(T Value)
20 std::ostringstream TempStream;
22 return TempStream.str();
32 #include "BuildInfo.h" 65 SERVICE_STATUS_HANDLE g_StatusHandle =
nullptr;
66 HANDLE g_ServiceThread = INVALID_HANDLE_VALUE;
67 #define SERVICE_NAME L"CuberiteService" 77 #pragma clang diagnostic push 78 #pragma clang diagnostic ignored "-Wunknown-warning-option" 79 #pragma clang diagnostic ignored "-Wunknown-pragmas" 80 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" 85 LOGD(
"Terminate event raised from std::signal");
92 std::signal(SIGSEGV, SIG_DFL);
93 LOGERROR(
" D: | Cuberite has encountered an error and needs to close");
94 LOGERROR(
"Details | SIGSEGV: Segmentation fault");
96 LOGERROR(
"Cuberite " BUILD_SERIES_NAME
" build id: " BUILD_ID);
97 LOGERROR(
"from commit id: " BUILD_COMMIT_ID
" built at: " BUILD_DATETIME);
103 #ifdef SIGABRT_COMPAT 107 std::signal(a_Signal, SIG_DFL);
108 LOGERROR(
" D: | Cuberite has encountered an error and needs to close");
109 LOGERROR(
"Details | SIGABRT: Server self-terminated due to an internal fault");
111 LOGERROR(
"Cuberite " BUILD_SERIES_NAME
" build id: " BUILD_ID);
112 LOGERROR(
"from commit id: " BUILD_COMMIT_ID
" built at: " BUILD_DATETIME);
120 std::signal(a_Signal, SIG_IGN);
128 #pragma clang diagnostic pop 136 #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) 140 typedef BOOL (WINAPI *pMiniDumpWriteDump)(
144 MINIDUMP_TYPE DumpType,
145 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
146 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
147 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
150 static pMiniDumpWriteDump g_WriteMiniDump;
152 static wchar_t g_DumpFileName[MAX_PATH];
153 static char g_ExceptionStack[128 * 1024];
154 static MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal;
163 static LONG WINAPI LastChanceExceptionFilter(__in
struct _EXCEPTION_POINTERS * a_ExceptionInfo)
165 char * newStack = &g_ExceptionStack[
sizeof(g_ExceptionStack) - 1];
176 MINIDUMP_EXCEPTION_INFORMATION ExcInformation;
177 ExcInformation.ThreadId = GetCurrentThreadId();
178 ExcInformation.ExceptionPointers = a_ExceptionInfo;
179 ExcInformation.ClientPointers = 0;
182 HANDLE dumpFile = CreateFile(g_DumpFileName, GENERIC_WRITE, 0,
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
nullptr);
183 g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation :
nullptr,
nullptr,
nullptr);
184 CloseHandle(dumpFile);
198 #endif // _WIN32 && !_WIN64 206 static BOOL CtrlHandler(DWORD fdwCtrlType)
209 LOGD(
"Terminate event raised from the Windows CtrlHandler");
211 std::this_thread::sleep_for(std::chrono::seconds(10));
225 static void UniversalMain(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
236 Root.
Start(std::move(a_OverridesRepo));
238 catch (
const fmt::FormatError & exc)
240 cRoot::m_TerminateEventRaised =
true;
241 FLOGERROR(
"Formatting exception: {0}", exc.what());
243 catch (
const std::exception & exc)
245 cRoot::m_TerminateEventRaised =
true;
246 LOGERROR(
"Standard exception: %s", exc.what());
250 cRoot::m_TerminateEventRaised =
true;
262 #if defined(_WIN32) // Windows service support. 266 static DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
268 UNREFERENCED_PARAMETER(lpParam);
270 while (!cRoot::m_TerminateEventRaised)
273 UniversalMain(cpp14::make_unique<cMemorySettingsRepository>());
276 return ERROR_SUCCESS;
286 static void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode)
288 SERVICE_STATUS serviceStatus = {};
289 serviceStatus.dwCheckPoint = 0;
290 serviceStatus.dwControlsAccepted = acceptedControls;
291 serviceStatus.dwCurrentState = newState;
292 serviceStatus.dwServiceSpecificExitCode = 0;
293 serviceStatus.dwServiceType = SERVICE_WIN32;
294 serviceStatus.dwWaitHint = 0;
295 serviceStatus.dwWin32ExitCode = exitCode;
297 if (SetServiceStatus(g_StatusHandle, &serviceStatus) == FALSE)
299 LOGERROR(
"SetServiceStatus() failed\n");
309 static void WINAPI serviceCtrlHandler(DWORD CtrlCode)
313 case SERVICE_CONTROL_STOP:
316 serviceSetState(0, SERVICE_STOP_PENDING, 0);
332 static void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
334 wchar_t applicationFilename[MAX_PATH];
335 wchar_t applicationDirectory[MAX_PATH];
337 GetModuleFileName(
nullptr, applicationFilename,
sizeof(applicationFilename));
340 wcsncpy_s(applicationDirectory,
sizeof(applicationDirectory), applicationFilename, (wcsrchr(applicationFilename,
'\\') - applicationFilename));
341 applicationDirectory[wcslen(applicationDirectory)] =
'\0';
345 SetCurrentDirectory(applicationDirectory);
347 g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, serviceCtrlHandler);
348 if (g_StatusHandle ==
nullptr)
350 OutputDebugStringA(
"RegisterServiceCtrlHandler() failed\n");
351 serviceSetState(0, SERVICE_STOPPED, GetLastError());
355 serviceSetState(SERVICE_ACCEPT_STOP, SERVICE_RUNNING, 0);
358 g_ServiceThread = CreateThread(
nullptr, 0, serviceWorkerThread,
nullptr, 0, &ThreadID);
359 if (g_ServiceThread ==
nullptr)
361 OutputDebugStringA(
"CreateThread() failed\n");
362 serviceSetState(0, SERVICE_STOPPED, GetLastError());
365 WaitForSingleObject(g_ServiceThread, INFINITE);
367 CloseHandle(g_ServiceThread);
369 serviceSetState(0, SERVICE_STOPPED, 0);
371 #endif // Windows service support. 377 static std::unique_ptr<cMemorySettingsRepository>
ParseArguments(
int argc,
char ** argv)
382 TCLAP::CmdLine cmd(
"Cuberite");
383 TCLAP::ValueArg<int> slotsArg (
"s",
"max-players",
"Maximum number of slots for the server to use, overrides setting in setting.ini",
false, -1,
"number", cmd);
384 TCLAP::ValueArg<AString> confArg (
"c",
"config-file",
"Config file to use",
false,
"settings.ini",
"string", cmd);
385 TCLAP::MultiArg<int> portsArg (
"p",
"port",
"The port number the server should listen to",
false,
"port", cmd);
386 TCLAP::SwitchArg commLogArg (
"",
"log-comm",
"Log server client communications to file", cmd);
387 TCLAP::SwitchArg commLogInArg (
"",
"log-comm-in",
"Log inbound server client communications to file", cmd);
388 TCLAP::SwitchArg commLogOutArg (
"",
"log-comm-out",
"Log outbound server client communications to file", cmd);
389 TCLAP::SwitchArg crashDumpFull (
"",
"crash-dump-full",
"Crashdumps created by the server will contain full server memory", cmd);
390 TCLAP::SwitchArg crashDumpGlobals(
"",
"crash-dump-globals",
"Crashdumps created by the server will contain the global variables' values", cmd);
391 TCLAP::SwitchArg noBufArg (
"",
"no-output-buffering",
"Disable output buffering", cmd);
392 TCLAP::SwitchArg noFileLogArg (
"",
"no-log-file",
"Disable logging to file", cmd);
393 TCLAP::SwitchArg runAsServiceArg (
"d",
"service",
"Run as a service on Windows, or daemon on UNIX like systems", cmd);
394 cmd.parse(argc, argv);
397 auto repo = cpp14::make_unique<cMemorySettingsRepository>();
400 AString conf_file = confArg.getValue();
401 repo->AddValue(
"Server",
"ConfigFile", conf_file);
403 if (slotsArg.isSet())
405 int slots = slotsArg.getValue();
406 repo->AddValue(
"Server",
"MaxPlayers", static_cast<Int64>(slots));
408 if (portsArg.isSet())
410 for (
auto port: portsArg.getValue())
412 repo->AddValue(
"Server",
"Ports", std::to_string(port));
415 if (noFileLogArg.getValue())
417 repo->AddValue(
"Server",
"DisableLogFile",
true);
419 if (commLogArg.getValue())
421 g_ShouldLogCommIn =
true;
422 g_ShouldLogCommOut =
true;
426 g_ShouldLogCommIn = commLogInArg.getValue();
427 g_ShouldLogCommOut = commLogOutArg.getValue();
429 if (noBufArg.getValue())
431 setvbuf(stdout,
nullptr, _IONBF, 0);
436 if (runAsServiceArg.getValue())
438 cRoot::m_RunAsService =
true;
442 #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) // 32-bit Windows app compiled in MSVC 443 if (crashDumpGlobals.getValue())
445 g_DumpFlags =
static_cast<MINIDUMP_TYPE
>(g_DumpFlags | MiniDumpWithDataSegs);
447 if (crashDumpFull.getValue())
449 g_DumpFlags =
static_cast<MINIDUMP_TYPE
>(g_DumpFlags | MiniDumpWithFullMemory);
451 #endif // 32-bit Windows app compiled in MSVC 455 catch (
const TCLAP::ArgException & exc)
457 fmt::print(
"Error reading command line {0} for arg {1}", exc.error(), exc.argId());
458 return cpp14::make_unique<cMemorySettingsRepository>();
469 int main(
int argc,
char ** argv)
472 #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) // 32-bit Windows app compiled in MSVC 473 HINSTANCE hDbgHelp = LoadLibrary(L
"DBGHELP.DLL");
474 g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp,
"MiniDumpWriteDump");
475 if (g_WriteMiniDump !=
nullptr)
477 _snwprintf_s(g_DumpFileName,
ARRAYCOUNT(g_DumpFileName), _TRUNCATE, L
"crash_mcs_%x.dmp", GetCurrentProcessId());
478 SetUnhandledExceptionFilter(LastChanceExceptionFilter);
480 #endif // 32-bit Windows app compiled in MSVC 484 #if defined(_DEBUG) && defined(_MSC_VER) 485 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
491 #endif // _DEBUG && _MSC_VER 498 #ifdef SIGABRT_COMPAT 500 #endif // SIGABRT_COMPAT 505 std::signal(SIGPIPE, SIG_IGN);
509 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
511 LOGERROR(
"Could not install the Windows CTRL handler!");
519 if (cRoot::m_RunAsService)
521 #if defined(_WIN32) // Windows service. 522 SERVICE_TABLE_ENTRY ServiceTable[] =
524 { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)serviceMain },
528 if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
530 LOGERROR(
"Attempted, but failed, service startup.");
531 return GetLastError();
533 #else // UNIX daemon. 539 LOGERROR(
"Could not fork process.");
551 close(STDOUT_FILENO);
552 close(STDERR_FILENO);
554 while (!cRoot::m_TerminateEventRaised)
562 while (!cRoot::m_TerminateEventRaised)
bool g_ShouldLogCommOut
If set to true, the protocols will log each player's outgoing (S->C) communication to a per-connectio...
void Terminate(void)
Terminates all network-related threads.
static void NonCtrlHandler(int a_Signal)
void QueueExecuteConsoleCommand(const AString &a_Cmd, cCommandOutputCallback &a_Output)
Queues a console command for execution through the cServer class.
void LOGERROR(const char *a_Format, fmt::ArgList a_ArgList)
void FLOGERROR(const char *a_Format, fmt::ArgList a_ArgList)
static void UniversalMain(std::unique_ptr< cSettingsRepositoryInterface > a_OverridesRepo)
int main(int argc, char **argv)
static bool m_TerminateEventRaised
If something has told the server to stop; checked periodically in cRoot.
static bool m_RunAsService
If set to true, binary will attempt to run as a service on Windows.
bool g_ShouldLogCommIn
If set to true, the protocols will log each player's incoming (C->S) communication to a per-connectio...
The root of the object hierarchy.
static cNetworkSingleton & Get(void)
Returns the singleton instance of this class.
void PrintStackTrace(void)
Prints the stacktrace for the current thread.
void Start(std::unique_ptr< cSettingsRepositoryInterface > a_OverridesRepo)
static void InitiateMultithreading()
static std::unique_ptr< cMemorySettingsRepository > ParseArguments(int argc, char **argv)
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
void Initialise(void)
Initialises all network-related threads.