2012-06-14 13:06:06 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 22:09:57 +00:00
#include "Root.h"
#include "Server.h"
#include "World.h"
#include "WebAdmin.h"
2015-09-24 10:48:33 +02:00
#include "BrewingRecipes.h"
2012-09-23 22:09:57 +00:00
#include "FurnaceRecipe.h"
2012-06-14 13:06:06 +00:00
#include "CraftingRecipes.h"
2013-12-08 12:17:54 +01:00
#include "Bindings/PluginManager.h"
2012-09-23 22:09:57 +00:00
#include "MonsterConfig.h"
2013-08-19 11:39:13 +02:00
#include "Entities/Player.h"
2012-09-29 13:59:32 +00:00
#include "Blocks/BlockHandler.h"
#include "Items/ItemHandler.h"
2012-09-23 22:09:57 +00:00
#include "Chunk.h"
2012-10-31 19:54:42 +00:00
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
2013-06-29 15:30:05 +00:00
#include "CommandOutput.h"
2013-08-14 22:39:12 +02:00
#include "DeadlockDetect.h"
2014-08-12 16:05:04 +01:00
#include "LoggerListeners.h"
2014-09-10 16:07:00 +01:00
#include "BuildInfo.h"
2014-10-23 15:15:10 +02:00
#include "IniFile.h"
2015-05-14 15:47:51 +01:00
#include "SettingsRepositoryInterface.h"
#include "OverridesSettingsRepository.h"
2015-06-11 22:20:04 +02:00
#include "SelfTests.h"
2015-08-30 22:57:43 +01:00
#include "Logger.h"
2012-06-14 13:06:06 +00:00
2015-05-09 01:22:33 +01:00
#include <iostream>
2015-08-23 09:13:53 +03:00
#if defined(_WIN32)
2013-10-08 20:12:34 +02:00
#include <psapi.h>
2015-08-23 09:13:53 +03:00
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
2015-06-17 15:38:00 +01:00
#include <signal.h>
2015-08-23 09:13:53 +03:00
#if defined(__linux__)
#include <fstream>
#elif defined(__APPLE__)
#include <mach/mach.h>
#endif
2013-10-08 20:12:34 +02:00
#endif
2012-06-14 13:06:06 +00:00
2015-04-05 17:07:10 +02:00
cRoot * cRoot :: s_Root = nullptr ;
2012-06-14 13:06:06 +00:00
2013-12-20 19:10:07 +01:00
cRoot :: cRoot ( void ) :
2014-10-20 21:55:07 +01:00
m_pDefaultWorld ( nullptr ),
m_Server ( nullptr ),
m_MonsterConfig ( nullptr ),
m_CraftingRecipes ( nullptr ),
m_FurnaceRecipe ( nullptr ),
2015-09-24 10:48:33 +02:00
m_BrewingRecipes ( nullptr ),
2014-10-20 21:55:07 +01:00
m_WebAdmin ( nullptr ),
m_PluginManager ( nullptr ),
2015-06-17 15:38:00 +01:00
m_MojangAPI ( nullptr )
2012-06-14 13:06:06 +00:00
{
s_Root = this ;
2015-06-17 15:38:00 +01:00
m_InputThreadRunFlag . clear ();
2012-06-14 13:06:06 +00:00
}
cRoot ::~ cRoot ()
{
s_Root = 0 ;
}
2014-10-19 14:10:18 +01:00
void cRoot :: InputThread ( cRoot & a_Params )
2012-06-14 13:06:06 +00:00
{
2013-06-29 15:30:05 +00:00
cLogCommandOutputCallback Output ;
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
while ( a_Params . m_InputThreadRunFlag . test_and_set () && std :: cin . good ())
2012-06-14 13:06:06 +00:00
{
2013-12-07 22:35:24 +00:00
AString Command ;
2012-06-14 13:06:06 +00:00
std :: getline ( std :: cin , Command );
2013-07-24 20:25:27 +00:00
if ( ! Command . empty ())
2014-07-17 16:33:09 +02:00
{
2015-06-17 15:38:00 +01:00
// Execute and clear command string when submitted
2014-10-19 14:10:18 +01:00
a_Params . ExecuteConsoleCommand ( TrimString ( Command ), Output );
2013-07-24 20:25:27 +00:00
}
}
2013-12-22 20:03:58 +00:00
2015-06-17 15:38:00 +01:00
// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running
if ( ! std :: cin . good ())
2013-07-24 20:25:27 +00:00
{
2015-04-05 17:07:10 +02:00
// Stop the server:
2015-06-17 15:38:00 +01:00
a_Params . QueueExecuteConsoleCommand ( "stop" );
2012-06-14 13:06:06 +00:00
}
}
2015-06-17 15:38:00 +01:00
void cRoot :: Start ( std :: unique_ptr < cSettingsRepositoryInterface > a_OverridesRepo )
2012-06-14 13:06:06 +00:00
{
2013-12-22 20:03:58 +00:00
#ifdef _WIN32
2015-08-22 14:21:25 +01:00
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
2013-12-22 20:03:58 +00:00
#endif
2015-05-26 05:36:46 +02:00
2015-08-30 22:57:43 +01:00
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 ));
2015-05-26 05:36:46 +02:00
2015-06-11 22:20:04 +02:00
LOG ( "--- Started Log ---" );
2013-12-22 20:03:58 +00:00
2014-09-10 16:07:00 +01:00
#ifdef BUILD_ID
2015-09-25 10:14:17 +02:00
LOG ( "Cuberite " BUILD_SERIES_NAME " build id: " BUILD_ID );
2015-06-11 22:20:04 +02:00
LOG ( "from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME );
#endif
// Run the self-tests registered previously via cSelfTests::Register():
2015-06-12 11:57:34 +01:00
#ifdef SELF_TEST
2015-06-11 22:20:04 +02:00
cSelfTests :: ExecuteAll ();
2014-09-10 16:07:00 +01:00
#endif
2013-08-14 22:39:12 +02:00
cDeadlockDetect dd ;
2015-06-17 15:38:00 +01:00
auto BeginTime = std :: chrono :: steady_clock :: now ();
2012-06-14 13:06:06 +00:00
2015-06-17 15:38:00 +01:00
LoadGlobalSettings ();
2012-06-14 13:06:06 +00:00
2015-06-17 15:38:00 +01:00
LOG ( "Creating new server instance..." );
m_Server = new cServer ();
2012-06-14 13:06:06 +00:00
2015-06-17 15:38:00 +01:00
LOG ( "Reading server config..." );
2015-05-14 15:47:51 +01:00
2015-06-17 15:38:00 +01:00
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 ( a_OverridesRepo ));
2013-11-04 21:51:24 +00:00
2015-06-17 15:38:00 +01:00
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 ;
}
2012-06-14 13:06:06 +00:00
2015-06-17 15:38:00 +01:00
m_WebAdmin = new cWebAdmin ();
m_WebAdmin -> Init ();
2012-06-14 13:06:06 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Loading settings..." );
m_RankManager . reset ( new cRankManager ());
m_RankManager -> Initialize ( * m_MojangAPI );
m_CraftingRecipes = new cCraftingRecipes ();
m_FurnaceRecipe = new cFurnaceRecipe ();
2015-09-24 10:48:33 +02:00
m_BrewingRecipes . reset ( new cBrewingRecipes ());
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
LOGD ( "Loading worlds..." );
LoadWorlds ( * settingsRepo );
2012-06-14 13:06:06 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Loading plugin manager..." );
m_PluginManager = new cPluginManager ();
m_PluginManager -> ReloadPluginsNow ( * settingsRepo );
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
LOGD ( "Loading MonsterConfig..." );
m_MonsterConfig = new cMonsterConfig ;
2012-06-14 13:06:06 +00:00
2015-06-17 15:38:00 +01:00
// This sets stuff in motion
LOGD ( "Starting Authenticator..." );
m_Authenticator . Start ( * settingsRepo );
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
LOGD ( "Starting worlds..." );
StartWorlds ();
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
if ( settingsRepo -> GetValueSetB ( "DeadlockDetect" , "Enabled" , true ))
{
LOGD ( "Starting deadlock detector..." );
dd . Start ( settingsRepo -> GetValueSetI ( "DeadlockDetect" , "IntervalSec" , 20 ));
}
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
settingsRepo -> Flush ();
2013-11-30 22:14:47 +01:00
2015-06-17 15:38:00 +01:00
LOGD ( "Finalising startup..." );
if ( m_Server -> Start ())
{
m_WebAdmin -> Start ();
2015-01-27 13:57:35 +01:00
2015-06-17 15:38:00 +01:00
#if !defined(ANDROID_NDK)
2015-01-27 13:57:35 +01:00
LOGD ( "Starting InputThread..." );
try
{
2015-06-17 15:38:00 +01:00
m_InputThreadRunFlag . test_and_set ();
2015-01-27 13:57:35 +01:00
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 ());
}
2015-06-17 15:38:00 +01:00
#endif
2015-01-27 13:57:35 +01:00
2015-06-17 15:38:00 +01:00
LOG ( "Startup complete, took %ldms!" , static_cast < long int > ( std :: chrono :: duration_cast < std :: chrono :: milliseconds > ( std :: chrono :: steady_clock :: now () - BeginTime ). count ()));
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
// Save the current time
m_StartTime = std :: chrono :: steady_clock :: now ();
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
#ifdef _WIN32
2015-08-22 14:21:25 +01:00
EnableMenuItem ( ConsoleMenu , SC_CLOSE , MF_ENABLED ); // Re-enable close button
2015-06-17 15:38:00 +01:00
#endif
2015-01-27 13:57:35 +01:00
2015-06-17 15:38:00 +01:00
for (;;)
{
m_StopEvent . Wait ();
if ( m_TerminateEventRaised && m_RunAsService )
2015-01-27 13:57:35 +01:00
{
2015-06-17 15:38:00 +01:00
// Dont kill if running as a service
m_TerminateEventRaised = false ;
2015-01-27 13:57:35 +01:00
}
2015-06-17 15:38:00 +01:00
else
2015-01-27 13:57:35 +01:00
{
2015-06-17 15:38:00 +01:00
break ;
2015-01-27 13:57:35 +01:00
}
2015-06-17 15:38:00 +01:00
}
2015-01-27 13:57:35 +01:00
2015-06-17 15:38:00 +01:00
// Stop the server:
m_WebAdmin -> Stop ();
2015-01-27 13:57:35 +01:00
2015-06-17 15:38:00 +01:00
LOG ( "Shutting down server..." );
m_Server -> Shutdown ();
} // if (m_Server->Start()
delete m_MojangAPI ; m_MojangAPI = nullptr ;
2013-12-22 20:03:58 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Shutting down deadlock detector..." );
dd . Stop ();
2014-12-21 14:31:20 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Stopping world threads..." );
StopWorlds ();
2014-12-21 14:31:20 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Stopping authenticator..." );
m_Authenticator . Stop ();
2014-12-21 14:31:20 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Freeing MonsterConfig..." );
delete m_MonsterConfig ; m_MonsterConfig = nullptr ;
delete m_WebAdmin ; m_WebAdmin = nullptr ;
2012-07-15 20:36:34 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Unloading recipes..." );
delete m_FurnaceRecipe ; m_FurnaceRecipe = nullptr ;
delete m_CraftingRecipes ; m_CraftingRecipes = nullptr ;
2014-12-21 14:31:20 +00:00
2015-06-17 15:38:00 +01:00
LOG ( "Unloading worlds..." );
UnloadWorlds ();
2014-12-21 14:31:20 +00:00
2015-06-17 15:38:00 +01:00
LOGD ( "Stopping plugin manager..." );
delete m_PluginManager ; m_PluginManager = nullptr ;
2015-05-26 05:36:46 +02:00
2015-06-17 15:38:00 +01:00
cItemHandler :: Deinit ();
2013-02-05 19:57:22 +00:00
2015-06-17 15:38:00 +01:00
LOG ( "Cleaning up..." );
delete m_Server ; m_Server = nullptr ;
m_InputThreadRunFlag . clear ();
#ifdef _WIN32
DWORD Length ;
INPUT_RECORD Record
{
2015-08-22 14:21:25 +01:00
KEY_EVENT ,
2015-06-17 15:38:00 +01:00
{
{
TRUE ,
1 ,
VK_RETURN ,
2015-08-22 14:21:25 +01:00
static_cast < WORD > ( MapVirtualKey ( VK_RETURN , MAPVK_VK_TO_VSC )),
2015-06-17 15:38:00 +01:00
{ { VK_RETURN } },
0
}
}
};
2012-07-15 20:36:34 +00:00
2015-06-17 15:38:00 +01:00
// 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
2014-12-21 14:31:20 +00:00
2015-06-17 15:38:00 +01:00
if ( m_TerminateEventRaised )
{
2013-09-28 20:36:01 +01:00
LOG ( "Shutdown successful!" );
2012-06-14 13:06:06 +00:00
}
2015-06-17 15:38:00 +01:00
else
{
LOG ( "Shutdown successful - restarting..." );
}
2014-08-10 19:34:11 +01:00
LOG ( "--- Stopped Log ---" );
2012-06-14 13:06:06 +00:00
}
void cRoot :: LoadGlobalSettings ()
{
2012-10-06 20:04:58 +00:00
// Nothing needed yet
2012-06-14 13:06:06 +00:00
}
2015-05-14 15:47:51 +01:00
void cRoot :: LoadWorlds ( cSettingsRepositoryInterface & a_Settings )
2012-06-14 13:06:06 +00:00
{
// First get the default world
2015-05-14 15:47:51 +01:00
AString DefaultWorldName = a_Settings . GetValueSet ( "Worlds" , "DefaultWorld" , "world" );
2014-07-27 00:39:39 +02:00
m_pDefaultWorld = new cWorld ( DefaultWorldName . c_str ());
2012-08-22 11:22:26 +00:00
m_WorldsByName [ DefaultWorldName ] = m_pDefaultWorld ;
2012-06-14 13:06:06 +00:00
// Then load the other worlds
2015-05-14 15:47:51 +01:00
auto Worlds = a_Settings . GetValues ( "Worlds" );
if ( Worlds . size () <= 0 )
2012-06-14 13:06:06 +00:00
{
return ;
}
2015-05-26 05:36:46 +02:00
2013-11-04 21:51:24 +00:00
bool FoundAdditionalWorlds = false ;
2015-05-14 15:47:51 +01:00
for ( auto WorldNameValue : Worlds )
2012-06-14 13:06:06 +00:00
{
2015-05-14 15:47:51 +01:00
AString ValueName = WorldNameValue . first ;
2012-06-14 13:06:06 +00:00
if ( ValueName . compare ( "World" ) != 0 )
{
continue ;
}
2015-05-14 15:47:51 +01:00
AString WorldName = WorldNameValue . second ;
2012-06-14 13:06:06 +00:00
if ( WorldName . empty ())
{
continue ;
}
2013-11-04 21:51:24 +00:00
FoundAdditionalWorlds = true ;
2015-05-09 11:16:56 +02:00
cWorld * NewWorld = new cWorld ( WorldName . c_str ());
m_WorldsByName [ WorldName ] = NewWorld ;
2012-06-14 13:06:06 +00:00
} // for i - Worlds
2013-11-04 21:51:24 +00:00
if ( ! FoundAdditionalWorlds )
{
2015-05-14 15:47:51 +01:00
if ( a_Settings . GetKeyComment ( "Worlds" , 0 ) != " World=secondworld" )
2013-11-07 22:33:46 +00:00
{
2015-05-14 15:47:51 +01:00
a_Settings . DeleteKeyComment ( "Worlds" , 0 );
a_Settings . AddKeyComment ( "Worlds" , " World=secondworld" );
2013-11-07 22:33:46 +00:00
}
2013-11-04 21:51:24 +00:00
}
2012-06-14 13:06:06 +00:00
}
2015-06-13 16:09:43 -05:00
cWorld * cRoot :: CreateAndInitializeWorld ( const AString & a_WorldName , eDimension a_Dimension , const AString & a_OverworldName , bool a_InitSpawn )
2013-12-11 12:39:13 +01:00
{
2014-07-22 17:26:48 +01:00
cWorld * World = m_WorldsByName [ a_WorldName ];
2014-10-20 21:55:07 +01:00
if ( World != nullptr )
2013-12-11 12:39:13 +01:00
{
2014-07-22 17:26:48 +01:00
return World ;
2013-12-11 12:39:13 +01:00
}
2014-07-22 17:26:48 +01:00
2014-06-10 20:43:27 +01:00
cWorld * NewWorld = new cWorld ( a_WorldName . c_str (), a_Dimension , a_OverworldName );
2013-12-11 12:39:13 +01:00
m_WorldsByName [ a_WorldName ] = NewWorld ;
NewWorld -> Start ();
2015-06-13 16:09:43 -05:00
if ( a_InitSpawn )
{
NewWorld -> InitializeSpawn ();
}
2013-12-11 16:19:38 +01:00
m_PluginManager -> CallHookWorldStarted ( * NewWorld );
2013-12-11 12:39:13 +01:00
return NewWorld ;
}
2012-06-14 13:06:06 +00:00
void cRoot :: StartWorlds ( void )
{
2013-08-11 20:16:41 +02:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (); itr != m_WorldsByName . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
2013-08-11 20:16:41 +02:00
itr -> second -> Start ();
2012-06-14 13:06:06 +00:00
itr -> second -> InitializeSpawn ();
2013-12-11 12:39:13 +01:00
m_PluginManager -> CallHookWorldStarted ( * itr -> second );
2012-06-14 13:06:06 +00:00
}
}
2012-07-15 20:07:38 +00:00
void cRoot :: StopWorlds ( void )
{
2013-08-11 20:16:41 +02:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (); itr != m_WorldsByName . end (); ++ itr )
2012-07-15 20:07:38 +00:00
{
2013-08-11 20:16:41 +02:00
itr -> second -> Stop ();
2012-07-15 20:07:38 +00:00
}
}
2012-06-14 13:06:06 +00:00
void cRoot :: UnloadWorlds ( void )
{
2014-10-20 21:55:07 +01:00
m_pDefaultWorld = nullptr ;
2014-07-21 15:19:48 +02:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (); itr != m_WorldsByName . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
delete itr -> second ;
}
2012-08-22 11:22:26 +00:00
m_WorldsByName . clear ();
2012-06-14 13:06:06 +00:00
}
2014-05-31 22:28:51 +01:00
cWorld * cRoot :: GetDefaultWorld ()
2012-06-14 13:06:06 +00:00
{
2012-08-22 11:22:26 +00:00
return m_pDefaultWorld ;
2012-06-14 13:06:06 +00:00
}
2014-07-20 10:46:45 +01:00
cWorld * cRoot :: GetWorld ( const AString & a_WorldName , bool a_SearchForFolder )
2012-06-14 13:06:06 +00:00
{
2014-05-31 22:28:51 +01:00
WorldMap :: iterator itr = m_WorldsByName . find ( a_WorldName );
2014-07-21 15:19:48 +02:00
if ( itr != m_WorldsByName . end ())
2014-05-31 22:28:51 +01:00
{
2012-06-14 13:06:06 +00:00
return itr -> second ;
2014-05-31 22:28:51 +01:00
}
2014-07-20 10:46:45 +01:00
if ( a_SearchForFolder && cFile :: IsFolder ( FILE_IO_PREFIX + a_WorldName ))
{
return CreateAndInitializeWorld ( a_WorldName );
}
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-06-14 13:06:06 +00:00
}
bool cRoot :: ForEachWorld ( cWorldListCallback & a_Callback )
{
2012-08-22 11:22:26 +00:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (), itr2 = itr ; itr != m_WorldsByName . end (); itr = itr2 )
2012-06-14 13:06:06 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2014-10-20 21:55:07 +01:00
if ( itr -> second != nullptr )
2012-06-14 13:06:06 +00:00
{
2014-05-31 22:28:51 +01:00
if ( a_Callback . Item ( itr -> second ))
{
return false ;
}
2012-06-14 13:06:06 +00:00
}
}
return true ;
}
2013-08-11 20:16:41 +02:00
void cRoot :: TickCommands ( void )
2012-06-14 13:06:06 +00:00
{
2013-04-27 21:05:34 +00:00
// Execute any pending commands:
2013-06-29 15:30:05 +00:00
cCommandQueue PendingCommands ;
2013-04-27 21:05:34 +00:00
{
cCSLock Lock ( m_CSPendingCommands );
std :: swap ( PendingCommands , m_PendingCommands );
}
2013-06-29 15:30:05 +00:00
for ( cCommandQueue :: iterator itr = PendingCommands . begin (), end = PendingCommands . end (); itr != end ; ++ itr )
2013-04-27 21:05:34 +00:00
{
2013-06-29 15:30:05 +00:00
ExecuteConsoleCommand ( itr -> m_Command , * ( itr -> m_Output ));
2013-04-27 21:05:34 +00:00
}
2012-06-14 13:06:06 +00:00
}
2013-06-29 15:30:05 +00:00
void cRoot :: QueueExecuteConsoleCommand ( const AString & a_Cmd , cCommandOutputCallback & a_Output )
{
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock ( m_CSPendingCommands );
2015-06-17 15:38:00 +01:00
m_PendingCommands . emplace_back ( a_Cmd , & a_Output );
2013-06-29 15:30:05 +00:00
}
void cRoot :: QueueExecuteConsoleCommand ( const AString & a_Cmd )
2013-04-27 21:05:34 +00:00
{
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock ( m_CSPendingCommands );
2013-06-29 15:30:05 +00:00
m_PendingCommands . push_back ( cCommand ( a_Cmd , new cLogCommandDeleteSelfOutputCallback ));
2013-04-27 21:05:34 +00:00
}
2013-06-29 15:30:05 +00:00
void cRoot :: ExecuteConsoleCommand ( const AString & a_Cmd , cCommandOutputCallback & a_Output )
2012-06-14 13:06:06 +00:00
{
2015-06-17 15:38:00 +01:00
// Some commands are built-in:
2013-06-29 15:30:05 +00:00
if ( a_Cmd == "stop" )
{
2015-06-17 15:38:00 +01:00
m_TerminateEventRaised = true ;
m_StopEvent . Set ();
m_InputThreadRunFlag . clear ();
2014-08-29 14:43:49 +01:00
return ;
2013-06-29 15:30:05 +00:00
}
else if ( a_Cmd == "restart" )
{
2015-06-17 15:38:00 +01:00
m_StopEvent . Set ();
m_InputThreadRunFlag . clear ();
2014-08-29 14:43:49 +01:00
return ;
2013-06-29 15:30:05 +00:00
}
2013-02-15 13:00:59 +00:00
LOG ( "Executing console command: \" %s \" " , a_Cmd . c_str ());
2013-06-29 15:30:05 +00:00
m_Server -> ExecuteConsoleCommand ( a_Cmd , a_Output );
2012-06-14 13:06:06 +00:00
}
void cRoot :: KickUser ( int a_ClientID , const AString & a_Reason )
{
m_Server -> KickUser ( a_ClientID , a_Reason );
}
2014-07-16 00:03:47 +01:00
void cRoot :: AuthenticateUser ( int a_ClientID , const AString & a_Name , const AString & a_UUID , const Json :: Value & a_Properties )
2012-06-14 13:06:06 +00:00
{
2014-07-14 19:49:31 +01:00
m_Server -> AuthenticateUser ( a_ClientID , a_Name , a_UUID , a_Properties );
2012-06-14 13:06:06 +00:00
}
int cRoot :: GetTotalChunkCount ( void )
{
int res = 0 ;
2014-07-21 15:19:48 +02:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (); itr != m_WorldsByName . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
res += itr -> second -> GetNumChunks ();
}
return res ;
}
void cRoot :: SaveAllChunks ( void )
{
2012-08-22 11:22:26 +00:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (); itr != m_WorldsByName . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
2013-11-15 10:13:32 +01:00
itr -> second -> QueueSaveAllChunks ();
2012-06-14 13:06:06 +00:00
}
}
2015-05-28 13:17:21 -07:00
void cRoot :: SendPlayerLists ( cPlayer * a_DestPlayer )
{
for ( const auto & itr : m_WorldsByName )
{
itr . second -> SendPlayerList ( a_DestPlayer );
} // for itr - m_WorldsByName[]
}
void cRoot :: BroadcastPlayerListsAddPlayer ( const cPlayer & a_Player , const cClientHandle * a_Exclude )
{
for ( const auto & itr : m_WorldsByName )
{
itr . second -> BroadcastPlayerListAddPlayer ( a_Player );
} // for itr - m_WorldsByName[]
}
2012-06-14 13:06:06 +00:00
2014-02-15 23:26:19 +01:00
void cRoot :: BroadcastChat ( const AString & a_Message , eMessageType a_ChatPrefix )
2013-08-17 23:58:37 +02:00
{
for ( WorldMap :: iterator itr = m_WorldsByName . begin (), end = m_WorldsByName . end (); itr != end ; ++ itr )
{
2014-10-20 21:55:07 +01:00
itr -> second -> BroadcastChat ( a_Message , nullptr , a_ChatPrefix );
2013-08-17 23:58:37 +02:00
} // for itr - m_WorldsByName[]
2014-02-15 23:16:44 +01:00
}
void cRoot :: BroadcastChat ( const cCompositeChat & a_Message )
{
for ( WorldMap :: iterator itr = m_WorldsByName . begin (), end = m_WorldsByName . end (); itr != end ; ++ itr )
{
itr -> second -> BroadcastChat ( a_Message );
} // for itr - m_WorldsByName[]
2013-08-17 23:58:37 +02:00
}
2012-06-14 13:06:06 +00:00
bool cRoot :: ForEachPlayer ( cPlayerListCallback & a_Callback )
{
2012-08-22 11:22:26 +00:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (), itr2 = itr ; itr != m_WorldsByName . end (); itr = itr2 )
2012-06-14 13:06:06 +00:00
{
2012-06-19 20:31:21 +00:00
++ itr2 ;
2012-06-14 13:06:06 +00:00
if ( ! itr -> second -> ForEachPlayer ( a_Callback ))
{
return false ;
}
}
return true ;
}
2012-08-15 12:08:11 +00:00
2012-08-22 23:05:12 +00:00
bool cRoot :: FindAndDoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback & a_Callback )
{
class cCallback : public cPlayerListCallback
{
2014-05-08 20:16:35 +02:00
size_t m_BestRating ;
size_t m_NameLength ;
2013-12-20 19:10:07 +01:00
const AString m_PlayerName ;
2012-08-22 23:05:12 +00:00
virtual bool Item ( cPlayer * a_pPlayer )
{
2014-05-08 20:16:35 +02:00
size_t Rating = RateCompareString ( m_PlayerName , a_pPlayer -> GetName ());
2013-12-20 19:10:07 +01:00
if (( Rating > 0 ) && ( Rating >= m_BestRating ))
2012-08-22 23:05:12 +00:00
{
2015-04-24 12:45:44 +01:00
m_BestMatch = a_pPlayer -> GetName ();
2013-12-20 19:10:07 +01:00
if ( Rating > m_BestRating )
{
m_NumMatches = 0 ;
}
m_BestRating = Rating ;
++ m_NumMatches ;
2012-08-22 23:05:12 +00:00
}
2014-07-17 19:13:23 +02:00
if ( Rating == m_NameLength ) // Perfect match
2012-08-22 23:05:12 +00:00
{
2013-11-10 22:58:14 +01:00
return true ;
2012-08-22 23:05:12 +00:00
}
2013-11-10 22:58:14 +01:00
return false ;
2012-08-22 23:05:12 +00:00
}
public :
2014-03-08 08:33:38 -08:00
cCallback ( const AString & a_PlayerName ) :
2013-12-20 19:10:07 +01:00
m_BestRating ( 0 ),
m_NameLength ( a_PlayerName . length ()),
m_PlayerName ( a_PlayerName ),
2015-04-24 12:45:44 +01:00
m_BestMatch (),
2013-12-20 19:10:07 +01:00
m_NumMatches ( 0 )
2012-08-22 23:05:12 +00:00
{}
2015-04-24 12:45:44 +01:00
AString m_BestMatch ;
2013-12-20 19:10:07 +01:00
unsigned m_NumMatches ;
2014-03-08 08:33:38 -08:00
} Callback ( a_PlayerName );
2014-05-08 20:16:35 +02:00
ForEachPlayer ( Callback );
2012-08-22 23:05:12 +00:00
2013-12-20 19:10:07 +01:00
if ( Callback . m_NumMatches == 1 )
2012-08-22 23:05:12 +00:00
{
2015-04-24 12:45:44 +01:00
return DoWithPlayer ( Callback . m_BestMatch , a_Callback );
2012-08-22 23:05:12 +00:00
}
return false ;
}
2014-11-05 21:57:38 +01:00
bool cRoot :: DoWithPlayerByUUID ( const AString & a_PlayerUUID , cPlayerListCallback & a_Callback )
2014-11-12 21:59:42 +01:00
{
2015-01-17 16:00:12 +03:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (); itr != m_WorldsByName . end (); ++ itr )
2014-11-02 21:01:23 +01:00
{
2014-11-05 21:57:38 +01:00
if ( itr -> second -> DoWithPlayerByUUID ( a_PlayerUUID , a_Callback ))
2014-11-02 21:01:23 +01:00
{
return true ;
}
}
return false ;
}
2014-12-08 00:45:29 -08:00
bool cRoot :: DoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback & a_Callback )
2014-12-08 00:12:48 -08:00
{
2014-12-08 15:58:46 -08:00
for ( auto World : m_WorldsByName )
2014-12-08 00:57:46 -08:00
{
2014-12-08 15:58:46 -08:00
if ( World . second -> DoWithPlayer ( a_PlayerName , a_Callback ))
2014-12-08 00:57:46 -08:00
{
return true ;
}
}
return false ;
2014-12-08 00:12:48 -08:00
}
2013-02-15 13:00:59 +00:00
AString cRoot :: GetProtocolVersionTextFromInt ( int a_ProtocolVersion )
{
return cProtocolRecognizer :: GetVersionTextFromInt ( a_ProtocolVersion );
}
2013-10-08 20:12:34 +02:00
int cRoot :: GetVirtualRAMUsage ( void )
{
#ifdef _WIN32
PROCESS_MEMORY_COUNTERS_EX pmc ;
if ( GetProcessMemoryInfo ( GetCurrentProcess (), ( PROCESS_MEMORY_COUNTERS * ) & pmc , sizeof ( pmc )))
{
return ( int )( pmc . PrivateUsage / 1024 );
}
return - 1 ;
#elif defined(__linux__)
// Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
std :: ifstream StatFile ( "/proc/self/status" );
if ( ! StatFile . good ())
{
return - 1 ;
}
while ( StatFile . good ())
{
AString Line ;
std :: getline ( StatFile , Line );
if ( strncmp ( Line . c_str (), "VmSize:" , 7 ) == 0 )
{
int res = atoi ( Line . c_str () + 8 );
return ( res == 0 ) ? - 1 : res ; // If parsing failed, return -1
}
}
return - 1 ;
#elif defined (__APPLE__)
// Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
struct task_basic_info t_info ;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT ;
if ( KERN_SUCCESS == task_info (
mach_task_self (),
TASK_BASIC_INFO ,
2015-08-05 01:24:59 +03:00
reinterpret_cast < task_info_t > ( & t_info ),
2013-10-08 20:12:34 +02:00
& t_info_count
))
{
2015-07-29 09:04:03 -06:00
return static_cast < int > ( t_info . virtual_size / 1024 );
2013-10-08 20:12:34 +02:00
}
return - 1 ;
#else
LOGINFO ( "%s: Unknown platform, cannot query memory usage" , __FUNCTION__ );
return - 1 ;
#endif
}
int cRoot :: GetPhysicalRAMUsage ( void )
{
#ifdef _WIN32
PROCESS_MEMORY_COUNTERS pmc ;
if ( GetProcessMemoryInfo ( GetCurrentProcess (), & pmc , sizeof ( pmc )))
{
return ( int )( pmc . WorkingSetSize / 1024 );
}
return - 1 ;
#elif defined(__linux__)
// Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
std :: ifstream StatFile ( "/proc/self/status" );
if ( ! StatFile . good ())
{
return - 1 ;
}
while ( StatFile . good ())
{
AString Line ;
std :: getline ( StatFile , Line );
2014-01-01 11:18:56 +02:00
if ( strncmp ( Line . c_str (), "VmRSS:" , 6 ) == 0 )
2013-10-08 20:12:34 +02:00
{
2014-01-01 11:18:56 +02:00
int res = atoi ( Line . c_str () + 7 );
2013-10-08 20:12:34 +02:00
return ( res == 0 ) ? - 1 : res ; // If parsing failed, return -1
}
}
return - 1 ;
#elif defined (__APPLE__)
// Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
struct task_basic_info t_info ;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT ;
if ( KERN_SUCCESS == task_info (
mach_task_self (),
TASK_BASIC_INFO ,
2015-08-05 01:24:59 +03:00
reinterpret_cast < task_info_t > ( & t_info ),
2013-10-08 20:12:34 +02:00
& t_info_count
))
{
2015-07-29 09:04:03 -06:00
return static_cast < int > ( t_info . resident_size / 1024 );
2013-10-08 20:12:34 +02:00
}
return - 1 ;
#else
LOGINFO ( "%s: Unknown platform, cannot query memory usage" , __FUNCTION__ );
return - 1 ;
#endif
}
2013-06-29 15:30:05 +00:00
void cRoot :: LogChunkStats ( cCommandOutputCallback & a_Output )
2012-08-15 12:08:11 +00:00
{
int SumNumValid = 0 ;
int SumNumDirty = 0 ;
int SumNumInLighting = 0 ;
int SumNumInGenerator = 0 ;
int SumMem = 0 ;
2012-08-22 11:22:26 +00:00
for ( WorldMap :: iterator itr = m_WorldsByName . begin (), end = m_WorldsByName . end (); itr != end ; ++ itr )
2012-08-15 12:08:11 +00:00
{
cWorld * World = itr -> second ;
int NumInGenerator = World -> GetGeneratorQueueLength ();
2015-07-29 09:04:03 -06:00
int NumInSaveQueue = static_cast < int > ( World -> GetStorageSaveQueueLength ());
int NumInLoadQueue = static_cast < int > ( World -> GetStorageLoadQueueLength ());
2012-08-15 12:08:11 +00:00
int NumValid = 0 ;
int NumDirty = 0 ;
int NumInLighting = 0 ;
World -> GetChunkStats ( NumValid , NumDirty , NumInLighting );
2013-06-29 15:30:05 +00:00
a_Output . Out ( "World %s:" , World -> GetName (). c_str ());
a_Output . Out ( " Num loaded chunks: %d" , NumValid );
a_Output . Out ( " Num dirty chunks: %d" , NumDirty );
a_Output . Out ( " Num chunks in lighting queue: %d" , NumInLighting );
a_Output . Out ( " Num chunks in generator queue: %d" , NumInGenerator );
a_Output . Out ( " Num chunks in storage load queue: %d" , NumInLoadQueue );
a_Output . Out ( " Num chunks in storage save queue: %d" , NumInSaveQueue );
2015-07-29 09:04:03 -06:00
int Mem = NumValid * static_cast < int > ( sizeof ( cChunk ));
2013-06-29 15:30:05 +00:00
a_Output . Out ( " Memory used by chunks: %d KiB (%d MiB)" , ( Mem + 1023 ) / 1024 , ( Mem + 1024 * 1024 - 1 ) / ( 1024 * 1024 ));
a_Output . Out ( " Per-chunk memory size breakdown:" );
2014-03-12 10:34:50 -07:00
a_Output . Out ( " block types: " SIZE_T_FMT_PRECISION ( 6 ) " bytes (" SIZE_T_FMT_PRECISION ( 3 ) " KiB)" , sizeof ( cChunkDef :: BlockTypes ), ( sizeof ( cChunkDef :: BlockTypes ) + 1023 ) / 1024 );
a_Output . Out ( " block metadata: " SIZE_T_FMT_PRECISION ( 6 ) " bytes (" SIZE_T_FMT_PRECISION ( 3 ) " KiB)" , sizeof ( cChunkDef :: BlockNibbles ), ( sizeof ( cChunkDef :: BlockNibbles ) + 1023 ) / 1024 );
a_Output . Out ( " block lighting: " SIZE_T_FMT_PRECISION ( 6 ) " bytes (" SIZE_T_FMT_PRECISION ( 3 ) " KiB)" , 2 * sizeof ( cChunkDef :: BlockNibbles ), ( 2 * sizeof ( cChunkDef :: BlockNibbles ) + 1023 ) / 1024 );
a_Output . Out ( " heightmap: " SIZE_T_FMT_PRECISION ( 6 ) " bytes (" SIZE_T_FMT_PRECISION ( 3 ) " KiB)" , sizeof ( cChunkDef :: HeightMap ), ( sizeof ( cChunkDef :: HeightMap ) + 1023 ) / 1024 );
a_Output . Out ( " biomemap: " SIZE_T_FMT_PRECISION ( 6 ) " bytes (" SIZE_T_FMT_PRECISION ( 3 ) " KiB)" , sizeof ( cChunkDef :: BiomeMap ), ( sizeof ( cChunkDef :: BiomeMap ) + 1023 ) / 1024 );
2012-08-15 12:08:11 +00:00
SumNumValid += NumValid ;
SumNumDirty += NumDirty ;
SumNumInLighting += NumInLighting ;
SumNumInGenerator += NumInGenerator ;
SumMem += Mem ;
}
2013-06-29 15:30:05 +00:00
a_Output . Out ( "Totals:" );
a_Output . Out ( " Num loaded chunks: %d" , SumNumValid );
a_Output . Out ( " Num dirty chunks: %d" , SumNumDirty );
a_Output . Out ( " Num chunks in lighting queue: %d" , SumNumInLighting );
a_Output . Out ( " Num chunks in generator queue: %d" , SumNumInGenerator );
a_Output . Out ( " Memory used by chunks: %d KiB (%d MiB)" , ( SumMem + 1023 ) / 1024 , ( SumMem + 1024 * 1024 - 1 ) / ( 1024 * 1024 ));
2012-08-15 12:08:11 +00:00
}
2013-11-22 16:50:03 +01:00
int cRoot :: GetFurnaceFuelBurnTime ( const cItem & a_Fuel )
{
cFurnaceRecipe * FR = Get () -> GetFurnaceRecipe ();
return FR -> GetBurnTime ( a_Fuel );
}