2012-06-14 09:06:06 -04:00
// ReDucTor is an awesome guy who helped me a lot
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 18:09:57 -04:00
# include "Server.h"
# include "ClientHandle.h"
2012-09-23 16:53:08 -04:00
# include "Mobs/Monster.h"
2012-09-23 18:09:57 -04:00
# include "Root.h"
# include "World.h"
2013-12-08 06:17:54 -05:00
# include "Bindings/PluginManager.h"
2012-09-23 18:09:57 -04:00
# include "ChatColor.h"
2013-08-19 05:39:13 -04:00
# include "Entities/Player.h"
2012-09-23 18:09:57 -04:00
# include "Inventory.h"
# include "Item.h"
# include "FurnaceRecipe.h"
# include "WebAdmin.h"
2012-09-23 16:03:26 -04:00
# include "Protocol/ProtocolRecognizer.h"
2013-06-29 11:30:05 -04:00
# include "CommandOutput.h"
2017-06-13 15:35:30 -04:00
# include "FastRandom.h"
2012-06-14 09:06:06 -04:00
2014-10-23 09:15:10 -04:00
# include "IniFile.h"
2012-06-14 09:06:06 -04:00
# include <fstream>
# include <sstream>
# include <iostream>
2014-07-19 09:23:40 -04:00
extern " C "
{
2013-11-27 02:40:59 -05:00
# include "zlib/zlib.h"
2012-06-14 09:06:06 -04:00
}
2012-10-19 16:51:46 -04:00
2012-06-14 09:06:06 -04:00
typedef std : : list < cClientHandle * > ClientList ;
2015-01-24 14:17:00 -05:00
////////////////////////////////////////////////////////////////////////////////
// cServerListenCallbacks:
class cServerListenCallbacks :
public cNetwork : : cListenCallbacks
{
cServer & m_Server ;
UInt16 m_Port ;
virtual cTCPLink : : cCallbacksPtr OnIncomingConnection ( const AString & a_RemoteIPAddress , UInt16 a_RemotePort ) override
{
return m_Server . OnConnectionAccepted ( a_RemoteIPAddress ) ;
}
virtual void OnAccepted ( cTCPLink & a_Link ) override { }
2015-05-23 07:56:08 -04:00
virtual void OnError ( int a_ErrorCode , const AString & a_ErrorMsg ) override
2015-01-24 14:17:00 -05:00
{
LOGWARNING ( " Cannot listen on port %d: %d (%s). " , m_Port , a_ErrorCode , a_ErrorMsg . c_str ( ) ) ;
}
public :
cServerListenCallbacks ( cServer & a_Server , UInt16 a_Port ) :
m_Server ( a_Server ) ,
m_Port ( a_Port )
{
}
} ;
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 13:46:27 -04:00
// cServer::cTickThread:
cServer : : cTickThread : : cTickThread ( cServer & a_Server ) :
super ( " ServerTickThread " ) ,
m_Server ( a_Server )
{
}
void cServer : : cTickThread : : Execute ( void )
2012-06-14 09:06:06 -04:00
{
2014-10-20 13:59:40 -04:00
auto LastTime = std : : chrono : : steady_clock : : now ( ) ;
static const auto msPerTick = std : : chrono : : milliseconds ( 50 ) ;
2013-08-11 13:46:27 -04:00
while ( ! m_ShouldTerminate )
{
2014-10-20 13:59:40 -04:00
auto NowTime = std : : chrono : : steady_clock : : now ( ) ;
2014-12-07 09:46:27 -05:00
auto msec = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( NowTime - LastTime ) . count ( ) ;
m_ShouldTerminate = ! m_Server . Tick ( static_cast < float > ( msec ) ) ;
2014-10-20 13:59:40 -04:00
auto TickTime = std : : chrono : : steady_clock : : now ( ) - NowTime ;
2015-04-29 12:24:14 -04:00
2013-08-11 13:46:27 -04:00
if ( TickTime < msPerTick )
{
// Stretch tick time until it's at least msPerTick
2014-10-21 09:17:36 -04:00
std : : this_thread : : sleep_for ( msPerTick - TickTime ) ;
2013-08-11 13:46:27 -04:00
}
2012-06-14 09:06:06 -04:00
2013-08-11 13:46:27 -04:00
LastTime = NowTime ;
}
}
2012-06-14 09:06:06 -04:00
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 13:18:06 -04:00
// cServer:
cServer : : cServer ( void ) :
2014-05-19 08:34:34 -04:00
m_PlayerCount ( 0 ) ,
m_ClientViewDistance ( 0 ) ,
2013-08-11 13:18:06 -04:00
m_bIsConnected ( false ) ,
m_bRestarting ( false ) ,
2013-08-11 13:46:27 -04:00
m_RCONServer ( * this ) ,
2014-05-19 08:34:34 -04:00
m_MaxPlayers ( 0 ) ,
m_bIsHardcore ( false ) ,
m_TickThread ( * this ) ,
2014-08-21 16:39:53 -04:00
m_ShouldAuthenticate ( false ) ,
m_ShouldLoadOfflinePlayerData ( false ) ,
m_ShouldLoadNamedPlayerData ( true )
2013-08-11 13:18:06 -04:00
{
2015-09-28 15:30:31 -04:00
// Initialize the LuaStateTracker singleton before the app goes multithreaded:
cLuaStateTracker : : GetStats ( ) ;
2013-08-11 13:18:06 -04:00
}
2013-08-12 02:35:13 -04:00
void cServer : : ClientMovedToWorld ( const cClientHandle * a_Client )
{
cCSLock Lock ( m_CSClients ) ;
2013-08-13 16:45:29 -04:00
m_ClientsToRemove . push_back ( const_cast < cClientHandle * > ( a_Client ) ) ;
2013-08-12 02:35:13 -04:00
}
2017-07-28 12:54:40 -04:00
void cServer : : PlayerCreated ( )
2013-08-14 04:24:34 -04:00
{
2017-07-28 12:54:40 -04:00
m_PlayerCount + + ;
2013-08-14 04:24:34 -04:00
}
2017-07-28 12:54:40 -04:00
void cServer : : PlayerDestroyed ( )
2013-08-14 04:24:34 -04:00
{
2017-07-28 12:54:40 -04:00
m_PlayerCount - - ;
2013-08-14 04:24:34 -04:00
}
2015-05-14 10:47:51 -04:00
bool cServer : : InitServer ( cSettingsRepositoryInterface & a_Settings , bool a_ShouldAuth )
2012-06-14 09:06:06 -04:00
{
2015-11-22 09:30:33 -05:00
m_Description = a_Settings . GetValueSet ( " Server " , " Description " , " Cuberite - in C++! " ) ;
2017-01-03 13:57:31 -05:00
m_ShutdownMessage = a_Settings . GetValueSet ( " Server " , " ShutdownMessage " , " Server shutdown " ) ;
2017-07-28 12:54:40 -04:00
m_MaxPlayers = static_cast < size_t > ( a_Settings . GetValueSetI ( " Server " , " MaxPlayers " , 100 ) ) ;
2015-05-14 10:47:51 -04:00
m_bIsHardcore = a_Settings . GetValueSetB ( " Server " , " HardcoreEnabled " , false ) ;
m_bAllowMultiLogin = a_Settings . GetValueSetB ( " Server " , " AllowMultiLogin " , false ) ;
2013-08-11 13:18:06 -04:00
2014-07-17 13:13:23 -04:00
m_FaviconData = Base64Encode ( cFile : : ReadWholeFile ( FILE_IO_PREFIX + AString ( " favicon.png " ) ) ) ; // Will return empty string if file nonexistant; client doesn't mind
2014-01-07 10:31:06 -05:00
2012-11-11 09:23:47 -05:00
if ( m_bIsConnected )
2012-06-14 09:06:06 -04:00
{
LOGERROR ( " ERROR: Trying to initialize server while server is already running! " ) ;
return false ;
}
2013-08-03 13:29:49 -04:00
LOGINFO ( " Compatible clients: %s " , MCS_CLIENT_VERSIONS ) ;
LOGINFO ( " Compatible protocol versions %s " , MCS_PROTOCOL_VERSIONS ) ;
2012-06-14 09:06:06 -04:00
2015-05-14 10:47:51 -04:00
m_Ports = ReadUpgradeIniPorts ( a_Settings , " Server " , " Ports " , " Port " , " PortsIPv6 " , " 25565 " ) ;
2015-04-29 12:24:14 -04:00
2015-05-14 10:47:51 -04:00
m_RCONServer . Initialize ( a_Settings ) ;
2013-06-27 11:14:20 -04:00
2012-06-14 09:06:06 -04:00
m_bIsConnected = true ;
2013-08-11 13:46:27 -04:00
m_ServerID = " - " ;
2014-10-17 07:57:18 -04:00
m_ShouldAuthenticate = a_ShouldAuth ;
2014-01-28 17:53:07 -05:00
if ( m_ShouldAuthenticate )
2012-11-11 09:23:47 -05:00
{
2017-06-13 15:35:30 -04:00
auto & rand = GetRandomProvider ( ) ;
unsigned int r1 = rand . RandInt < unsigned int > ( 1000000000U , 0x7fffffffU ) ;
unsigned int r2 = rand . RandInt < unsigned int > ( 1000000000U , 0x7fffffffU ) ;
2012-11-11 09:23:47 -05:00
std : : ostringstream sid ;
sid < < std : : hex < < r1 ;
sid < < std : : hex < < r2 ;
2013-08-11 13:46:27 -04:00
m_ServerID = sid . str ( ) ;
m_ServerID . resize ( 16 , ' 0 ' ) ;
2012-11-11 09:23:47 -05:00
}
2014-09-17 14:55:46 -04:00
// Check if both BungeeCord and online mode are on, if so, warn the admin:
2015-05-14 10:47:51 -04:00
m_ShouldAllowBungeeCord = a_Settings . GetValueSetB ( " Authentication " , " AllowBungeeCord " , false ) ;
2014-09-17 14:55:46 -04:00
if ( m_ShouldAllowBungeeCord & & m_ShouldAuthenticate )
{
LOGWARNING ( " WARNING: BungeeCord is allowed and server set to online mode. This is unsafe and will not work properly. Disable either authentication or BungeeCord in settings.ini. " ) ;
}
2015-04-29 12:24:14 -04:00
2016-07-21 07:00:30 -04:00
m_ShouldAllowMultiWorldTabCompletion = a_Settings . GetValueSetB ( " Server " , " AllowMultiWorldTabCompletion " , true ) ;
2017-05-19 10:09:01 -04:00
m_ShouldLimitPlayerBlockChanges = a_Settings . GetValueSetB ( " AntiCheat " , " LimitPlayerBlockChanges " , true ) ;
2015-05-14 10:47:51 -04:00
m_ShouldLoadOfflinePlayerData = a_Settings . GetValueSetB ( " PlayerData " , " LoadOfflinePlayerData " , false ) ;
m_ShouldLoadNamedPlayerData = a_Settings . GetValueSetB ( " PlayerData " , " LoadNamedPlayerData " , true ) ;
2015-04-29 12:24:14 -04:00
2015-05-14 10:47:51 -04:00
m_ClientViewDistance = a_Settings . GetValueSetI ( " Server " , " DefaultViewDistance " , cClientHandle : : DEFAULT_VIEW_DISTANCE ) ;
2012-11-11 09:23:47 -05:00
if ( m_ClientViewDistance < cClientHandle : : MIN_VIEW_DISTANCE )
{
m_ClientViewDistance = cClientHandle : : MIN_VIEW_DISTANCE ;
LOGINFO ( " Setting default viewdistance to the minimum of %d " , m_ClientViewDistance ) ;
}
if ( m_ClientViewDistance > cClientHandle : : MAX_VIEW_DISTANCE )
2012-06-14 09:06:06 -04:00
{
2012-11-11 09:23:47 -05:00
m_ClientViewDistance = cClientHandle : : MAX_VIEW_DISTANCE ;
LOGINFO ( " Setting default viewdistance to the maximum of %d " , m_ClientViewDistance ) ;
2012-06-14 09:06:06 -04:00
}
2015-04-29 12:24:14 -04:00
2012-08-30 17:06:13 -04:00
PrepareKeys ( ) ;
2015-04-29 12:24:14 -04:00
2012-06-14 09:06:06 -04:00
return true ;
}
2017-08-27 17:10:20 -04:00
bool cServer : : RegisterForgeMod ( const AString & a_ModName , const AString & a_ModVersion , UInt32 a_ProtocolVersionNumber )
{
auto & Mods = RegisteredForgeMods ( a_ProtocolVersionNumber ) ;
return Mods . insert ( { a_ModName , a_ModVersion } ) . second ;
}
void cServer : : UnregisterForgeMod ( const AString & a_ModName , UInt32 a_ProtocolVersionNumber )
{
auto & Mods = RegisteredForgeMods ( a_ProtocolVersionNumber ) ;
auto it = Mods . find ( a_ModName ) ;
if ( it ! = Mods . end ( ) )
{
Mods . erase ( it ) ;
}
}
AStringMap & cServer : : RegisteredForgeMods ( const UInt32 a_Protocol )
{
auto it = m_ForgeModsByVersion . find ( a_Protocol ) ;
if ( it = = m_ForgeModsByVersion . end ( ) )
{
AStringMap mods ;
m_ForgeModsByVersion . insert ( { a_Protocol , mods } ) ;
return m_ForgeModsByVersion . find ( a_Protocol ) - > second ;
}
return it - > second ;
}
const AStringMap & cServer : : GetRegisteredForgeMods ( const UInt32 a_Protocol )
{
return RegisteredForgeMods ( a_Protocol ) ;
}
2014-12-08 03:45:29 -05:00
bool cServer : : IsPlayerInQueue ( AString a_Username )
2014-11-29 03:36:15 -05:00
{
cCSLock Lock ( m_CSClients ) ;
2014-12-07 15:41:42 -05:00
for ( auto client : m_Clients )
2014-11-29 03:36:15 -05:00
{
2014-12-08 03:45:29 -05:00
if ( ( client - > GetUsername ( ) ) . compare ( a_Username ) = = 0 )
{
return true ;
}
2014-11-29 03:36:15 -05:00
}
2014-12-08 03:45:29 -05:00
return false ;
2014-11-29 03:36:15 -05:00
}
2012-08-30 17:06:13 -04:00
void cServer : : PrepareKeys ( void )
{
2013-09-28 15:36:01 -04:00
LOGD ( " Generating protocol encryption keypair... " ) ;
2014-01-23 17:35:23 -05:00
VERIFY ( m_PrivateKey . Generate ( 1024 ) ) ;
m_PublicKeyDER = m_PrivateKey . GetPubKeyDER ( ) ;
2012-08-30 17:06:13 -04:00
}
2015-01-24 14:17:00 -05:00
cTCPLink : : cCallbacksPtr cServer : : OnConnectionAccepted ( const AString & a_RemoteIPAddress )
2012-06-14 09:06:06 -04:00
{
2015-01-24 14:17:00 -05:00
LOGD ( " Client \" %s \" connected! " , a_RemoteIPAddress . c_str ( ) ) ;
cClientHandlePtr NewHandle = std : : make_shared < cClientHandle > ( a_RemoteIPAddress , m_ClientViewDistance ) ;
NewHandle - > SetSelf ( NewHandle ) ;
2012-06-14 09:06:06 -04:00
cCSLock Lock ( m_CSClients ) ;
2013-03-04 16:13:08 -05:00
m_Clients . push_back ( NewHandle ) ;
2015-01-24 14:17:00 -05:00
return NewHandle ;
2013-03-04 16:13:08 -05:00
}
2012-06-14 09:06:06 -04:00
bool cServer : : Tick ( float a_Dt )
{
2013-08-17 17:45:58 -04:00
// Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102):
cPluginManager : : Get ( ) - > Tick ( a_Dt ) ;
2015-04-29 12:24:14 -04:00
2013-08-17 17:45:58 -04:00
// Let the Root process all the queued commands:
2013-08-11 14:16:41 -04:00
cRoot : : Get ( ) - > TickCommands ( ) ;
2015-04-29 12:24:14 -04:00
2013-08-17 17:45:58 -04:00
// Tick all clients not yet assigned to a world:
2013-08-13 16:45:29 -04:00
TickClients ( a_Dt ) ;
if ( ! m_bRestarting )
{
return true ;
}
else
{
m_bRestarting = false ;
m_RestartEvent . Set ( ) ;
return false ;
}
}
void cServer : : TickClients ( float a_Dt )
{
2015-01-24 14:17:00 -05:00
cClientHandlePtrs RemoveClients ;
2012-06-14 09:06:06 -04:00
{
cCSLock Lock ( m_CSClients ) ;
2015-04-29 12:24:14 -04:00
2013-08-13 16:45:29 -04:00
// Remove clients that have moved to a world (the world will be ticking them from now on)
2015-01-24 14:17:00 -05:00
for ( auto itr = m_ClientsToRemove . begin ( ) , end = m_ClientsToRemove . end ( ) ; itr ! = end ; + + itr )
2013-08-13 16:45:29 -04:00
{
2015-01-24 14:17:00 -05:00
for ( auto itrC = m_Clients . begin ( ) , endC = m_Clients . end ( ) ; itrC ! = endC ; + + itrC )
{
if ( itrC - > get ( ) = = * itr )
{
m_Clients . erase ( itrC ) ;
break ;
}
}
2013-08-13 16:45:29 -04:00
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove . clear ( ) ;
2015-04-29 12:24:14 -04:00
2013-08-13 16:45:29 -04:00
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
2015-01-24 14:17:00 -05:00
for ( auto itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; )
2012-06-14 09:06:06 -04:00
{
if ( ( * itr ) - > IsDestroyed ( ) )
{
2016-01-30 19:25:03 -05:00
// Delete the client later, when CS is not held, to avoid deadlock: https://forum.cuberite.org/thread-374.html
2013-08-13 16:45:29 -04:00
RemoveClients . push_back ( * itr ) ;
2012-06-14 09:06:06 -04:00
itr = m_Clients . erase ( itr ) ;
continue ;
}
2013-12-16 04:41:35 -05:00
( * itr ) - > ServerTick ( a_Dt ) ;
2012-06-14 09:06:06 -04:00
+ + itr ;
} // for itr - m_Clients[]
}
2015-04-29 12:24:14 -04:00
2013-08-13 16:45:29 -04:00
// Delete the clients that have been destroyed
2015-01-24 14:17:00 -05:00
RemoveClients . clear ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-04 16:13:08 -05:00
bool cServer : : Start ( void )
2012-06-14 09:06:06 -04:00
{
2015-01-24 14:17:00 -05:00
for ( auto port : m_Ports )
2013-03-05 04:53:29 -05:00
{
2015-01-25 10:25:15 -05:00
UInt16 PortNum ;
if ( ! StringToInteger ( port , PortNum ) )
2015-01-24 14:17:00 -05:00
{
LOGWARNING ( " Invalid port specified for server: \" %s \" . Ignoring. " , port . c_str ( ) ) ;
continue ;
}
auto Handle = cNetwork : : Listen ( PortNum , std : : make_shared < cServerListenCallbacks > ( * this , PortNum ) ) ;
if ( Handle - > IsListening ( ) )
{
m_ServerHandles . push_back ( Handle ) ;
}
} // for port - Ports[]
if ( m_ServerHandles . empty ( ) )
2013-03-04 16:13:08 -05:00
{
2015-01-24 14:17:00 -05:00
LOGERROR ( " Couldn't open any ports. Aborting the server " ) ;
2013-03-04 16:13:08 -05:00
return false ;
}
2013-08-11 13:46:27 -04:00
if ( ! m_TickThread . Start ( ) )
{
return false ;
}
2013-03-04 16:13:08 -05:00
return true ;
2012-06-14 09:06:06 -04:00
}
2013-06-22 15:08:34 -04:00
bool cServer : : Command ( cClientHandle & a_Client , AString & a_Cmd )
2012-06-14 09:06:06 -04:00
{
2014-10-15 13:01:55 -04:00
return cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookChat ( * ( a_Client . GetPlayer ( ) ) , a_Cmd ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-29 11:30:05 -04:00
void cServer : : ExecuteConsoleCommand ( const AString & a_Cmd , cCommandOutputCallback & a_Output )
2012-06-14 09:06:06 -04:00
{
2012-08-18 06:38:15 -04:00
AStringVector split = StringSplit ( a_Cmd , " " ) ;
if ( split . empty ( ) )
2012-06-14 09:06:06 -04:00
{
return ;
}
2014-01-18 09:59:33 -05:00
2014-08-29 09:43:49 -04:00
// "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
2015-04-29 12:24:14 -04:00
2013-11-13 09:56:40 -05:00
// "help" and "reload" are to be handled by MCS, so that they work no matter what
if ( split [ 0 ] = = " help " )
{
PrintHelp ( split , a_Output ) ;
2014-08-29 09:43:49 -04:00
a_Output . Finished ( ) ;
2013-11-13 09:56:40 -05:00
return ;
}
2014-08-21 09:29:54 -04:00
else if ( split [ 0 ] = = " reload " )
2014-02-08 18:13:25 -05:00
{
cPluginManager : : Get ( ) - > ReloadPlugins ( ) ;
2014-08-29 09:43:49 -04:00
a_Output . Finished ( ) ;
2014-02-08 18:13:25 -05:00
return ;
}
2014-08-21 09:29:54 -04:00
else if ( split [ 0 ] = = " reloadplugins " )
2013-11-13 09:56:40 -05:00
{
cPluginManager : : Get ( ) - > ReloadPlugins ( ) ;
2014-08-29 09:43:49 -04:00
a_Output . Out ( " Plugins reloaded " ) ;
a_Output . Finished ( ) ;
2013-11-13 09:56:40 -05:00
return ;
}
2014-08-21 09:29:54 -04:00
else if ( split [ 0 ] = = " load " )
2014-05-17 12:38:33 -04:00
{
2014-05-17 13:39:16 -04:00
if ( split . size ( ) > 1 )
{
2015-04-19 11:25:48 -04:00
cPluginManager : : Get ( ) - > RefreshPluginList ( ) ; // Refresh the plugin list, so that if the plugin was added just now, it is loadable
2014-08-29 09:43:49 -04:00
a_Output . Out ( cPluginManager : : Get ( ) - > LoadPlugin ( split [ 1 ] ) ? " Plugin loaded " : " Error occurred loading plugin " ) ;
2014-05-17 13:39:16 -04:00
}
else
{
2015-04-19 04:57:41 -04:00
a_Output . Out ( " Usage: load <PluginFolder> " ) ;
2014-05-17 13:39:16 -04:00
}
2014-08-29 09:43:49 -04:00
a_Output . Finished ( ) ;
return ;
2014-05-17 12:38:33 -04:00
}
2014-08-21 09:29:54 -04:00
else if ( split [ 0 ] = = " unload " )
2014-05-17 13:39:16 -04:00
{
if ( split . size ( ) > 1 )
{
2015-04-19 04:57:41 -04:00
cPluginManager : : Get ( ) - > UnloadPlugin ( split [ 1 ] ) ;
a_Output . Out ( " Plugin unload scheduled " ) ;
2014-05-17 13:39:16 -04:00
}
else
{
2015-04-19 04:57:41 -04:00
a_Output . Out ( " Usage: unload <PluginFolder> " ) ;
2014-05-17 13:39:16 -04:00
}
2014-08-29 09:43:49 -04:00
a_Output . Finished ( ) ;
return ;
}
if ( split [ 0 ] = = " destroyentities " )
{
2017-09-11 17:20:49 -04:00
cRoot : : Get ( ) - > ForEachWorld ( [ ] ( cWorld & a_World )
2014-08-29 09:43:49 -04:00
{
2017-09-11 17:20:49 -04:00
a_World . ForEachEntity ( [ ] ( cEntity & a_Entity )
2014-08-29 09:43:49 -04:00
{
2017-09-11 17:20:49 -04:00
if ( ! a_Entity . IsPlayer ( ) )
2014-08-29 09:43:49 -04:00
{
2017-09-11 17:20:49 -04:00
a_Entity . Destroy ( ) ;
2014-08-29 09:43:49 -04:00
}
return false ;
}
2017-09-11 17:20:49 -04:00
) ;
2014-08-29 09:43:49 -04:00
return false ;
}
2017-09-11 17:20:49 -04:00
) ;
2014-08-29 09:43:49 -04:00
a_Output . Out ( " Destroyed all entities " ) ;
a_Output . Finished ( ) ;
return ;
2014-05-17 13:39:16 -04:00
}
2014-05-18 11:16:02 -04:00
2013-02-15 08:00:59 -05:00
// There is currently no way a plugin can do these (and probably won't ever be):
2014-08-21 09:29:54 -04:00
else if ( split [ 0 ] . compare ( " chunkstats " ) = = 0 )
2012-06-14 09:06:06 -04:00
{
2013-06-29 11:30:05 -04:00
cRoot : : Get ( ) - > LogChunkStats ( a_Output ) ;
a_Output . Finished ( ) ;
2012-06-14 09:06:06 -04:00
return ;
}
2015-09-28 15:30:31 -04:00
else if ( split [ 0 ] . compare ( " luastats " ) = = 0 )
{
a_Output . Out ( cLuaStateTracker : : GetStats ( ) ) ;
a_Output . Finished ( ) ;
return ;
}
2015-03-10 23:14:17 -04:00
else if ( cPluginManager : : Get ( ) - > ExecuteConsoleCommand ( split , a_Output , a_Cmd ) )
2012-11-20 16:28:43 -05:00
{
2013-06-29 11:30:05 -04:00
a_Output . Finished ( ) ;
2012-11-20 16:28:43 -05:00
return ;
}
2015-04-29 12:24:14 -04:00
2013-06-29 11:30:05 -04:00
a_Output . Out ( " Unknown command, type 'help' for all commands. " ) ;
a_Output . Finished ( ) ;
2013-02-15 08:00:59 -05:00
}
2013-11-13 09:56:40 -05:00
void cServer : : PrintHelp ( const AStringVector & a_Split , cCommandOutputCallback & a_Output )
{
2013-12-22 09:42:17 -05:00
UNUSED ( a_Split ) ;
2013-11-13 14:40:18 -05:00
typedef std : : pair < AString , AString > AStringPair ;
typedef std : : vector < AStringPair > AStringPairs ;
2015-04-29 12:24:14 -04:00
2013-11-13 14:40:18 -05:00
class cCallback :
public cPluginManager : : cCommandEnumCallback
{
public :
cCallback ( void ) : m_MaxLen ( 0 ) { }
2015-04-29 12:24:14 -04:00
2013-11-13 14:40:18 -05:00
virtual bool Command ( const AString & a_Command , const cPlugin * a_Plugin , const AString & a_Permission , const AString & a_HelpString ) override
{
2013-12-22 09:42:17 -05:00
UNUSED ( a_Plugin ) ;
UNUSED ( a_Permission ) ;
2013-11-13 14:40:18 -05:00
if ( ! a_HelpString . empty ( ) )
{
m_Commands . push_back ( AStringPair ( a_Command , a_HelpString ) ) ;
if ( m_MaxLen < a_Command . length ( ) )
{
m_MaxLen = a_Command . length ( ) ;
}
}
return false ;
}
2015-04-29 12:24:14 -04:00
2013-11-13 14:40:18 -05:00
AStringPairs m_Commands ;
size_t m_MaxLen ;
} Callback ;
cPluginManager : : Get ( ) - > ForEachConsoleCommand ( Callback ) ;
std : : sort ( Callback . m_Commands . begin ( ) , Callback . m_Commands . end ( ) ) ;
for ( AStringPairs : : const_iterator itr = Callback . m_Commands . begin ( ) , end = Callback . m_Commands . end ( ) ; itr ! = end ; + + itr )
{
const AStringPair & cmd = * itr ;
2015-11-23 12:20:37 -05:00
a_Output . Out ( Printf ( " %-*s - %s \n " , static_cast < int > ( Callback . m_MaxLen ) , cmd . first . c_str ( ) , cmd . second . c_str ( ) ) ) ;
2013-11-13 14:40:18 -05:00
} // for itr - Callback.m_Commands[]
2013-11-13 09:56:40 -05:00
}
2013-02-15 08:00:59 -05:00
void cServer : : BindBuiltInConsoleCommands ( void )
{
2016-06-12 12:11:40 -04:00
// Create an empty handler - the actual handling for the commands is performed before they are handed off to cPluginManager
class cEmptyHandler :
public cPluginManager : : cCommandHandler
{
virtual bool ExecuteCommand (
const AStringVector & a_Split ,
cPlayer * a_Player ,
const AString & a_Command ,
cCommandOutputCallback * a_Output = nullptr
) override
{
return false ;
}
} ;
auto handler = std : : make_shared < cEmptyHandler > ( ) ;
// Register internal commands:
2013-02-15 08:00:59 -05:00
cPluginManager * PlgMgr = cPluginManager : : Get ( ) ;
2016-06-12 12:11:40 -04:00
PlgMgr - > BindConsoleCommand ( " help " , nullptr , handler , " Shows the available commands " ) ;
PlgMgr - > BindConsoleCommand ( " reload " , nullptr , handler , " Reloads all plugins " ) ;
PlgMgr - > BindConsoleCommand ( " restart " , nullptr , handler , " Restarts the server cleanly " ) ;
PlgMgr - > BindConsoleCommand ( " stop " , nullptr , handler , " Stops the server cleanly " ) ;
PlgMgr - > BindConsoleCommand ( " chunkstats " , nullptr , handler , " Displays detailed chunk memory statistics " ) ;
PlgMgr - > BindConsoleCommand ( " load " , nullptr , handler , " Adds and enables the specified plugin " ) ;
PlgMgr - > BindConsoleCommand ( " unload " , nullptr , handler , " Disables the specified plugin " ) ;
PlgMgr - > BindConsoleCommand ( " destroyentities " , nullptr , handler , " Destroys all entities in all worlds " ) ;
2012-06-14 09:06:06 -04:00
}
2013-08-11 13:46:27 -04:00
void cServer : : Shutdown ( void )
2012-06-14 09:06:06 -04:00
{
2015-01-24 14:17:00 -05:00
// Stop listening on all sockets:
for ( auto srv : m_ServerHandles )
{
srv - > Close ( ) ;
}
m_ServerHandles . clear ( ) ;
2015-04-29 12:24:14 -04:00
2015-01-24 14:17:00 -05:00
// Notify the tick thread and wait for it to terminate:
2012-06-14 09:06:06 -04:00
m_bRestarting = true ;
2013-08-11 13:46:27 -04:00
m_RestartEvent . Wait ( ) ;
2012-06-14 09:06:06 -04:00
cRoot : : Get ( ) - > SaveAllChunks ( ) ;
2015-01-24 14:17:00 -05:00
// Remove all clients:
2012-06-14 09:06:06 -04:00
cCSLock Lock ( m_CSClients ) ;
2015-01-24 14:17:00 -05:00
for ( auto itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
2012-06-19 13:34:22 -04:00
( * itr ) - > Destroy ( ) ;
2012-06-14 09:06:06 -04:00
}
m_Clients . clear ( ) ;
}
void cServer : : KickUser ( int a_ClientID , const AString & a_Reason )
{
cCSLock Lock ( m_CSClients ) ;
2015-01-24 14:17:00 -05:00
for ( auto itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
if ( ( * itr ) - > GetUniqueID ( ) = = a_ClientID )
{
( * itr ) - > Kick ( a_Reason ) ;
}
} // for itr - m_Clients[]
}
2017-08-25 08:43:18 -04:00
void cServer : : 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
{
cCSLock Lock ( m_CSClients ) ;
2017-07-28 12:54:40 -04:00
// Check max players condition within lock (expect server and authenticator thread to both call here)
if ( GetNumPlayers ( ) > = GetMaxPlayers ( ) )
{
KickUser ( a_ClientID , " The server is currently full :( " " \n " " Try again later? " ) ;
return ;
}
2015-01-24 14:17:00 -05:00
for ( auto itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; + + itr )
2012-06-14 09:06:06 -04:00
{
if ( ( * itr ) - > GetUniqueID ( ) = = a_ClientID )
{
2014-07-14 14:49:31 -04:00
( * itr ) - > Authenticate ( a_Name , a_UUID , a_Properties ) ;
2013-08-13 16:45:29 -04:00
return ;
2012-06-14 09:06:06 -04:00
}
} // for itr - m_Clients[]
}