2017-09-19 04:34:08 -04:00
2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 18:09:57 -04:00
# include "Root.h"
2016-11-18 14:00:04 -05:00
// STD lib hreaders:
# include <iostream>
// OS-specific headers:
# if defined(_WIN32)
# include <psapi.h>
# elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
# include <signal.h>
# if defined(__linux__)
# include <fstream>
# elif defined(__APPLE__)
# include <mach/mach.h>
# endif
# endif
2012-09-23 18:09:57 -04:00
# include "Server.h"
# include "World.h"
# include "WebAdmin.h"
2015-09-24 04:48:33 -04:00
# include "BrewingRecipes.h"
2012-09-23 18:09:57 -04:00
# include "FurnaceRecipe.h"
2012-06-14 09:06:06 -04:00
# include "CraftingRecipes.h"
2020-07-14 12:56:42 -04:00
# include "Protocol/RecipeMapper.h"
2013-12-08 06:17:54 -05:00
# include "Bindings/PluginManager.h"
2012-09-23 18:09:57 -04:00
# include "MonsterConfig.h"
2013-08-19 05:39:13 -04:00
# include "Entities/Player.h"
2012-09-29 09:59:32 -04:00
# include "Blocks/BlockHandler.h"
# include "Items/ItemHandler.h"
2012-09-23 18:09:57 -04:00
# include "Chunk.h"
2012-10-31 15:54:42 -04:00
# include "Protocol/ProtocolRecognizer.h" // for protocol version constants
2013-06-29 11:30:05 -04:00
# include "CommandOutput.h"
2013-08-14 16:39:12 -04:00
# include "DeadlockDetect.h"
2014-08-12 11:05:04 -04:00
# include "LoggerListeners.h"
2014-09-10 11:07:00 -04:00
# include "BuildInfo.h"
2014-10-23 09:15:10 -04:00
# include "IniFile.h"
2015-05-14 10:47:51 -04:00
# include "OverridesSettingsRepository.h"
2015-08-30 17:57:43 -04:00
# include "Logger.h"
2016-11-18 14:00:04 -05:00
# include "ClientHandle.h"
2020-01-02 09:11:49 -05:00
# include "BlockTypePalette.h"
2012-06-14 09:06:06 -04:00
2020-07-22 19:34:43 -04:00
extern bool g_RunAsService ;
decltype ( cRoot : : s_Root ) cRoot : : s_Root ;
decltype ( cRoot : : s_NextState ) cRoot : : s_NextState ;
decltype ( cRoot : : s_StopEvent ) cRoot : : s_StopEvent ;
2012-06-14 09:06:06 -04:00
2013-12-20 13:10:07 -05:00
cRoot : : cRoot ( void ) :
2014-10-20 16:55:07 -04:00
m_pDefaultWorld ( nullptr ) ,
m_Server ( nullptr ) ,
m_MonsterConfig ( nullptr ) ,
m_CraftingRecipes ( nullptr ) ,
m_FurnaceRecipe ( nullptr ) ,
2015-09-24 04:48:33 -04:00
m_BrewingRecipes ( nullptr ) ,
2014-10-20 16:55:07 -04:00
m_WebAdmin ( nullptr ) ,
m_PluginManager ( nullptr ) ,
2015-06-17 10:38:00 -04:00
m_MojangAPI ( nullptr )
2012-06-14 09:06:06 -04:00
{
2019-08-04 19:43:54 -04:00
Temporary : : RegisterAllBlockHandlers ( m_BlockTypeRegistry ) ;
2012-06-14 09:06:06 -04:00
s_Root = this ;
2020-07-22 19:34:43 -04:00
TransitionNextState ( NextState : : Run ) ;
2012-06-14 09:06:06 -04:00
}
cRoot : : ~ cRoot ( )
{
2017-12-21 06:36:58 -05:00
s_Root = nullptr ;
2012-06-14 09:06:06 -04:00
}
2020-07-22 19:34:43 -04:00
bool cRoot : : Run ( cSettingsRepositoryInterface & a_OverridesRepo )
2012-06-14 09:06:06 -04:00
{
2020-07-22 19:34:43 -04:00
auto consoleLogListener = MakeConsoleListener ( g_RunAsService ) ;
2015-08-30 17:57:43 -04:00
auto consoleAttachment = cLogger : : GetInstance ( ) . AttachListener ( std : : move ( consoleLogListener ) ) ;
2017-04-01 11:28:42 -04:00
cLogger : : cAttachment fileAttachment ;
2020-07-22 19:34:43 -04:00
if ( ! a_OverridesRepo . HasValue ( " Server " , " DisableLogFile " ) )
2015-08-30 17:57:43 -04:00
{
2017-04-01 11:28:42 -04:00
auto fileLogListenerRet = MakeFileListener ( ) ;
if ( ! fileLogListenerRet . first )
{
2020-07-22 19:34:43 -04:00
throw std : : runtime_error ( " failed to open log file " ) ;
2017-04-01 11:28:42 -04:00
}
fileAttachment = cLogger : : GetInstance ( ) . AttachListener ( std : : move ( fileLogListenerRet . second ) ) ;
2015-08-30 17:57:43 -04:00
}
2015-05-25 23:36:46 -04:00
2015-06-11 16:20:04 -04:00
LOG ( " --- Started Log --- " ) ;
2013-12-22 15:03:58 -05:00
2020-07-22 19:34:43 -04:00
# ifdef BUILD_ID
LOG ( " Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID " ) " ) ;
LOG ( " from commit " BUILD_COMMIT_ID " built at: " BUILD_DATETIME ) ;
# endif
2015-06-11 16:20:04 -04:00
2013-08-14 16:39:12 -04:00
cDeadlockDetect dd ;
2015-06-17 10:38:00 -04:00
auto BeginTime = std : : chrono : : steady_clock : : now ( ) ;
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
LoadGlobalSettings ( ) ;
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
LOG ( " Creating new server instance... " ) ;
m_Server = new cServer ( ) ;
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
LOG ( " Reading server config... " ) ;
2015-05-14 10:47:51 -04:00
2017-02-22 08:10:32 -05:00
m_SettingsFilename = " settings.ini " ;
2020-07-22 19:34:43 -04:00
if ( a_OverridesRepo . HasValue ( " Server " , " ConfigFile " ) )
2017-02-22 08:10:32 -05:00
{
2020-07-22 19:34:43 -04:00
m_SettingsFilename = a_OverridesRepo . GetValue ( " Server " , " ConfigFile " ) ;
2017-02-22 08:10:32 -05:00
}
2020-08-01 14:18:03 -04:00
auto IniFile = std : : make_unique < cIniFile > ( ) ;
2017-02-22 08:10:32 -05:00
bool IsNewIniFile = ! IniFile - > ReadFile ( m_SettingsFilename ) ;
2015-12-24 09:53:36 -05:00
if ( IsNewIniFile )
2015-06-17 10:38:00 -04:00
{
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 " ) ;
}
2015-12-24 09:53:36 -05:00
2020-07-22 19:34:43 -04:00
auto settingsRepo = std : : make_unique < cOverridesSettingsRepository > ( std : : move ( IniFile ) , a_OverridesRepo ) ;
2013-11-04 16:51:24 -05:00
2015-06-17 10:38:00 -04:00
LOG ( " Starting server... " ) ;
2016-11-06 13:30:19 -05:00
2017-01-01 08:47:17 -05:00
// cClientHandle::FASTBREAK_PERCENTAGE = settingsRepo->GetValueSetI("AntiCheat", "FastBreakPercentage", 97) / 100.0f;
cClientHandle : : FASTBREAK_PERCENTAGE = 0 ; // AntiCheat disabled due to bugs. We will enabled it once they are fixed. See #3506.
2016-11-06 13:30:19 -05:00
2015-06-17 10:38:00 -04:00
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 ( ) ;
2020-07-22 19:34:43 -04:00
throw std : : runtime_error ( " failure starting server " ) ;
2015-06-17 10:38:00 -04:00
}
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
m_WebAdmin = new cWebAdmin ( ) ;
m_WebAdmin - > Init ( ) ;
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
LOGD ( " Loading settings... " ) ;
m_RankManager . reset ( new cRankManager ( ) ) ;
m_RankManager - > Initialize ( * m_MojangAPI ) ;
m_CraftingRecipes = new cCraftingRecipes ( ) ;
2020-07-14 12:56:42 -04:00
m_RecipeMapper . reset ( new cRecipeMapper ( ) ) ;
2015-06-17 10:38:00 -04:00
m_FurnaceRecipe = new cFurnaceRecipe ( ) ;
2015-09-24 04:48:33 -04:00
m_BrewingRecipes . reset ( new cBrewingRecipes ( ) ) ;
2015-05-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
LOGD ( " Loading worlds... " ) ;
2017-09-22 11:55:42 -04:00
LoadWorlds ( dd , * settingsRepo , IsNewIniFile ) ;
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
LOGD ( " Loading plugin manager... " ) ;
2017-01-17 16:38:04 -05:00
m_PluginManager = new cPluginManager ( dd ) ;
2015-06-17 10:38:00 -04:00
m_PluginManager - > ReloadPluginsNow ( * settingsRepo ) ;
2015-05-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
LOGD ( " Loading MonsterConfig... " ) ;
m_MonsterConfig = new cMonsterConfig ;
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
// This sets stuff in motion
LOGD ( " Starting Authenticator... " ) ;
m_Authenticator . Start ( * settingsRepo ) ;
2015-05-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
LOGD ( " Starting worlds... " ) ;
2017-01-17 16:38:04 -05:00
StartWorlds ( dd ) ;
2015-05-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
if ( settingsRepo - > GetValueSetB ( " DeadlockDetect " , " Enabled " , true ) )
{
LOGD ( " Starting deadlock detector... " ) ;
dd . Start ( settingsRepo - > GetValueSetI ( " DeadlockDetect " , " IntervalSec " , 20 ) ) ;
}
2015-05-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
settingsRepo - > Flush ( ) ;
2013-11-30 16:14:47 -05:00
2015-06-17 10:38:00 -04:00
LOGD ( " Finalising startup... " ) ;
if ( m_Server - > Start ( ) )
{
m_WebAdmin - > Start ( ) ;
2015-01-27 07:57:35 -05:00
2015-06-17 10:38:00 -04: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-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
// Save the current time
m_StartTime = std : : chrono : : steady_clock : : now ( ) ;
2015-05-25 23:36:46 -04:00
2020-07-22 19:34:43 -04:00
HandleInput ( ) ;
s_StopEvent . Wait ( ) ;
2015-01-27 07:57:35 -05:00
2015-06-17 10:38:00 -04:00
// Stop the server:
m_WebAdmin - > Stop ( ) ;
2015-01-27 07:57:35 -05:00
2015-06-17 10:38:00 -04:00
LOG ( " Shutting down server... " ) ;
m_Server - > Shutdown ( ) ;
} // if (m_Server->Start()
delete m_MojangAPI ; m_MojangAPI = nullptr ;
2013-12-22 15:03:58 -05:00
2015-06-17 10:38:00 -04:00
LOGD ( " Shutting down deadlock detector... " ) ;
dd . Stop ( ) ;
2014-12-21 09:31:20 -05:00
2015-06-17 10:38:00 -04:00
LOGD ( " Stopping world threads... " ) ;
2017-01-17 16:38:04 -05:00
StopWorlds ( dd ) ;
2014-12-21 09:31:20 -05:00
2015-06-17 10:38:00 -04:00
LOGD ( " Stopping authenticator... " ) ;
m_Authenticator . Stop ( ) ;
2014-12-21 09:31:20 -05:00
2015-06-17 10:38:00 -04:00
LOGD ( " Freeing MonsterConfig... " ) ;
delete m_MonsterConfig ; m_MonsterConfig = nullptr ;
delete m_WebAdmin ; m_WebAdmin = nullptr ;
2012-07-15 16:36:34 -04:00
2015-06-17 10:38:00 -04:00
LOGD ( " Unloading recipes... " ) ;
delete m_FurnaceRecipe ; m_FurnaceRecipe = nullptr ;
delete m_CraftingRecipes ; m_CraftingRecipes = nullptr ;
2014-12-21 09:31:20 -05:00
2015-06-17 10:38:00 -04:00
LOGD ( " Stopping plugin manager... " ) ;
delete m_PluginManager ; m_PluginManager = nullptr ;
2015-05-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
cItemHandler : : Deinit ( ) ;
2013-02-05 14:57:22 -05:00
2015-06-17 10:38:00 -04:00
LOG ( " Cleaning up... " ) ;
delete m_Server ; m_Server = nullptr ;
2020-07-22 19:34:43 -04:00
LOG ( " Shutdown successful! " ) ;
2014-08-10 14:34:11 -04:00
LOG ( " --- Stopped Log --- " ) ;
2020-07-22 19:34:43 -04:00
return s_NextState = = NextState : : Restart ;
2012-06-14 09:06:06 -04:00
}
2020-07-22 19:34:43 -04:00
void cRoot : : Stop ( )
2016-02-04 09:06:37 -05:00
{
2020-07-22 19:34:43 -04:00
TransitionNextState ( NextState : : Stop ) ;
}
2017-09-11 17:20:49 -04:00
2020-07-22 19:34:43 -04:00
void cRoot : : Restart ( )
{
TransitionNextState ( NextState : : Restart ) ;
2016-02-04 09:06:37 -05:00
}
2012-06-14 09:06:06 -04:00
void cRoot : : LoadGlobalSettings ( )
{
2012-10-06 16:04:58 -04:00
// Nothing needed yet
2012-06-14 09:06:06 -04:00
}
2017-09-22 11:55:42 -04:00
void cRoot : : LoadWorlds ( cDeadlockDetect & a_dd , cSettingsRepositoryInterface & a_Settings , bool a_IsNewIniFile )
2012-06-14 09:06:06 -04:00
{
2015-12-24 09:53:36 -05:00
if ( a_IsNewIniFile )
{
2016-02-08 05:26:21 -05:00
a_Settings . AddValue ( " Worlds " , " DefaultWorld " , " world " ) ;
2015-12-24 09:53:36 -05:00
a_Settings . AddValue ( " Worlds " , " World " , " world_nether " ) ;
2017-02-24 04:02:16 -05:00
a_Settings . AddValue ( " Worlds " , " World " , " world_the_end " ) ;
2017-09-07 08:41:16 -04:00
a_Settings . AddValue ( " WorldPaths " , " world " , " world " ) ;
a_Settings . AddValue ( " WorldPaths " , " world_nether " , " world_nether " ) ;
a_Settings . AddValue ( " WorldPaths " , " world_the_end " , " world_the_end " ) ;
2017-09-22 11:55:42 -04:00
2020-07-14 12:57:30 -04:00
const AStringVector WorldNames { " world " , " world_nether " , " world_the_end " } ;
m_pDefaultWorld = & m_WorldsByName . try_emplace ( " world " , " world " , " world " , a_dd , WorldNames ) . first - > second ;
m_WorldsByName . try_emplace ( " world_nether " , " world_nether " , " world_nether " , a_dd , WorldNames , dimNether , " world " ) ;
m_WorldsByName . try_emplace ( " world_the_end " , " world_the_end " , " world_the_end " , a_dd , WorldNames , dimEnd , " world " ) ;
2015-12-24 09:53:36 -05:00
return ;
}
2017-09-22 11:55:42 -04:00
// Build a list of all world names
auto Worlds = a_Settings . GetValues ( " Worlds " ) ;
AStringVector WorldNames ( Worlds . size ( ) ) ;
for ( const auto & World : Worlds )
{
WorldNames . push_back ( World . second ) ;
}
// Get the default world
2015-12-25 07:25:20 -05:00
AString DefaultWorldName = a_Settings . GetValueSet ( " Worlds " , " DefaultWorld " , " world " ) ;
2017-09-07 08:41:16 -04:00
AString DefaultWorldPath = a_Settings . GetValueSet ( " WorldPaths " , DefaultWorldName , DefaultWorldName ) ;
2020-07-14 12:57:30 -04:00
m_pDefaultWorld = & m_WorldsByName . try_emplace ( DefaultWorldName , DefaultWorldName , DefaultWorldPath , a_dd , WorldNames ) . first - > second ;
2015-12-25 07:25:20 -05:00
2012-06-14 09:06:06 -04:00
// Then load the other worlds
2015-05-14 10:47:51 -04:00
if ( Worlds . size ( ) < = 0 )
2012-06-14 09:06:06 -04:00
{
return ;
}
2015-05-25 23:36:46 -04:00
2016-02-03 16:37:35 -05:00
/* Here are the world creation rules. Note that these only apply for a world which is in settings.ini but has no world.ini file.
If an ini file is present , it overrides the world linkages and the dimension type in cWorld : : start ( )
The creation rules are as follows :
- If a world exists in settings . ini but has no world . ini , then :
- If the world name is x_nether , create a world . ini with the dimension type " nether " .
- If a world called x exists , set it as x_nether ' s overworld .
- Otherwise set the default world as x_nether ' s overworld .
2017-02-24 04:02:16 -05:00
- If the world name is x_the_end or x_end , create a world . ini with the dimension type " end " .
- If a world called x exists , set it as x_the_end ' s overworld .
- Otherwise set the default world as x_the_end ' s overworld .
2016-02-03 16:37:35 -05:00
2017-02-24 04:02:16 -05:00
- If the world name is x ( and doesn ' t end with _the_end , _end or _nether )
2016-02-03 16:37:35 -05:00
- Create a world . ini with a dimension type of " overworld " .
- If a world called x_nether exists , set it as x ' s nether world .
- Otherwise set x ' s nether world to blank . h
2017-02-24 04:02:16 -05:00
- If a world called x_the_end or x_end exists , set it as x ' s end world .
2016-02-03 16:37:35 -05:00
- Otherwise set x ' s nether world to blank .
*/
2013-11-04 16:51:24 -05:00
bool FoundAdditionalWorlds = false ;
2020-05-14 18:15:35 -04:00
for ( const auto & WorldNameValue : Worlds )
2012-06-14 09:06:06 -04:00
{
2015-05-14 10:47:51 -04:00
AString ValueName = WorldNameValue . first ;
2012-06-14 09:06:06 -04:00
if ( ValueName . compare ( " World " ) ! = 0 )
{
continue ;
}
2015-05-14 10:47:51 -04:00
AString WorldName = WorldNameValue . second ;
2012-06-14 09:06:06 -04:00
if ( WorldName . empty ( ) )
{
continue ;
}
2013-11-04 16:51:24 -05:00
FoundAdditionalWorlds = true ;
2016-02-03 16:37:35 -05:00
AString LowercaseName = StrToLower ( WorldName ) ;
2017-09-07 08:41:16 -04:00
AString WorldPath = a_Settings . GetValueSet ( " WorldPaths " , WorldName , WorldName ) ;
2017-02-24 04:02:16 -05:00
AString NetherAppend = " _nether " ;
AString EndAppend1 = " _the_end " ;
AString EndAppend2 = " _end " ;
2016-02-03 16:37:35 -05:00
2017-09-07 08:41:16 -04:00
// The default world is an overworld with no links
eDimension Dimension = dimOverworld ;
2020-05-14 18:15:35 -04:00
AString LinkTo ;
2017-09-07 08:41:16 -04:00
2016-02-03 16:37:35 -05:00
// if the world is called x_nether
if ( ( LowercaseName . size ( ) > NetherAppend . size ( ) ) & & ( LowercaseName . substr ( LowercaseName . size ( ) - NetherAppend . size ( ) ) = = NetherAppend ) )
{
// The world is called x_nether, see if a world called x exists. If yes, choose it as the linked world,
// otherwise, choose the default world as the linked world.
// As before, any ini settings will completely override this if an ini is already present.
2017-09-07 08:41:16 -04:00
LinkTo = WorldName . substr ( 0 , WorldName . size ( ) - NetherAppend . size ( ) ) ;
2016-02-03 16:37:35 -05:00
if ( GetWorld ( LinkTo ) = = nullptr )
{
LinkTo = DefaultWorldName ;
}
2017-09-07 08:41:16 -04:00
Dimension = dimNether ;
2016-02-03 16:37:35 -05:00
}
2017-02-24 04:02:16 -05:00
// if the world is called x_the_end
else if ( ( LowercaseName . size ( ) > EndAppend1 . size ( ) ) & & ( LowercaseName . substr ( LowercaseName . size ( ) - EndAppend1 . size ( ) ) = = EndAppend1 ) )
{
// The world is called x_the_end, see if a world called x exists. If yes, choose it as the linked world,
// otherwise, choose the default world as the linked world.
// As before, any ini settings will completely override this if an ini is already present.
2017-09-07 08:41:16 -04:00
LinkTo = WorldName . substr ( 0 , WorldName . size ( ) - EndAppend1 . size ( ) ) ;
2017-02-24 04:02:16 -05:00
if ( GetWorld ( LinkTo ) = = nullptr )
{
LinkTo = DefaultWorldName ;
}
2017-09-07 08:41:16 -04:00
Dimension = dimEnd ;
2017-02-24 04:02:16 -05:00
}
2016-02-03 16:37:35 -05:00
// if the world is called x_end
2017-02-24 04:02:16 -05:00
else if ( ( LowercaseName . size ( ) > EndAppend2 . size ( ) ) & & ( LowercaseName . substr ( LowercaseName . size ( ) - EndAppend2 . size ( ) ) = = EndAppend2 ) )
2016-02-03 16:37:35 -05:00
{
// The world is called x_end, see if a world called x exists. If yes, choose it as the linked world,
// otherwise, choose the default world as the linked world.
// As before, any ini settings will completely override this if an ini is already present.
2017-09-07 08:41:16 -04:00
LinkTo = WorldName . substr ( 0 , WorldName . size ( ) - EndAppend2 . size ( ) ) ;
2016-02-03 16:37:35 -05:00
if ( GetWorld ( LinkTo ) = = nullptr )
{
LinkTo = DefaultWorldName ;
}
2017-09-07 08:41:16 -04:00
Dimension = dimEnd ;
2016-02-03 16:37:35 -05:00
}
2020-07-14 12:57:30 -04:00
m_WorldsByName . try_emplace ( WorldName , WorldName , WorldPath , a_dd , WorldNames , Dimension , LinkTo ) ;
2012-06-14 09:06:06 -04:00
} // for i - Worlds
2013-11-04 16:51:24 -05:00
if ( ! FoundAdditionalWorlds )
{
2015-05-14 10:47:51 -04:00
if ( a_Settings . GetKeyComment ( " Worlds " , 0 ) ! = " World=secondworld " )
2013-11-07 17:33:46 -05:00
{
2015-05-14 10:47:51 -04:00
a_Settings . DeleteKeyComment ( " Worlds " , 0 ) ;
a_Settings . AddKeyComment ( " Worlds " , " World=secondworld " ) ;
2013-11-07 17:33:46 -05:00
}
2013-11-04 16:51:24 -05:00
}
2012-06-14 09:06:06 -04:00
}
2017-01-17 16:38:04 -05:00
void cRoot : : StartWorlds ( cDeadlockDetect & a_DeadlockDetect )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
auto & World = Entry . second ;
World . Start ( ) ;
World . InitializeSpawn ( ) ;
m_PluginManager - > CallHookWorldStarted ( World ) ;
2012-06-14 09:06:06 -04:00
}
}
2017-01-17 16:38:04 -05:00
void cRoot : : StopWorlds ( cDeadlockDetect & a_DeadlockDetect )
2012-07-15 16:07:38 -04:00
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
Entry . second . Stop ( a_DeadlockDetect ) ;
2012-06-14 09:06:06 -04:00
}
}
2014-05-31 17:28:51 -04:00
cWorld * cRoot : : GetDefaultWorld ( )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
ASSERT ( m_pDefaultWorld ! = nullptr ) ;
2012-08-22 07:22:26 -04:00
return m_pDefaultWorld ;
2012-06-14 09:06:06 -04:00
}
2016-02-08 05:06:14 -05:00
cWorld * cRoot : : GetWorld ( const AString & a_WorldName )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
const auto FindResult = m_WorldsByName . find ( a_WorldName ) ;
if ( FindResult ! = m_WorldsByName . cend ( ) )
2014-05-31 17:28:51 -04:00
{
2020-07-14 12:57:30 -04:00
return & FindResult - > second ;
2014-05-31 17:28:51 -04:00
}
2014-07-20 05:46:45 -04:00
2014-10-20 16:55:07 -04:00
return nullptr ;
2012-06-14 09:06:06 -04:00
}
2017-09-11 17:20:49 -04:00
bool cRoot : : ForEachWorld ( cWorldListCallback a_Callback )
2012-06-14 09:06:06 -04:00
{
2017-09-11 17:20:49 -04:00
for ( auto & World : m_WorldsByName )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
if ( a_Callback ( World . second ) )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
return false ;
2012-06-14 09:06:06 -04:00
}
}
return true ;
}
2013-06-29 11:30:05 -04:00
void cRoot : : QueueExecuteConsoleCommand ( const AString & a_Cmd , cCommandOutputCallback & a_Output )
{
2020-07-22 19:34:43 -04:00
const auto KickPlayers = [ this ]
{
// Kick all players from the server with custom disconnect message
2013-04-27 17:05:34 -04:00
2020-07-22 19:34:43 -04:00
bool SentDisconnect = false ;
cRoot : : Get ( ) - > ForEachPlayer (
[ & ] ( cPlayer & a_Player )
{
a_Player . GetClientHandlePtr ( ) - > Kick ( m_Server - > GetShutdownMessage ( ) ) ;
SentDisconnect = true ;
return false ;
}
) ;
2013-04-27 17:05:34 -04:00
2020-07-22 19:34:43 -04:00
if ( SentDisconnect )
{
std : : this_thread : : sleep_for ( std : : chrono : : seconds ( 1 ) ) ;
}
} ;
2013-04-27 17:05:34 -04:00
2015-06-17 10:38:00 -04:00
// Some commands are built-in:
2013-06-29 11:30:05 -04:00
if ( a_Cmd = = " stop " )
{
2020-07-22 19:34:43 -04:00
KickPlayers ( ) ;
cRoot : : Stop ( ) ;
2014-08-29 09:43:49 -04:00
return ;
2013-06-29 11:30:05 -04:00
}
else if ( a_Cmd = = " restart " )
{
2020-07-22 19:34:43 -04:00
KickPlayers ( ) ;
cRoot : : Restart ( ) ;
2014-08-29 09:43:49 -04:00
return ;
2013-06-29 11:30:05 -04:00
}
2013-02-15 08:00:59 -05:00
LOG ( " Executing console command: \" %s \" " , a_Cmd . c_str ( ) ) ;
2013-06-29 11:30:05 -04:00
m_Server - > ExecuteConsoleCommand ( a_Cmd , a_Output ) ;
2012-06-14 09:06:06 -04:00
}
2020-07-22 19:34:43 -04:00
void cRoot : : QueueExecuteConsoleCommand ( const AString & a_Cmd )
{
// Put the command into a queue (Alleviates FS #363):
QueueExecuteConsoleCommand ( a_Cmd , * new cLogCommandDeleteSelfOutputCallback ) ;
}
2012-06-14 09:06:06 -04:00
void cRoot : : KickUser ( int a_ClientID , const AString & a_Reason )
{
m_Server - > KickUser ( a_ClientID , a_Reason ) ;
}
2017-08-25 08:43:18 -04:00
void cRoot : : AuthenticateUser ( int a_ClientID , const AString & a_Name , const cUUID & a_UUID , const Json : : Value & a_Properties )
2012-06-14 09:06:06 -04:00
{
2014-07-14 14:49:31 -04:00
m_Server - > AuthenticateUser ( a_ClientID , a_Name , a_UUID , a_Properties ) ;
2012-06-14 09:06:06 -04:00
}
2020-07-14 12:57:30 -04:00
size_t cRoot : : GetTotalChunkCount ( void )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
size_t Count = 0 ;
for ( const auto & Entry : m_WorldsByName )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
Count + = Entry . second . GetNumChunks ( ) ;
2012-06-14 09:06:06 -04:00
}
2020-07-14 12:57:30 -04:00
return Count ;
2012-06-14 09:06:06 -04:00
}
void cRoot : : SaveAllChunks ( void )
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
Entry . second . QueueSaveAllChunks ( ) ;
2012-06-14 09:06:06 -04:00
}
}
2017-09-07 08:41:16 -04:00
void cRoot : : SetSavingEnabled ( bool a_SavingEnabled )
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2017-09-07 08:41:16 -04:00
{
2020-07-14 12:57:30 -04:00
Entry . second . SetSavingEnabled ( a_SavingEnabled ) ;
2017-09-07 08:41:16 -04:00
}
}
2015-05-28 16:17:21 -04:00
void cRoot : : SendPlayerLists ( cPlayer * a_DestPlayer )
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2015-05-28 16:17:21 -04:00
{
2020-07-14 12:57:30 -04:00
Entry . second . SendPlayerList ( a_DestPlayer ) ;
}
2015-05-28 16:17:21 -04:00
}
2018-04-02 13:39:54 -04:00
2015-05-28 16:17:21 -04:00
void cRoot : : BroadcastPlayerListsAddPlayer ( const cPlayer & a_Player , const cClientHandle * a_Exclude )
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2015-05-28 16:17:21 -04:00
{
2020-07-14 12:57:30 -04:00
Entry . second . BroadcastPlayerListAddPlayer ( a_Player ) ;
}
2015-05-28 16:17:21 -04:00
}
2012-06-14 09:06:06 -04:00
2018-04-02 13:39:54 -04:00
void cRoot : : BroadcastPlayerListsRemovePlayer ( const cPlayer & a_Player , const cClientHandle * a_Exclude )
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2018-04-02 13:39:54 -04:00
{
2020-07-14 12:57:30 -04:00
Entry . second . BroadcastPlayerListRemovePlayer ( a_Player ) ;
}
2018-04-02 13:39:54 -04:00
}
2014-02-15 17:26:19 -05:00
void cRoot : : BroadcastChat ( const AString & a_Message , eMessageType a_ChatPrefix )
2013-08-17 17:58:37 -04:00
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2013-08-17 17:58:37 -04:00
{
2020-07-14 12:57:30 -04:00
Entry . second . BroadcastChat ( a_Message , nullptr , a_ChatPrefix ) ;
}
2014-02-15 17:16:44 -05:00
}
void cRoot : : BroadcastChat ( const cCompositeChat & a_Message )
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2014-02-15 17:16:44 -05:00
{
2020-07-14 12:57:30 -04:00
Entry . second . BroadcastChat ( a_Message ) ;
}
2013-08-17 17:58:37 -04:00
}
2018-04-02 13:39:54 -04:00
2017-09-11 17:20:49 -04:00
bool cRoot : : ForEachPlayer ( cPlayerListCallback a_Callback )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2012-06-14 09:06:06 -04:00
{
2020-07-14 12:57:30 -04:00
if ( ! Entry . second . ForEachPlayer ( a_Callback ) )
2012-06-14 09:06:06 -04:00
{
return false ;
}
}
return true ;
}
2012-08-15 08:08:11 -04:00
2017-09-11 17:20:49 -04:00
bool cRoot : : FindAndDoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback a_Callback )
2012-08-22 19:05:12 -04:00
{
2017-09-11 17:20:49 -04:00
class cCallback
2012-08-22 19:05:12 -04:00
{
2014-05-08 14:16:35 -04:00
size_t m_BestRating ;
size_t m_NameLength ;
2013-12-20 13:10:07 -05:00
const AString m_PlayerName ;
2012-08-22 19:05:12 -04:00
2017-09-11 17:20:49 -04:00
public :
bool operator ( ) ( cPlayer & a_Player )
2012-08-22 19:05:12 -04:00
{
2017-09-11 17:20:49 -04:00
size_t Rating = RateCompareString ( m_PlayerName , a_Player . GetName ( ) ) ;
2013-12-20 13:10:07 -05:00
if ( ( Rating > 0 ) & & ( Rating > = m_BestRating ) )
2012-08-22 19:05:12 -04:00
{
2017-09-11 17:20:49 -04:00
m_BestMatch = a_Player . GetName ( ) ;
2013-12-20 13:10:07 -05:00
if ( Rating > m_BestRating )
{
m_NumMatches = 0 ;
}
m_BestRating = Rating ;
+ + m_NumMatches ;
2012-08-22 19:05:12 -04:00
}
2020-05-14 18:15:35 -04:00
return ( Rating = = m_NameLength ) ; // Perfect match
2012-08-22 19:05:12 -04:00
}
2016-01-06 10:20:12 -05:00
cCallback ( const AString & a_CBPlayerName ) :
2013-12-20 13:10:07 -05:00
m_BestRating ( 0 ) ,
2016-01-06 10:20:12 -05:00
m_NameLength ( a_CBPlayerName . length ( ) ) ,
m_PlayerName ( a_CBPlayerName ) ,
2015-04-24 07:45:44 -04:00
m_BestMatch ( ) ,
2013-12-20 13:10:07 -05:00
m_NumMatches ( 0 )
2012-08-22 19:05:12 -04:00
{ }
2015-04-24 07:45:44 -04:00
AString m_BestMatch ;
2013-12-20 13:10:07 -05:00
unsigned m_NumMatches ;
2014-03-08 11:33:38 -05:00
} Callback ( a_PlayerName ) ;
2014-05-08 14:16:35 -04:00
ForEachPlayer ( Callback ) ;
2012-08-22 19:05:12 -04:00
2013-12-20 13:10:07 -05:00
if ( Callback . m_NumMatches = = 1 )
2012-08-22 19:05:12 -04:00
{
2015-04-24 07:45:44 -04:00
return DoWithPlayer ( Callback . m_BestMatch , a_Callback ) ;
2012-08-22 19:05:12 -04:00
}
return false ;
}
2017-09-11 17:20:49 -04:00
bool cRoot : : DoWithPlayerByUUID ( const cUUID & a_PlayerUUID , cPlayerListCallback a_Callback )
2014-11-12 15:59:42 -05:00
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2014-11-02 15:01:23 -05:00
{
2020-07-14 12:57:30 -04:00
if ( Entry . second . DoWithPlayerByUUID ( a_PlayerUUID , a_Callback ) )
2014-11-02 15:01:23 -05:00
{
return true ;
}
}
return false ;
}
2017-09-11 17:20:49 -04:00
bool cRoot : : DoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback a_Callback )
2014-12-08 03:12:48 -05:00
{
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2014-12-08 03:57:46 -05:00
{
2020-07-14 12:57:30 -04:00
if ( Entry . second . DoWithPlayer ( a_PlayerName , a_Callback ) )
2014-12-08 03:57:46 -05:00
{
return true ;
}
}
return false ;
2014-12-08 03:12:48 -05:00
}
2013-02-15 08:00:59 -05:00
AString cRoot : : GetProtocolVersionTextFromInt ( int a_ProtocolVersion )
{
2020-07-18 13:57:23 -04:00
return cMultiVersionProtocol : : GetVersionTextFromInt ( static_cast < cProtocol : : Version > ( a_ProtocolVersion ) ) ;
2013-02-15 08:00:59 -05:00
}
2013-10-08 14:12:34 -04: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__)
2015-12-19 09:30:32 -05:00
// Code adapted from https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
2013-10-08 14:12:34 -04:00
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__)
2015-12-19 09:30:32 -05:00
// Code adapted from https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
2013-10-08 14:12:34 -04:00
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-04 18:24:59 -04:00
reinterpret_cast < task_info_t > ( & t_info ) ,
2013-10-08 14:12:34 -04:00
& t_info_count
) )
{
2015-07-29 11:04:03 -04:00
return static_cast < int > ( t_info . virtual_size / 1024 ) ;
2013-10-08 14:12:34 -04: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__)
2015-12-19 09:30:32 -05:00
// Code adapted from https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
2013-10-08 14:12:34 -04:00
std : : ifstream StatFile ( " /proc/self/status " ) ;
if ( ! StatFile . good ( ) )
{
return - 1 ;
}
while ( StatFile . good ( ) )
{
AString Line ;
std : : getline ( StatFile , Line ) ;
2014-01-01 04:18:56 -05:00
if ( strncmp ( Line . c_str ( ) , " VmRSS: " , 6 ) = = 0 )
2013-10-08 14:12:34 -04:00
{
2014-01-01 04:18:56 -05:00
int res = atoi ( Line . c_str ( ) + 7 ) ;
2013-10-08 14:12:34 -04:00
return ( res = = 0 ) ? - 1 : res ; // If parsing failed, return -1
}
}
return - 1 ;
# elif defined (__APPLE__)
2015-12-19 09:30:32 -05:00
// Code adapted from https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
2013-10-08 14:12:34 -04:00
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-04 18:24:59 -04:00
reinterpret_cast < task_info_t > ( & t_info ) ,
2013-10-08 14:12:34 -04:00
& t_info_count
) )
{
2015-07-29 11:04:03 -04:00
return static_cast < int > ( t_info . resident_size / 1024 ) ;
2013-10-08 14:12:34 -04:00
}
return - 1 ;
# else
LOGINFO ( " %s: Unknown platform, cannot query memory usage " , __FUNCTION__ ) ;
return - 1 ;
# endif
}
2013-06-29 11:30:05 -04:00
void cRoot : : LogChunkStats ( cCommandOutputCallback & a_Output )
2012-08-15 08:08:11 -04:00
{
int SumNumValid = 0 ;
int SumNumDirty = 0 ;
int SumNumInLighting = 0 ;
int SumNumInGenerator = 0 ;
int SumMem = 0 ;
2020-07-14 12:57:30 -04:00
for ( auto & Entry : m_WorldsByName )
2012-08-15 08:08:11 -04:00
{
2020-07-14 12:57:30 -04:00
auto & World = Entry . second ;
const auto NumInGenerator = World . GetGeneratorQueueLength ( ) ;
const auto NumInSaveQueue = World . GetStorageSaveQueueLength ( ) ;
const auto NumInLoadQueue = World . GetStorageLoadQueueLength ( ) ;
2012-08-15 08:08:11 -04:00
int NumValid = 0 ;
int NumDirty = 0 ;
int NumInLighting = 0 ;
2020-07-14 12:57:30 -04:00
World . GetChunkStats ( NumValid , NumDirty , NumInLighting ) ;
a_Output . Out ( " World %s: " , World . GetName ( ) . c_str ( ) ) ;
2013-06-29 11:30:05 -04:00
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 ) ;
2020-07-14 12:57:30 -04:00
a_Output . Out ( " Num chunks in generator queue: %zu " , NumInGenerator ) ;
a_Output . Out ( " Num chunks in storage load queue: %zu " , NumInLoadQueue ) ;
a_Output . Out ( " Num chunks in storage save queue: %zu " , NumInSaveQueue ) ;
2015-07-29 11:04:03 -04:00
int Mem = NumValid * static_cast < int > ( sizeof ( cChunk ) ) ;
2013-06-29 11:30:05 -04: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: " ) ;
2018-01-03 12:41:16 -05:00
a_Output . Out ( " block types: %6zu bytes (%3zu KiB) " , sizeof ( cChunkDef : : BlockTypes ) , ( sizeof ( cChunkDef : : BlockTypes ) + 1023 ) / 1024 ) ;
a_Output . Out ( " block metadata: %6zu bytes (%3zu KiB) " , sizeof ( cChunkDef : : BlockNibbles ) , ( sizeof ( cChunkDef : : BlockNibbles ) + 1023 ) / 1024 ) ;
a_Output . Out ( " block lighting: %6zu bytes (%3zu KiB) " , 2 * sizeof ( cChunkDef : : BlockNibbles ) , ( 2 * sizeof ( cChunkDef : : BlockNibbles ) + 1023 ) / 1024 ) ;
a_Output . Out ( " heightmap: %6zu bytes (%3zu KiB) " , sizeof ( cChunkDef : : HeightMap ) , ( sizeof ( cChunkDef : : HeightMap ) + 1023 ) / 1024 ) ;
a_Output . Out ( " biomemap: %6zu bytes (%3zu KiB) " , sizeof ( cChunkDef : : BiomeMap ) , ( sizeof ( cChunkDef : : BiomeMap ) + 1023 ) / 1024 ) ;
2012-08-15 08:08:11 -04:00
SumNumValid + = NumValid ;
SumNumDirty + = NumDirty ;
SumNumInLighting + = NumInLighting ;
SumNumInGenerator + = NumInGenerator ;
SumMem + = Mem ;
}
2013-06-29 11:30:05 -04: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 08:08:11 -04:00
}
2013-11-22 10:50:03 -05:00
int cRoot : : GetFurnaceFuelBurnTime ( const cItem & a_Fuel )
{
cFurnaceRecipe * FR = Get ( ) - > GetFurnaceRecipe ( ) ;
return FR - > GetBurnTime ( a_Fuel ) ;
}
2016-07-21 07:00:30 -04:00
AStringVector cRoot : : GetPlayerTabCompletionMultiWorld ( const AString & a_Text )
{
AStringVector Results ;
2017-09-11 17:20:49 -04:00
ForEachWorld ( [ & ] ( cWorld & a_World )
2016-07-21 07:00:30 -04:00
{
2017-09-11 17:20:49 -04:00
a_World . TabCompleteUserName ( a_Text , Results ) ;
2016-07-29 14:51:33 -04:00
return false ;
2016-07-21 07:00:30 -04:00
}
2017-09-11 17:20:49 -04:00
) ;
2016-07-21 07:00:30 -04:00
return Results ;
}
2020-07-22 19:34:43 -04:00
void cRoot : : HandleInput ( )
{
if ( g_RunAsService )
{
// Ignore input when running as a service, cin was never opened in that case:
return ;
}
cLogCommandOutputCallback Output ;
AString Command ;
while ( s_NextState = = NextState : : Run )
{
# ifndef _WIN32
timeval Timeout { 0 , 0 } ;
Timeout . tv_usec = 100 * 1000 ; // 100 msec
fd_set ReadSet ;
FD_ZERO ( & ReadSet ) ;
FD_SET ( STDIN_FILENO , & ReadSet ) ;
if ( select ( STDIN_FILENO + 1 , & ReadSet , nullptr , nullptr , & Timeout ) < = 0 )
{
// Don't call getline because there's nothing to read
continue ;
}
# endif
if ( ! std : : getline ( std : : cin , Command ) )
{
cRoot : : Stop ( ) ;
return ;
}
if ( s_NextState ! = NextState : : Run )
{
// Already shutting down, can't execute commands
break ;
}
if ( ! Command . empty ( ) )
{
// Execute and clear command string when submitted
QueueExecuteConsoleCommand ( TrimString ( Command ) , Output ) ;
}
}
}
void cRoot : : TransitionNextState ( NextState a_NextState )
{
{
auto Current = s_NextState . load ( ) ;
do
{
// Stopping is final, so stops override restarts:
if ( Current = = NextState : : Stop )
{
return ;
}
}
while ( ! s_NextState . compare_exchange_strong ( Current , a_NextState ) ) ;
}
if ( s_NextState = = NextState : : Run )
{
return ;
}
s_StopEvent . Set ( ) ;
# 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 apart from CancelIoEx, but xoft wants Windows XP support
2020-09-05 09:23:54 -04:00
// Only thing I can think of for now. Also, ignore the retval since sometimes there's no cin.
WriteConsoleInput ( GetStdHandle ( STD_INPUT_HANDLE ) , & Record , 1 , & Length ) ;
2020-07-22 19:34:43 -04:00
# endif
}