<commit_before>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Root.h"
#include "Server.h"
#include "World.h"
#include "WebAdmin.h"
#include "FurnaceRecipe.h"
#include "CraftingRecipes.h"
#include "Bindings/PluginManager.h"
#include "MonsterConfig.h"
#include "Entities/Player.h"
#include "Blocks/BlockHandler.h"
#include "Items/ItemHandler.h"
#include "Chunk.h"
#include "Protocol/ProtocolRecognizer.h"  // for protocol version constants
#include "CommandOutput.h"
#include "DeadlockDetect.h"
#include "LoggerListeners.h"
#include "BuildInfo.h"
#include "IniFile.h"

#ifdef _WIN32
	#include <conio.h>
	#include <psapi.h>
#elif defined(__linux__)
	#include <fstream>
#elif defined(__APPLE__)
	#include <mach/mach.h>
#endif





cRoot* cRoot::s_Root = nullptr;





cRoot::cRoot(void) :
	m_pDefaultWorld(nullptr),
	m_Server(nullptr),
	m_MonsterConfig(nullptr),
	m_CraftingRecipes(nullptr),
	m_FurnaceRecipe(nullptr),
	m_WebAdmin(nullptr),
	m_PluginManager(nullptr),
	m_bStop(false),
	m_bRestart(false)
{
	s_Root = this;
}





cRoot::~cRoot()
{
	s_Root = 0;
}





void cRoot::InputThread(cRoot & a_Params)
{
	cLogCommandOutputCallback Output;
	
	while (!a_Params.m_bStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good())
	{
		AString Command;
		std::getline(std::cin, Command);
		if (!Command.empty())
		{
			a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
		}
	}

	if (m_TerminateEventRaised || !std::cin.good())
	{
		// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server:
		a_Params.m_bStop = true;
	}
}





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)
	{
		auto BeginTime = std::chrono::steady_clock::now();
		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");
		}

		LOG("Starting server...");
		bool ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
		m_MojangAPI.Start(IniFile, 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(IniFile, ShouldAuthenticate))
		{
			IniFile.WriteFile("settings.ini");
			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(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...");
		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 %ld ms!", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count());
		#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
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
		}

		if (m_TerminateEventRaised)
		{
			m_bStop = true;
		}

		// Stop the server:
		m_WebAdmin->Stop();
		LOG("Shutting down server...");
		m_Server->Shutdown();
		LOGD("Shutting down deadlock detector...");<commit_msg>Fixed print of milliseconds from chrono<commit_after>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Root.h"
#include "Server.h"
#include "World.h"
#include "WebAdmin.h"
#include "FurnaceRecipe.h"
#include "CraftingRecipes.h"
#include "Bindings/PluginManager.h"
#include "MonsterConfig.h"
#include "Entities/Player.h"
#include "Blocks/BlockHandler.h"
#include "Items/ItemHandler.h"
#include "Chunk.h"
#include "Protocol/ProtocolRecognizer.h"  // for protocol version constants
#include "CommandOutput.h"
#include "DeadlockDetect.h"
#include "LoggerListeners.h"
#include "BuildInfo.h"
#include "IniFile.h"

#ifdef _WIN32
	#include <conio.h>
	#include <psapi.h>
#elif defined(__linux__)
	#include <fstream>
#elif defined(__APPLE__)
	#include <mach/mach.h>
#endif





cRoot* cRoot::s_Root = nullptr;





cRoot::cRoot(void) :
	m_pDefaultWorld(nullptr),
	m_Server(nullptr),
	m_MonsterConfig(nullptr),
	m_CraftingRecipes(nullptr),
	m_FurnaceRecipe(nullptr),
	m_WebAdmin(nullptr),
	m_PluginManager(nullptr),
	m_bStop(false),
	m_bRestart(false)
{
	s_Root = this;
}





cRoot::~cRoot()
{
	s_Root = 0;
}





void cRoot::InputThread(cRoot & a_Params)
{
	cLogCommandOutputCallback Output;
	
	while (!a_Params.m_bStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good())
	{
		AString Command;
		std::getline(std::cin, Command);
		if (!Command.empty())
		{
			a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
		}
	}

	if (m_TerminateEventRaised || !std::cin.good())
	{
		// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server:
		a_Params.m_bStop = true;
	}
}





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)
	{
		auto BeginTime = std::chrono::steady_clock::now();
		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");
		}

		LOG("Starting server...");
		bool ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
		m_MojangAPI.Start(IniFile, 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(IniFile, ShouldAuthenticate))
		{
			IniFile.WriteFile("settings.ini");
			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(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...");
		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 %ld ms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
		#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
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
		}

		if (m_TerminateEventRaised)
		{
			m_bStop = true;
		}

		// Stop the server:
		m_WebAdmin->Stop();
		LOG("Shutting down server...");
		m_Server->Shutdown();
		LOGD("Shutting down deadlock detector...");
{'fraction_non_alphanumeric': 0.14253778055977823, 'fraction_numerical': 0.001788428865241885, 'mean_word_length': 4.85243328100471, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5672385', 'n_tokens_mistral': 4219, 'n_tokens_neox': 3855, 'n_words': 843}