Example #1
0
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;
}
Example #2
0
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 ---");
}