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"
# 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"
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 "SettingsRepositoryInterface.h"
# include "OverridesSettingsRepository.h"
2015-08-30 17:57:43 -04:00
# include "Logger.h"
2012-06-14 09:06:06 -04:00
2015-05-08 20:22:33 -04:00
# include <iostream>
2015-08-23 02:13:53 -04:00
# if defined(_WIN32)
2013-10-08 14:12:34 -04:00
# include <psapi.h>
2015-08-23 02:13:53 -04:00
# elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
2015-06-17 10:38:00 -04:00
# include <signal.h>
2015-08-23 02:13:53 -04:00
# if defined(__linux__)
# include <fstream>
# elif defined(__APPLE__)
# include <mach/mach.h>
# endif
2013-10-08 14:12:34 -04:00
# endif
2012-06-14 09:06:06 -04:00
2015-04-05 11:07:10 -04:00
cRoot * cRoot : : s_Root = nullptr ;
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
{
s_Root = this ;
2015-06-17 10:38:00 -04:00
m_InputThreadRunFlag . clear ( ) ;
2012-06-14 09:06:06 -04:00
}
cRoot : : ~ cRoot ( )
{
s_Root = 0 ;
}
2014-10-19 09:10:18 -04:00
void cRoot : : InputThread ( cRoot & a_Params )
2012-06-14 09:06:06 -04:00
{
2013-06-29 11:30:05 -04:00
cLogCommandOutputCallback Output ;
2015-05-25 23:36:46 -04:00
2015-06-17 10:38:00 -04:00
while ( a_Params . m_InputThreadRunFlag . test_and_set ( ) & & std : : cin . good ( ) )
2012-06-14 09:06:06 -04:00
{
2013-12-07 17:35:24 -05:00
AString Command ;
2012-06-14 09:06:06 -04:00
std : : getline ( std : : cin , Command ) ;
2013-07-24 16:25:27 -04:00
if ( ! Command . empty ( ) )
2014-07-17 10:33:09 -04:00
{
2015-06-17 10:38:00 -04:00
// Execute and clear command string when submitted
2014-10-19 09:10:18 -04:00
a_Params . ExecuteConsoleCommand ( TrimString ( Command ) , Output ) ;
2013-07-24 16:25:27 -04:00
}
}
2013-12-22 15:03:58 -05:00
2015-06-17 10:38:00 -04: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
2016-01-21 10:22:05 -05:00
// Ignore this when running as a service, cin was never opened in that case
if ( ! std : : cin . good ( ) & & ! m_RunAsService )
2013-07-24 16:25:27 -04:00
{
2015-04-05 11:07:10 -04:00
// Stop the server:
2015-06-17 10:38:00 -04:00
a_Params . QueueExecuteConsoleCommand ( " stop " ) ;
2012-06-14 09:06:06 -04:00
}
}
2015-06-17 10:38:00 -04:00
void cRoot : : Start ( std : : unique_ptr < cSettingsRepositoryInterface > a_OverridesRepo )
2012-06-14 09:06:06 -04:00
{
2013-12-22 15:03:58 -05:00
# ifdef _WIN32
2015-08-22 09:21:25 -04: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 15:03:58 -05:00
# endif
2015-05-25 23:36:46 -04:00
2015-08-30 17:57:43 -04: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-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
2014-09-10 11:07:00 -04:00
# ifdef BUILD_ID
2015-09-25 04:14:17 -04:00
LOG ( " Cuberite " BUILD_SERIES_NAME " build id: " BUILD_ID ) ;
2015-06-11 16:20:04 -04:00
LOG ( " from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME ) ;
# endif
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
2015-06-17 10:38:00 -04:00
auto IniFile = cpp14 : : make_unique < cIniFile > ( ) ;
2015-12-24 09:53:36 -05:00
bool IsNewIniFile = ! IniFile - > ReadFile ( " settings.ini " ) ;
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
2015-06-17 10:38:00 -04:00
auto settingsRepo = cpp14 : : make_unique < cOverridesSettingsRepository > ( std : : move ( IniFile ) , std : : move ( a_OverridesRepo ) ) ;
2013-11-04 16:51:24 -05:00
2015-06-17 10:38:00 -04: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 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 ( ) ;
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... " ) ;
2015-12-24 09:53:36 -05:00
LoadWorlds ( * settingsRepo , IsNewIniFile ) ;
2012-06-14 09:06:06 -04:00
2015-06-17 10:38:00 -04:00
LOGD ( " Loading plugin manager... " ) ;
m_PluginManager = new cPluginManager ( ) ;
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... " ) ;
StartWorlds ( ) ;
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
# if !defined(ANDROID_NDK)
2015-01-27 07:57:35 -05:00
LOGD ( " Starting InputThread... " ) ;
try
{
2015-06-17 10:38:00 -04:00
m_InputThreadRunFlag . test_and_set ( ) ;
2015-01-27 07:57:35 -05: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 10:38:00 -04:00
# endif
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
2015-06-17 10:38:00 -04:00
# ifdef _WIN32
2015-08-22 09:21:25 -04:00
EnableMenuItem ( ConsoleMenu , SC_CLOSE , MF_ENABLED ) ; // Re-enable close button
2015-06-17 10:38:00 -04:00
# endif
2015-01-27 07:57:35 -05:00
2015-06-17 10:38:00 -04:00
for ( ; ; )
{
m_StopEvent . Wait ( ) ;
2016-01-21 10:22:05 -05:00
break ;
2015-06-17 10:38:00 -04:00
}
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... " ) ;
StopWorlds ( ) ;
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
LOG ( " Unloading worlds... " ) ;
UnloadWorlds ( ) ;
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 ;
m_InputThreadRunFlag . clear ( ) ;
# ifdef _WIN32
DWORD Length ;
INPUT_RECORD Record
{
2015-08-22 09:21:25 -04:00
KEY_EVENT ,
2015-06-17 10:38:00 -04:00
{
{
TRUE ,
1 ,
VK_RETURN ,
2015-08-22 09:21:25 -04:00
static_cast < WORD > ( MapVirtualKey ( VK_RETURN , MAPVK_VK_TO_VSC ) ) ,
2015-06-17 10:38:00 -04:00
{ { VK_RETURN } } ,
0
}
}
} ;
2012-07-15 16:36:34 -04:00
2015-06-17 10:38:00 -04: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 09:31:20 -05:00
2015-06-17 10:38:00 -04:00
if ( m_TerminateEventRaised )
{
2013-09-28 15:36:01 -04:00
LOG ( " Shutdown successful! " ) ;
2012-06-14 09:06:06 -04:00
}
2015-06-17 10:38:00 -04:00
else
{
LOG ( " Shutdown successful - restarting... " ) ;
}
2014-08-10 14:34:11 -04:00
LOG ( " --- Stopped Log --- " ) ;
2012-06-14 09:06:06 -04:00
}
2016-02-04 09:06:37 -05:00
void cRoot : : StopServer ( )
{
m_TerminateEventRaised = true ;
m_StopEvent . Set ( ) ;
m_InputThreadRunFlag . clear ( ) ;
}
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
}
2015-12-24 09:53:36 -05:00
void cRoot : : LoadWorlds ( 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 " ) ;
a_Settings . AddValue ( " Worlds " , " World " , " world_end " ) ;
m_pDefaultWorld = new cWorld ( " world " ) ;
m_WorldsByName [ " world " ] = m_pDefaultWorld ;
m_WorldsByName [ " world_nether " ] = new cWorld ( " world_nether " , dimNether , " world " ) ;
m_WorldsByName [ " world_end " ] = new cWorld ( " world_end " , dimEnd , " world " ) ;
return ;
}
2016-02-08 05:26:21 -05:00
// First get the default world
2015-12-25 07:25:20 -05:00
AString DefaultWorldName = a_Settings . GetValueSet ( " Worlds " , " DefaultWorld " , " world " ) ;
2014-07-26 18:39:39 -04:00
m_pDefaultWorld = new cWorld ( DefaultWorldName . c_str ( ) ) ;
2012-08-22 07:22:26 -04:00
m_WorldsByName [ DefaultWorldName ] = m_pDefaultWorld ;
2015-12-25 07:25:20 -05:00
auto Worlds = a_Settings . GetValues ( " Worlds " ) ;
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 .
- If the world name is x_end , create a world . ini with the dimension type " end " .
- If a world called x exists , set it as x_end ' s overworld .
- Otherwise set the default world as x_end ' s overworld .
- If the world name is x ( and doesn ' t end with _end or _nether )
- 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
- If a world called x_end exists , set it as x ' s end world .
- Otherwise set x ' s nether world to blank .
*/
2013-11-04 16:51:24 -05:00
bool FoundAdditionalWorlds = false ;
2015-05-14 10:47:51 -04:00
for ( 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
cWorld * NewWorld ;
AString LowercaseName = StrToLower ( WorldName ) ;
AString NetherAppend = " _nether " ;
AString EndAppend = " _end " ;
// 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.
AString LinkTo = WorldName . substr ( 0 , WorldName . size ( ) - NetherAppend . size ( ) ) ;
if ( GetWorld ( LinkTo ) = = nullptr )
{
LinkTo = DefaultWorldName ;
}
NewWorld = new cWorld ( WorldName . c_str ( ) , dimNether , LinkTo ) ;
}
// if the world is called x_end
else if ( ( LowercaseName . size ( ) > EndAppend . size ( ) ) & & ( LowercaseName . substr ( LowercaseName . size ( ) - EndAppend . size ( ) ) = = EndAppend ) )
{
// 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.
AString LinkTo = WorldName . substr ( 0 , WorldName . size ( ) - EndAppend . size ( ) ) ;
if ( GetWorld ( LinkTo ) = = nullptr )
{
LinkTo = DefaultWorldName ;
}
NewWorld = new cWorld ( WorldName . c_str ( ) , dimEnd , LinkTo ) ;
}
else
{
NewWorld = new cWorld ( WorldName . c_str ( ) ) ;
}
2015-05-09 05:16:56 -04:00
m_WorldsByName [ WorldName ] = NewWorld ;
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
}
void cRoot : : StartWorlds ( void )
{
2013-08-11 14:16:41 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) ; itr ! = m_WorldsByName . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
2013-08-11 14:16:41 -04:00
itr - > second - > Start ( ) ;
2012-06-14 09:06:06 -04:00
itr - > second - > InitializeSpawn ( ) ;
2013-12-11 06:39:13 -05:00
m_PluginManager - > CallHookWorldStarted ( * itr - > second ) ;
2012-06-14 09:06:06 -04:00
}
}
2012-07-15 16:07:38 -04:00
void cRoot : : StopWorlds ( void )
{
2013-08-11 14:16:41 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) ; itr ! = m_WorldsByName . end ( ) ; + + itr )
2012-07-15 16:07:38 -04:00
{
2013-08-11 14:16:41 -04:00
itr - > second - > Stop ( ) ;
2012-07-15 16:07:38 -04:00
}
}
2012-06-14 09:06:06 -04:00
void cRoot : : UnloadWorlds ( void )
{
2014-10-20 16:55:07 -04:00
m_pDefaultWorld = nullptr ;
2014-07-21 09:19:48 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) ; itr ! = m_WorldsByName . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
delete itr - > second ;
}
2012-08-22 07:22:26 -04:00
m_WorldsByName . clear ( ) ;
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
{
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
{
2014-05-31 17:28:51 -04:00
WorldMap : : iterator itr = m_WorldsByName . find ( a_WorldName ) ;
2014-07-21 09:19:48 -04:00
if ( itr ! = m_WorldsByName . end ( ) )
2014-05-31 17:28:51 -04:00
{
2012-06-14 09:06:06 -04:00
return itr - > 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
}
bool cRoot : : ForEachWorld ( cWorldListCallback & a_Callback )
{
2012-08-22 07:22:26 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) , itr2 = itr ; itr ! = m_WorldsByName . end ( ) ; itr = itr2 )
2012-06-14 09:06:06 -04:00
{
2012-06-19 16:31:21 -04:00
+ + itr2 ;
2014-10-20 16:55:07 -04:00
if ( itr - > second ! = nullptr )
2012-06-14 09:06:06 -04:00
{
2014-05-31 17:28:51 -04:00
if ( a_Callback . Item ( itr - > second ) )
{
return false ;
}
2012-06-14 09:06:06 -04:00
}
}
return true ;
}
2013-08-11 14:16:41 -04:00
void cRoot : : TickCommands ( void )
2012-06-14 09:06:06 -04:00
{
2013-04-27 17:05:34 -04:00
// Execute any pending commands:
2013-06-29 11:30:05 -04:00
cCommandQueue PendingCommands ;
2013-04-27 17:05:34 -04:00
{
cCSLock Lock ( m_CSPendingCommands ) ;
std : : swap ( PendingCommands , m_PendingCommands ) ;
}
2013-06-29 11:30:05 -04:00
for ( cCommandQueue : : iterator itr = PendingCommands . begin ( ) , end = PendingCommands . end ( ) ; itr ! = end ; + + itr )
2013-04-27 17:05:34 -04:00
{
2013-06-29 11:30:05 -04:00
ExecuteConsoleCommand ( itr - > m_Command , * ( itr - > m_Output ) ) ;
2013-04-27 17:05:34 -04:00
}
2012-06-14 09:06:06 -04:00
}
2013-06-29 11:30:05 -04: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 10:38:00 -04:00
m_PendingCommands . emplace_back ( a_Cmd , & a_Output ) ;
2013-06-29 11:30:05 -04:00
}
void cRoot : : QueueExecuteConsoleCommand ( const AString & a_Cmd )
2013-04-27 17:05:34 -04:00
{
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock ( m_CSPendingCommands ) ;
2013-06-29 11:30:05 -04:00
m_PendingCommands . push_back ( cCommand ( a_Cmd , new cLogCommandDeleteSelfOutputCallback ) ) ;
2013-04-27 17:05:34 -04:00
}
2013-06-29 11:30:05 -04:00
void cRoot : : ExecuteConsoleCommand ( const AString & a_Cmd , cCommandOutputCallback & a_Output )
2012-06-14 09:06:06 -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 " )
{
2016-02-04 09:06:37 -05:00
StopServer ( ) ;
2014-08-29 09:43:49 -04:00
return ;
2013-06-29 11:30:05 -04:00
}
else if ( a_Cmd = = " restart " )
{
2015-06-17 10:38:00 -04:00
m_StopEvent . Set ( ) ;
m_InputThreadRunFlag . clear ( ) ;
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
}
void cRoot : : KickUser ( int a_ClientID , const AString & a_Reason )
{
m_Server - > KickUser ( a_ClientID , a_Reason ) ;
}
2014-07-15 19:03:47 -04:00
void cRoot : : AuthenticateUser ( int a_ClientID , const AString & a_Name , const AString & 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
}
int cRoot : : GetTotalChunkCount ( void )
{
int res = 0 ;
2014-07-21 09:19:48 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) ; itr ! = m_WorldsByName . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
res + = itr - > second - > GetNumChunks ( ) ;
}
return res ;
}
void cRoot : : SaveAllChunks ( void )
{
2012-08-22 07:22:26 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) ; itr ! = m_WorldsByName . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
2013-11-15 04:13:32 -05:00
itr - > second - > QueueSaveAllChunks ( ) ;
2012-06-14 09:06:06 -04:00
}
}
2015-05-28 16:17:21 -04: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 09:06:06 -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
{
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) , end = m_WorldsByName . end ( ) ; itr ! = end ; + + itr )
{
2014-10-20 16:55:07 -04:00
itr - > second - > BroadcastChat ( a_Message , nullptr , a_ChatPrefix ) ;
2013-08-17 17:58:37 -04:00
} // for itr - m_WorldsByName[]
2014-02-15 17:16:44 -05: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 17:58:37 -04:00
}
2012-06-14 09:06:06 -04:00
bool cRoot : : ForEachPlayer ( cPlayerListCallback & a_Callback )
{
2012-08-22 07:22:26 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) , itr2 = itr ; itr ! = m_WorldsByName . end ( ) ; itr = itr2 )
2012-06-14 09:06:06 -04:00
{
2012-06-19 16:31:21 -04:00
+ + itr2 ;
2012-06-14 09:06:06 -04:00
if ( ! itr - > second - > ForEachPlayer ( a_Callback ) )
{
return false ;
}
}
return true ;
}
2012-08-15 08:08:11 -04:00
2012-08-22 19:05:12 -04:00
bool cRoot : : FindAndDoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback & a_Callback )
{
class cCallback : public cPlayerListCallback
{
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
virtual bool Item ( cPlayer * a_pPlayer )
{
2014-05-08 14:16:35 -04:00
size_t Rating = RateCompareString ( m_PlayerName , a_pPlayer - > GetName ( ) ) ;
2013-12-20 13:10:07 -05:00
if ( ( Rating > 0 ) & & ( Rating > = m_BestRating ) )
2012-08-22 19:05:12 -04:00
{
2015-04-24 07:45:44 -04:00
m_BestMatch = a_pPlayer - > 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
}
2014-07-17 13:13:23 -04:00
if ( Rating = = m_NameLength ) // Perfect match
2012-08-22 19:05:12 -04:00
{
2013-11-10 16:58:14 -05:00
return true ;
2012-08-22 19:05:12 -04:00
}
2013-11-10 16:58:14 -05:00
return false ;
2012-08-22 19:05:12 -04:00
}
public :
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 ;
}
2014-11-05 15:57:38 -05:00
bool cRoot : : DoWithPlayerByUUID ( const AString & a_PlayerUUID , cPlayerListCallback & a_Callback )
2014-11-12 15:59:42 -05:00
{
2015-01-17 08:00:12 -05:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) ; itr ! = m_WorldsByName . end ( ) ; + + itr )
2014-11-02 15:01:23 -05:00
{
2014-11-05 15:57:38 -05:00
if ( itr - > second - > DoWithPlayerByUUID ( a_PlayerUUID , a_Callback ) )
2014-11-02 15:01:23 -05:00
{
return true ;
}
}
return false ;
}
2014-12-08 03:45:29 -05:00
bool cRoot : : DoWithPlayer ( const AString & a_PlayerName , cPlayerListCallback & a_Callback )
2014-12-08 03:12:48 -05:00
{
2014-12-08 18:58:46 -05:00
for ( auto World : m_WorldsByName )
2014-12-08 03:57:46 -05:00
{
2014-12-08 18:58:46 -05:00
if ( World . 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 )
{
return cProtocolRecognizer : : GetVersionTextFromInt ( a_ProtocolVersion ) ;
}
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 ;
2012-08-22 07:22:26 -04:00
for ( WorldMap : : iterator itr = m_WorldsByName . begin ( ) , end = m_WorldsByName . end ( ) ; itr ! = end ; + + itr )
2012-08-15 08:08:11 -04:00
{
cWorld * World = itr - > second ;
int NumInGenerator = World - > GetGeneratorQueueLength ( ) ;
2015-07-29 11:04:03 -04:00
int NumInSaveQueue = static_cast < int > ( World - > GetStorageSaveQueueLength ( ) ) ;
int NumInLoadQueue = static_cast < int > ( World - > GetStorageLoadQueueLength ( ) ) ;
2012-08-15 08:08:11 -04:00
int NumValid = 0 ;
int NumDirty = 0 ;
int NumInLighting = 0 ;
World - > GetChunkStats ( NumValid , NumDirty , NumInLighting ) ;
2013-06-29 11:30:05 -04: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 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: " ) ;
2014-03-12 13:34:50 -04: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 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 ;
class cWorldCallback : public cWorldListCallback
{
public :
cWorldCallback ( AStringVector & a_Results , const AString & a_Search ) :
m_Results ( a_Results ) ,
m_Search ( a_Search )
{
}
virtual bool Item ( cWorld * a_World ) override
{
a_World - > TabCompleteUserName ( m_Search , m_Results ) ;
2016-07-29 14:51:33 -04:00
return false ;
2016-07-21 07:00:30 -04:00
}
private :
AStringVector & m_Results ;
const AString & m_Search ;
} WC ( Results , a_Text ) ;
Get ( ) - > ForEachWorld ( WC ) ;
return Results ;
}