void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo) { #ifdef _WIN32 HWND hwnd = GetConsoleWindow(); HMENU hmenu = GetSystemMenu(hwnd, FALSE); EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling #endif cLogger::cListener * consoleLogListener = MakeConsoleListener(); cLogger::cListener * fileLogListener = new cFileListener(); cLogger::GetInstance().AttachListener(consoleLogListener); cLogger::GetInstance().AttachListener(fileLogListener); LOG("--- Started Log ---\n"); #ifdef BUILD_ID LOG("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID); LOG("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME); #endif cDeadlockDetect dd; m_ShouldStop = false; while (!m_ShouldStop) { auto BeginTime = std::chrono::steady_clock::now(); m_bRestart = false; LoadGlobalSettings(); LOG("Creating new server instance..."); m_Server = new cServer(); LOG("Reading server config..."); auto IniFile = cpp14::make_unique<cIniFile>(); if (!IniFile->ReadFile("settings.ini")) { LOGWARN("Regenerating settings.ini, all settings will be reset"); IniFile->AddHeaderComment(" This is the main server configuration"); IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help"); } auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(overridesRepo)); LOG("Starting server..."); m_MojangAPI = new cMojangAPI; bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true); m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate)) { settingsRepo->Flush(); LOGERROR("Failure starting server, aborting..."); return; } m_WebAdmin = new cWebAdmin(); m_WebAdmin->Init(); LOGD("Loading settings..."); m_RankManager.reset(new cRankManager()); m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); LOGD("Loading worlds..."); LoadWorlds(*settingsRepo); LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(*settingsRepo); LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion LOGD("Starting Authenticator..."); m_Authenticator.Start(*settingsRepo); LOGD("Starting worlds..."); StartWorlds(); if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true)) { LOGD("Starting deadlock detector..."); dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20)); } settingsRepo->Flush(); LOGD("Finalising startup..."); if (m_Server->Start()) { m_WebAdmin->Start(); #if !defined(ANDROID_NDK) LOGD("Starting InputThread..."); try { m_InputThread = std::thread(InputThread, std::ref(*this)); m_InputThread.detach(); } catch (std::system_error & a_Exception) { LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what()); } #endif LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); // Save the current time m_StartTime = std::chrono::steady_clock::now(); #ifdef _WIN32 EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif while (!m_ShouldStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads { std::this_thread::sleep_for(std::chrono::seconds(1)); } if (m_TerminateEventRaised) { m_ShouldStop = true; } // Stop the server: m_WebAdmin->Stop(); LOG("Shutting down server..."); m_Server->Shutdown(); } // if (m_Server->Start()) else { m_ShouldStop = true; } delete m_MojangAPI; m_MojangAPI = nullptr; LOGD("Shutting down deadlock detector..."); dd.Stop(); LOGD("Stopping world threads..."); StopWorlds(); LOGD("Stopping authenticator..."); m_Authenticator.Stop(); LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = nullptr; delete m_WebAdmin; m_WebAdmin = nullptr; LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr; delete m_CraftingRecipes; m_CraftingRecipes = nullptr; LOGD("Unloading worlds..."); UnloadWorlds(); LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = nullptr; cItemHandler::Deinit(); LOG("Cleaning up..."); delete m_Server; m_Server = nullptr; LOG("Shutdown successful!"); } LOG("--- Stopped Log ---"); cLogger::GetInstance().DetachListener(consoleLogListener); delete consoleLogListener; cLogger::GetInstance().DetachListener(fileLogListener); delete fileLogListener; }
void cRoot::Start(void) { cDeadlockDetect dd; delete m_Log; m_Log = new cMCLogger(); m_bStop = false; while (!m_bStop) { m_bRestart = false; LoadGlobalSettings(); LOG("Creating new server instance..."); m_Server = new cServer(); LOG("Reading server config..."); cIniFile IniFile("settings.ini"); if (!IniFile.ReadFile()) { LOGWARNING("settings.ini inaccessible, all settings are reset to default values"); } m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0); if (m_PrimaryServerVersion == 0) { m_PrimaryServerVersion = cProtocolRecognizer::PROTO_VERSION_LATEST; } else { // Make a note in the log that the primary server version is explicitly set in the ini file LOGINFO("settings.ini: [Server].PrimaryServerVersion set to %d.", m_PrimaryServerVersion); } LOG("Starting server..."); if (!m_Server->InitServer(IniFile)) { LOGERROR("Failed to start server, shutting down."); return; } IniFile.WriteFile(); cIniFile WebIniFile("webadmin.ini"); if (!WebIniFile.ReadFile()) { LOGWARNING("webadmin.ini inaccessible, wabadmin is disabled"); } if (WebIniFile.GetValueB("WebAdmin", "Enabled", false)) { LOG("Creating WebAdmin..."); m_WebAdmin = new cWebAdmin(8080); } LOG("Loading settings..."); m_GroupManager = new cGroupManager(); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); LOG("Loading worlds..."); LoadWorlds(); LOG("Loading plugin manager..."); m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(); LOG("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion LOG("Starting Authenticator..."); m_Authenticator.Start(); LOG("Starting worlds..."); StartWorlds(); LOG("Starting deadlock detector..."); dd.Start(); LOG("Starting server..."); m_Server->Start(); #if !defined(ANDROID_NDK) LOG("Starting InputThread..."); m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread #endif LOG("Initialization done, server running now."); while (!m_bStop && !m_bRestart) // These are modified by external threads { cSleep::MilliSleep(1000); } #if !defined(ANDROID_NDK) delete m_InputThread; m_InputThread = NULL; #endif // Deallocate stuffs LOG("Shutting down server..."); m_Server->Shutdown(); LOG("Shutting down deadlock detector..."); dd.Stop(); LOG("Stopping world threads..."); StopWorlds(); LOG("Stopping authenticator..."); m_Authenticator.Stop(); LOG("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = NULL; LOG("Stopping WebAdmin..."); delete m_WebAdmin; m_WebAdmin = NULL; LOG("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; delete m_CraftingRecipes; m_CraftingRecipes = NULL; LOG("Forgetting groups..."); delete m_GroupManager; m_GroupManager = 0; LOG("Unloading worlds..."); UnloadWorlds(); LOG("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = NULL; cItemHandler::Deinit(); cBlockHandler::Deinit(); LOG("Destroying server..."); //delete HeartBeat; HeartBeat = 0; delete m_Server; m_Server = 0; LOG("Shutdown done."); } delete m_Log; m_Log = 0; }
void cRoot::Start(void) { #ifdef _WIN32 HWND hwnd = GetConsoleWindow(); HMENU hmenu = GetSystemMenu(hwnd, FALSE); EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling #endif cLogger::cListener * consoleLogListener = MakeConsoleListener(); cLogger::cListener * fileLogListener = new cFileListener(); cLogger::GetInstance().AttachListener(consoleLogListener); cLogger::GetInstance().AttachListener(fileLogListener); LOG("--- Started Log ---\n"); #ifdef BUILD_ID LOG("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID ); LOG("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME ); #endif cDeadlockDetect dd; m_bStop = false; while (!m_bStop) { cTimer Time; long long mseconds = Time.GetNowTime(); m_bRestart = false; LoadGlobalSettings(); LOG("Creating new server instance..."); m_Server = new cServer(); LOG("Reading server config..."); cIniFile IniFile; if (!IniFile.ReadFile("settings.ini")) { LOGWARN("Regenerating settings.ini, all settings will be reset"); IniFile.AddHeaderComment(" This is the main server configuration"); IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); IniFile.AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help"); } m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0); if (m_PrimaryServerVersion == 0) { m_PrimaryServerVersion = cProtocolRecognizer::PROTO_VERSION_LATEST; } else { // Make a note in the log that the primary server version is explicitly set in the ini file LOGINFO("Primary server version set explicitly to %d.", m_PrimaryServerVersion); } LOG("Starting server..."); m_MojangAPI.Start(IniFile); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init if (!m_Server->InitServer(IniFile)) { IniFile.WriteFile("settings.ini"); LOGERROR("Failure starting server, aborting..."); return; } m_WebAdmin = new cWebAdmin(); m_WebAdmin->Init(); LOGD("Loading settings..."); m_RankManager.Initialize(m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); LOGD("Loading worlds..."); LoadWorlds(IniFile); LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(IniFile); LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion LOGD("Starting Authenticator..."); m_Authenticator.Start(IniFile); LOGD("Starting worlds..."); StartWorlds(); if (IniFile.GetValueSetB("DeadlockDetect", "Enabled", true)) { LOGD("Starting deadlock detector..."); dd.Start(IniFile.GetValueSetI("DeadlockDetect", "IntervalSec", 20)); } IniFile.WriteFile("settings.ini"); LOGD("Finalising startup..."); m_Server->Start(); m_WebAdmin->Start(); #if !defined(ANDROID_NDK) LOGD("Starting InputThread..."); m_InputThread = new cThread( InputThread, this, "cRoot::InputThread"); m_InputThread->Start( false); // We should NOT wait? Otherwise we can't stop the server from other threads than the input thread #endif long long finishmseconds = Time.GetNowTime(); finishmseconds -= mseconds; LOG("Startup complete, took %lld ms!", finishmseconds); #ifdef _WIN32 EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif while (!m_bStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads { cSleep::MilliSleep(1000); } if (m_TerminateEventRaised) { m_bStop = true; } #if !defined(ANDROID_NDK) delete m_InputThread; m_InputThread = NULL; #endif // Stop the server: m_WebAdmin->Stop(); LOG("Shutting down server..."); m_Server->Shutdown(); LOGD("Shutting down deadlock detector..."); dd.Stop(); LOGD("Stopping world threads..."); StopWorlds(); LOGD("Stopping authenticator..."); m_Authenticator.Stop(); LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = NULL; delete m_WebAdmin; m_WebAdmin = NULL; LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; delete m_CraftingRecipes; m_CraftingRecipes = NULL; LOGD("Unloading worlds..."); UnloadWorlds(); LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = NULL; cItemHandler::Deinit(); LOG("Cleaning up..."); delete m_Server; m_Server = NULL; LOG("Shutdown successful!"); } LOG("--- Stopped Log ---"); cLogger::GetInstance().DetachListener(consoleLogListener); delete consoleLogListener; cLogger::GetInstance().DetachListener(fileLogListener); delete fileLogListener; }
void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo) { #ifdef _WIN32 HMENU ConsoleMenu = GetSystemMenu(GetConsoleWindow(), FALSE); EnableMenuItem(ConsoleMenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling #endif auto consoleLogListener = MakeConsoleListener(m_RunAsService); auto consoleAttachment = cLogger::GetInstance().AttachListener(std::move(consoleLogListener)); auto fileLogListenerRet = MakeFileListener(); if (!fileLogListenerRet.first) { LOGERROR("Failed to open log file, aborting"); return; } auto fileAttachment = cLogger::GetInstance().AttachListener(std::move(fileLogListenerRet.second)); LOG("--- Started Log ---"); #ifdef BUILD_ID LOG("Cuberite " BUILD_SERIES_NAME " build id: " BUILD_ID); LOG("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME); #endif // Run the self-tests registered previously via cSelfTests::Register(): #ifdef SELF_TEST cSelfTests::ExecuteAll(); #endif cDeadlockDetect dd; auto BeginTime = std::chrono::steady_clock::now(); LoadGlobalSettings(); LOG("Creating new server instance..."); m_Server = new cServer(); LOG("Reading server config..."); auto IniFile = cpp14::make_unique<cIniFile>(); bool IsNewIniFile = !IniFile->ReadFile("settings.ini"); if (IsNewIniFile) { LOGWARN("Regenerating settings.ini, all settings will be reset"); IniFile->AddHeaderComment(" This is the main server configuration"); IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); } auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(a_OverridesRepo)); LOG("Starting server..."); m_MojangAPI = new cMojangAPI; bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true); m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate)) { settingsRepo->Flush(); LOGERROR("Failure starting server, aborting..."); return; } m_WebAdmin = new cWebAdmin(); m_WebAdmin->Init(); LOGD("Loading settings..."); m_RankManager.reset(new cRankManager()); m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes(); m_FurnaceRecipe = new cFurnaceRecipe(); m_BrewingRecipes.reset(new cBrewingRecipes()); LOGD("Loading worlds..."); LoadWorlds(*settingsRepo, IsNewIniFile); LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(*settingsRepo); LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion LOGD("Starting Authenticator..."); m_Authenticator.Start(*settingsRepo); LOGD("Starting worlds..."); StartWorlds(); if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true)) { LOGD("Starting deadlock detector..."); dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20)); } settingsRepo->Flush(); LOGD("Finalising startup..."); if (m_Server->Start()) { m_WebAdmin->Start(); #if !defined(ANDROID_NDK) LOGD("Starting InputThread..."); try { m_InputThreadRunFlag.test_and_set(); m_InputThread = std::thread(InputThread, std::ref(*this)); } catch (std::system_error & a_Exception) { LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what()); } #endif LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); // Save the current time m_StartTime = std::chrono::steady_clock::now(); #ifdef _WIN32 EnableMenuItem(ConsoleMenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif for (;;) { m_StopEvent.Wait(); break; } // Stop the server: m_WebAdmin->Stop(); LOG("Shutting down server..."); m_Server->Shutdown(); } // if (m_Server->Start() delete m_MojangAPI; m_MojangAPI = nullptr; LOGD("Shutting down deadlock detector..."); dd.Stop(); LOGD("Stopping world threads..."); StopWorlds(); LOGD("Stopping authenticator..."); m_Authenticator.Stop(); LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = nullptr; delete m_WebAdmin; m_WebAdmin = nullptr; LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr; delete m_CraftingRecipes; m_CraftingRecipes = nullptr; LOG("Unloading worlds..."); UnloadWorlds(); LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = nullptr; cItemHandler::Deinit(); LOG("Cleaning up..."); delete m_Server; m_Server = nullptr; m_InputThreadRunFlag.clear(); #ifdef _WIN32 DWORD Length; INPUT_RECORD Record { KEY_EVENT, { { TRUE, 1, VK_RETURN, static_cast<WORD>(MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC)), { { VK_RETURN } }, 0 } } }; // Can't kill the input thread since it breaks cin (getline doesn't block / receive input on restart) // Apparently no way to unblock getline // Only thing I can think of for now if (WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &Record, 1, &Length) == 0) { LOGWARN("Couldn't notify the input thread; the server will hang before shutdown!"); m_TerminateEventRaised = true; m_InputThread.detach(); } else { m_InputThread.join(); } #else if (pthread_kill(m_InputThread.native_handle(), SIGKILL) != 0) { LOGWARN("Couldn't notify the input thread; the server will hang before shutdown!"); m_TerminateEventRaised = true; m_InputThread.detach(); } #endif if (m_TerminateEventRaised) { LOG("Shutdown successful!"); } else { LOG("Shutdown successful - restarting..."); } LOG("--- Stopped Log ---"); }
void cRoot::Start(void) { cDeadlockDetect dd; delete m_Log; m_Log = new cMCLogger(); m_bStop = false; while (!m_bStop) { cTimer Time; long long mseconds = Time.GetNowTime(); m_bRestart = false; LoadGlobalSettings(); LOG("Creating new server instance..."); m_Server = new cServer(); LOG("Reading server config..."); cIniFile IniFile; if (!IniFile.ReadFile("settings.ini")) { LOGWARN("Regenerating settings.ini, all settings will be reset"); IniFile.AddHeaderComment(" This is the main server configuration"); IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); IniFile.AddHeaderComment(" See: http://www.mc-server.org/wiki/doku.php?id=configure:settings.ini for further configuration help"); } m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0); if (m_PrimaryServerVersion == 0) { m_PrimaryServerVersion = cProtocolRecognizer::PROTO_VERSION_LATEST; } else { // Make a note in the log that the primary server version is explicitly set in the ini file LOGINFO("Primary server version set explicitly to %d.", m_PrimaryServerVersion); } LOG("Starting server..."); if (!m_Server->InitServer(IniFile)) { LOGERROR("Failure starting server, aborting..."); return; } m_WebAdmin = new cWebAdmin(); m_WebAdmin->Init(); LOGD("Loading settings..."); m_GroupManager = new cGroupManager(); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); LOGD("Loading worlds..."); LoadWorlds(IniFile); LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(IniFile); LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion LOGD("Starting Authenticator..."); m_Authenticator.Start(IniFile); IniFile.WriteFile("settings.ini"); LOGD("Starting worlds..."); StartWorlds(); LOGD("Starting deadlock detector..."); dd.Start(); LOGD("Finalising startup..."); m_Server->Start(); m_WebAdmin->Start(); #if !defined(ANDROID_NDK) LOGD("Starting InputThread..."); m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread #endif long long finishmseconds = Time.GetNowTime(); finishmseconds -= mseconds; LOG("Startup complete, took %i ms!", finishmseconds); while (!m_bStop && !m_bRestart) // These are modified by external threads { cSleep::MilliSleep(1000); } #if !defined(ANDROID_NDK) delete m_InputThread; m_InputThread = NULL; #endif // Deallocate stuffs LOG("Shutting down server..."); m_Server->Shutdown(); LOGD("Shutting down deadlock detector..."); dd.Stop(); LOGD("Stopping world threads..."); StopWorlds(); LOGD("Stopping authenticator..."); m_Authenticator.Stop(); LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = NULL; delete m_WebAdmin; m_WebAdmin = NULL; LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; delete m_CraftingRecipes; m_CraftingRecipes = NULL; LOGD("Forgetting groups..."); delete m_GroupManager; m_GroupManager = 0; LOGD("Unloading worlds..."); UnloadWorlds(); LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = NULL; cItemHandler::Deinit(); cBlockHandler::Deinit(); LOG("Cleaning up..."); //delete HeartBeat; HeartBeat = 0; delete m_Server; m_Server = 0; LOG("Shutdown successful!"); } delete m_Log; m_Log = 0; }