2012-06-14 13:06:06 +00: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 22:09:57 +00:00
#include "Server.h"
#include "ClientHandle.h"
2012-09-23 20:53:08 +00:00
#include "Mobs/Monster.h"
2012-09-23 22:09:57 +00:00
#include "Root.h"
#include "World.h"
2012-06-14 13:06:06 +00:00
#include "ChunkDef.h"
2013-12-08 12:17:54 +01:00
#include "Bindings/PluginManager.h"
2012-09-23 22:09:57 +00:00
#include "ChatColor.h"
2013-08-19 11:39:13 +02:00
#include "Entities/Player.h"
2012-09-23 22:09:57 +00:00
#include "Inventory.h"
#include "Item.h"
#include "FurnaceRecipe.h"
#include "WebAdmin.h"
2012-09-23 20:03:26 +00:00
#include "Protocol/ProtocolRecognizer.h"
2013-06-29 15:30:05 +00:00
#include "CommandOutput.h"
2012-06-14 13:06:06 +00:00
2014-10-23 15:15:10 +02:00
#include "IniFile.h"
2014-03-11 16:01:17 +02:00
#include "Vector3.h"
2012-06-14 13:06:06 +00:00
#include <fstream>
#include <sstream>
#include <iostream>
2014-07-19 15:23:40 +02:00
extern "C"
{
2013-11-27 07:40:59 +00:00
#include "zlib/zlib.h"
2012-06-14 13:06:06 +00:00
}
2012-10-19 20:51:46 +00:00
2015-07-31 16:49:10 +02:00
/** Enable the memory leak finder - needed for the "dumpmem" server command:
Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory
_X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
and we haven't had a memory leak for over a year anyway. */
2014-02-20 20:11:17 +01:00
// #define ENABLE_LEAK_FINDER
2012-10-19 22:09:33 +00:00
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
#pragma warning(push)
#pragma warning(disable:4100)
#include "LeakFinder.h"
#pragma warning(pop)
#endif
2012-06-14 13:06:06 +00:00
typedef std :: list < cClientHandle * > ClientList ;
2015-01-24 20:17:00 +01: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 13:56:08 +02:00
virtual void OnError ( int a_ErrorCode , const AString & a_ErrorMsg ) override
2015-01-24 20:17:00 +01: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 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 19:46:27 +02:00
// cServer::cTickThread:
cServer :: cTickThread :: cTickThread ( cServer & a_Server ) :
super ( "ServerTickThread" ),
m_Server ( a_Server )
{
}
void cServer :: cTickThread :: Execute ( void )
2012-06-14 13:06:06 +00:00
{
2014-10-20 18:59:40 +01:00
auto LastTime = std :: chrono :: steady_clock :: now ();
static const auto msPerTick = std :: chrono :: milliseconds ( 50 );
2013-08-11 19:46:27 +02:00
while ( ! m_ShouldTerminate )
{
2014-10-20 18:59:40 +01:00
auto NowTime = std :: chrono :: steady_clock :: now ();
2014-12-07 15:46:27 +01: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 18:59:40 +01:00
auto TickTime = std :: chrono :: steady_clock :: now () - NowTime ;
2015-04-29 19:24:14 +03:00
2013-08-11 19:46:27 +02:00
if ( TickTime < msPerTick )
{
// Stretch tick time until it's at least msPerTick
2014-10-21 14:17:36 +01:00
std :: this_thread :: sleep_for ( msPerTick - TickTime );
2013-08-11 19:46:27 +02:00
}
2012-06-14 13:06:06 +00:00
2013-08-11 19:46:27 +02:00
LastTime = NowTime ;
}
}
2012-06-14 13:06:06 +00:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 19:18:06 +02:00
// cServer:
cServer :: cServer ( void ) :
2014-05-19 14:34:34 +02:00
m_PlayerCount ( 0 ),
m_PlayerCountDiff ( 0 ),
m_ClientViewDistance ( 0 ),
2013-08-11 19:18:06 +02:00
m_bIsConnected ( false ),
m_bRestarting ( false ),
2013-08-11 19:46:27 +02:00
m_RCONServer ( * this ),
2014-05-19 14:34:34 +02:00
m_MaxPlayers ( 0 ),
m_bIsHardcore ( false ),
m_TickThread ( * this ),
2014-08-21 22:39:53 +02:00
m_ShouldAuthenticate ( false ),
m_ShouldLoadOfflinePlayerData ( false ),
m_ShouldLoadNamedPlayerData ( true )
2013-08-11 19:18:06 +02:00
{
2015-09-28 21:30:31 +02:00
// Initialize the LuaStateTracker singleton before the app goes multithreaded:
cLuaStateTracker :: GetStats ();
2013-08-11 19:18:06 +02:00
}
2013-08-12 08:35:13 +02:00
void cServer :: ClientMovedToWorld ( const cClientHandle * a_Client )
{
cCSLock Lock ( m_CSClients );
2013-08-13 22:45:29 +02:00
m_ClientsToRemove . push_back ( const_cast < cClientHandle *> ( a_Client ));
2013-08-12 08:35:13 +02:00
}
2013-08-14 10:24:34 +02:00
void cServer :: PlayerCreated ( const cPlayer * a_Player )
{
2013-12-22 14:42:17 +00:00
UNUSED ( a_Player );
2013-08-14 10:24:34 +02:00
// To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
cCSLock Lock ( m_CSPlayerCountDiff );
m_PlayerCountDiff += 1 ;
}
2013-08-14 19:11:54 +02:00
void cServer :: PlayerDestroying ( const cPlayer * a_Player )
2013-08-14 10:24:34 +02:00
{
2013-12-22 14:42:17 +00:00
UNUSED ( a_Player );
2013-08-14 10:24:34 +02:00
// To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
cCSLock Lock ( m_CSPlayerCountDiff );
m_PlayerCountDiff -= 1 ;
}
2015-05-14 15:47:51 +01:00
bool cServer :: InitServer ( cSettingsRepositoryInterface & a_Settings , bool a_ShouldAuth )
2012-06-14 13:06:06 +00:00
{
2015-11-22 16:30:33 +02:00
m_Description = a_Settings . GetValueSet ( "Server" , "Description" , "Cuberite - in C++!" );
2015-05-14 15:47:51 +01:00
m_MaxPlayers = a_Settings . GetValueSetI ( "Server" , "MaxPlayers" , 100 );
m_bIsHardcore = a_Settings . GetValueSetB ( "Server" , "HardcoreEnabled" , false );
m_bAllowMultiLogin = a_Settings . GetValueSetB ( "Server" , "AllowMultiLogin" , false );
2013-08-14 10:24:34 +02:00
m_PlayerCount = 0 ;
m_PlayerCountDiff = 0 ;
2013-08-11 19:18:06 +02:00
2014-07-17 19:13:23 +02: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 09:31:06 -06:00
2012-11-11 14:23:47 +00:00
if ( m_bIsConnected )
2012-06-14 13:06:06 +00:00
{
LOGERROR ( "ERROR: Trying to initialize server while server is already running!" );
return false ;
}
2013-08-03 18:29:49 +01:00
LOGINFO ( "Compatible clients: %s" , MCS_CLIENT_VERSIONS );
LOGINFO ( "Compatible protocol versions %s" , MCS_PROTOCOL_VERSIONS );
2012-06-14 13:06:06 +00:00
2015-05-14 15:47:51 +01:00
m_Ports = ReadUpgradeIniPorts ( a_Settings , "Server" , "Ports" , "Port" , "PortsIPv6" , "25565" );
2015-04-29 19:24:14 +03:00
2015-05-14 15:47:51 +01:00
m_RCONServer . Initialize ( a_Settings );
2013-06-27 15:14:20 +00:00
2012-06-14 13:06:06 +00:00
m_bIsConnected = true ;
2013-08-11 19:46:27 +02:00
m_ServerID = "-" ;
2014-10-17 13:57:18 +02:00
m_ShouldAuthenticate = a_ShouldAuth ;
2014-01-28 23:53:07 +01:00
if ( m_ShouldAuthenticate )
2012-11-11 14:23:47 +00:00
{
MTRand mtrand1 ;
2013-08-11 19:46:27 +02:00
unsigned int r1 = ( mtrand1 . randInt () % 1147483647 ) + 1000000000 ;
unsigned int r2 = ( mtrand1 . randInt () % 1147483647 ) + 1000000000 ;
2012-11-11 14:23:47 +00:00
std :: ostringstream sid ;
sid << std :: hex << r1 ;
sid << std :: hex << r2 ;
2013-08-11 19:46:27 +02:00
m_ServerID = sid . str ();
m_ServerID . resize ( 16 , '0' );
2012-11-11 14:23:47 +00:00
}
2014-09-17 20:55:46 +02:00
// Check if both BungeeCord and online mode are on, if so, warn the admin:
2015-05-14 15:47:51 +01:00
m_ShouldAllowBungeeCord = a_Settings . GetValueSetB ( "Authentication" , "AllowBungeeCord" , false );
2014-09-17 20:55:46 +02: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 19:24:14 +03:00
2015-05-14 15:47:51 +01:00
m_ShouldLoadOfflinePlayerData = a_Settings . GetValueSetB ( "PlayerData" , "LoadOfflinePlayerData" , false );
m_ShouldLoadNamedPlayerData = a_Settings . GetValueSetB ( "PlayerData" , "LoadNamedPlayerData" , true );
2015-04-29 19:24:14 +03:00
2015-05-14 15:47:51 +01:00
m_ClientViewDistance = a_Settings . GetValueSetI ( "Server" , "DefaultViewDistance" , cClientHandle :: DEFAULT_VIEW_DISTANCE );
2012-11-11 14:23:47 +00: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 13:06:06 +00:00
{
2012-11-11 14:23:47 +00:00
m_ClientViewDistance = cClientHandle :: MAX_VIEW_DISTANCE ;
LOGINFO ( "Setting default viewdistance to the maximum of %d" , m_ClientViewDistance );
2012-06-14 13:06:06 +00:00
}
2015-04-29 19:24:14 +03:00
2012-08-30 21:06:13 +00:00
PrepareKeys ();
2015-04-29 19:24:14 +03:00
2012-06-14 13:06:06 +00:00
return true ;
}
2014-04-19 17:53:02 +02:00
int cServer :: GetNumPlayers ( void ) const
2013-08-14 10:24:34 +02:00
{
cCSLock Lock ( m_CSPlayerCount );
return m_PlayerCount ;
}
2014-12-08 00:45:29 -08:00
bool cServer :: IsPlayerInQueue ( AString a_Username )
2014-11-29 00:36:15 -08:00
{
cCSLock Lock ( m_CSClients );
2014-12-07 12:41:42 -08:00
for ( auto client : m_Clients )
2014-11-29 00:36:15 -08:00
{
2014-12-08 00:45:29 -08:00
if (( client -> GetUsername ()). compare ( a_Username ) == 0 )
{
return true ;
}
2014-11-29 00:36:15 -08:00
}
2014-12-08 00:45:29 -08:00
return false ;
2014-11-29 00:36:15 -08:00
}
2012-08-30 21:06:13 +00:00
void cServer :: PrepareKeys ( void )
{
2013-09-28 20:36:01 +01:00
LOGD ( "Generating protocol encryption keypair..." );
2014-01-23 23:35:23 +01:00
VERIFY ( m_PrivateKey . Generate ( 1024 ));
m_PublicKeyDER = m_PrivateKey . GetPubKeyDER ();
2012-08-30 21:06:13 +00:00
}
2015-01-24 20:17:00 +01:00
cTCPLink :: cCallbacksPtr cServer :: OnConnectionAccepted ( const AString & a_RemoteIPAddress )
2012-06-14 13:06:06 +00:00
{
2015-01-24 20:17:00 +01: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 13:06:06 +00:00
cCSLock Lock ( m_CSClients );
2013-03-04 21:13:08 +00:00
m_Clients . push_back ( NewHandle );
2015-01-24 20:17:00 +01:00
return NewHandle ;
2013-03-04 21:13:08 +00:00
}
2012-06-14 13:06:06 +00:00
bool cServer :: Tick ( float a_Dt )
{
2013-08-14 10:24:34 +02:00
// Apply the queued playercount adjustments (postponed to avoid deadlocks)
int PlayerCountDiff = 0 ;
{
cCSLock Lock ( m_CSPlayerCountDiff );
std :: swap ( PlayerCountDiff , m_PlayerCountDiff );
}
{
cCSLock Lock ( m_CSPlayerCount );
m_PlayerCount += PlayerCountDiff ;
}
2015-04-29 19:24:14 +03:00
2013-08-17 23:45:58 +02: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 19:24:14 +03:00
2013-08-17 23:45:58 +02:00
// Let the Root process all the queued commands:
2013-08-11 20:16:41 +02:00
cRoot :: Get () -> TickCommands ();
2015-04-29 19:24:14 +03:00
2013-08-17 23:45:58 +02:00
// Tick all clients not yet assigned to a world:
2013-08-13 22:45:29 +02: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 20:17:00 +01:00
cClientHandlePtrs RemoveClients ;
2012-06-14 13:06:06 +00:00
{
cCSLock Lock ( m_CSClients );
2015-04-29 19:24:14 +03:00
2013-08-13 22:45:29 +02:00
// Remove clients that have moved to a world (the world will be ticking them from now on)
2015-01-24 20:17:00 +01:00
for ( auto itr = m_ClientsToRemove . begin (), end = m_ClientsToRemove . end (); itr != end ; ++ itr )
2013-08-13 22:45:29 +02:00
{
2015-01-24 20:17:00 +01: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 22:45:29 +02:00
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove . clear ();
2015-04-29 19:24:14 +03:00
2013-08-13 22:45:29 +02:00
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
2015-01-24 20:17:00 +01:00
for ( auto itr = m_Clients . begin (); itr != m_Clients . end ();)
2012-06-14 13:06:06 +00:00
{
if (( * itr ) -> IsDestroyed ())
{
2016-01-31 02:25:03 +02:00
// Delete the client later, when CS is not held, to avoid deadlock: https://forum.cuberite.org/thread-374.html
2013-08-13 22:45:29 +02:00
RemoveClients . push_back ( * itr );
2012-06-14 13:06:06 +00:00
itr = m_Clients . erase ( itr );
continue ;
}
2013-12-16 10:41:35 +01:00
( * itr ) -> ServerTick ( a_Dt );
2012-06-14 13:06:06 +00:00
++ itr ;
} // for itr - m_Clients[]
}
2015-04-29 19:24:14 +03:00
2013-08-13 22:45:29 +02:00
// Delete the clients that have been destroyed
2015-01-24 20:17:00 +01:00
RemoveClients . clear ();
2012-06-14 13:06:06 +00:00
}
2013-03-04 21:13:08 +00:00
bool cServer :: Start ( void )
2012-06-14 13:06:06 +00:00
{
2015-01-24 20:17:00 +01:00
for ( auto port : m_Ports )
2013-03-05 09:53:29 +00:00
{
2015-01-25 16:25:15 +01:00
UInt16 PortNum ;
if ( ! StringToInteger ( port , PortNum ))
2015-01-24 20:17:00 +01: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 21:13:08 +00:00
{
2015-01-24 20:17:00 +01:00
LOGERROR ( "Couldn't open any ports. Aborting the server" );
2013-03-04 21:13:08 +00:00
return false ;
}
2013-08-11 19:46:27 +02:00
if ( ! m_TickThread . Start ())
{
return false ;
}
2013-03-04 21:13:08 +00:00
return true ;
2012-06-14 13:06:06 +00:00
}
2013-06-22 19:08:34 +00:00
bool cServer :: Command ( cClientHandle & a_Client , AString & a_Cmd )
2012-06-14 13:06:06 +00:00
{
2014-10-15 19:01:55 +02:00
return cRoot :: Get () -> GetPluginManager () -> CallHookChat ( * ( a_Client . GetPlayer ()), a_Cmd );
2012-06-14 13:06:06 +00:00
}
2013-06-29 15:30:05 +00:00
void cServer :: ExecuteConsoleCommand ( const AString & a_Cmd , cCommandOutputCallback & a_Output )
2012-06-14 13:06:06 +00:00
{
2012-08-18 10:38:15 +00:00
AStringVector split = StringSplit ( a_Cmd , " " );
if ( split . empty ())
2012-06-14 13:06:06 +00:00
{
return ;
}
2014-01-18 16:59:33 +02:00
2014-08-29 14:43:49 +01:00
// "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
2015-04-29 19:24:14 +03:00
2013-11-13 15:56:40 +01: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 14:43:49 +01:00
a_Output . Finished ();
2013-11-13 15:56:40 +01:00
return ;
}
2014-08-21 15:29:54 +02:00
else if ( split [ 0 ] == "reload" )
2014-02-09 00:13:25 +01:00
{
cPluginManager :: Get () -> ReloadPlugins ();
2014-08-29 14:43:49 +01:00
a_Output . Finished ();
2014-02-09 00:13:25 +01:00
return ;
}
2014-08-21 15:29:54 +02:00
else if ( split [ 0 ] == "reloadplugins" )
2013-11-13 15:56:40 +01:00
{
cPluginManager :: Get () -> ReloadPlugins ();
2014-08-29 14:43:49 +01:00
a_Output . Out ( "Plugins reloaded" );
a_Output . Finished ();
2013-11-13 15:56:40 +01:00
return ;
}
2014-08-21 15:29:54 +02:00
else if ( split [ 0 ] == "load" )
2014-05-17 18:38:33 +02:00
{
2014-05-17 19:39:16 +02:00
if ( split . size () > 1 )
{
2015-04-19 17:25:48 +02:00
cPluginManager :: Get () -> RefreshPluginList (); // Refresh the plugin list, so that if the plugin was added just now, it is loadable
2014-08-29 14:43:49 +01:00
a_Output . Out ( cPluginManager :: Get () -> LoadPlugin ( split [ 1 ]) ? "Plugin loaded" : "Error occurred loading plugin" );
2014-05-17 19:39:16 +02:00
}
else
{
2015-04-19 10:57:41 +02:00
a_Output . Out ( "Usage: load <PluginFolder>" );
2014-05-17 19:39:16 +02:00
}
2014-08-29 14:43:49 +01:00
a_Output . Finished ();
return ;
2014-05-17 18:38:33 +02:00
}
2014-08-21 15:29:54 +02:00
else if ( split [ 0 ] == "unload" )
2014-05-17 19:39:16 +02:00
{
if ( split . size () > 1 )
{
2015-04-19 10:57:41 +02:00
cPluginManager :: Get () -> UnloadPlugin ( split [ 1 ]);
a_Output . Out ( "Plugin unload scheduled" );
2014-05-17 19:39:16 +02:00
}
else
{
2015-04-19 10:57:41 +02:00
a_Output . Out ( "Usage: unload <PluginFolder>" );
2014-05-17 19:39:16 +02:00
}
2014-08-29 14:43:49 +01:00
a_Output . Finished ();
return ;
}
if ( split [ 0 ] == "destroyentities" )
{
class WorldCallback : public cWorldListCallback
{
virtual bool Item ( cWorld * a_World ) override
{
class EntityCallback : public cEntityCallback
{
virtual bool Item ( cEntity * a_Entity ) override
{
if ( ! a_Entity -> IsPlayer ())
{
a_Entity -> Destroy ();
}
return false ;
}
} EC ;
a_World -> ForEachEntity ( EC );
return false ;
}
} WC ;
cRoot :: Get () -> ForEachWorld ( WC );
a_Output . Out ( "Destroyed all entities" );
a_Output . Finished ();
return ;
2014-05-17 19:39:16 +02:00
}
2014-05-18 17:16:02 +02:00
2013-02-15 13:00:59 +00:00
// There is currently no way a plugin can do these (and probably won't ever be):
2014-08-21 15:29:54 +02:00
else if ( split [ 0 ]. compare ( "chunkstats" ) == 0 )
2012-06-14 13:06:06 +00:00
{
2013-06-29 15:30:05 +00:00
cRoot :: Get () -> LogChunkStats ( a_Output );
a_Output . Finished ();
2012-06-14 13:06:06 +00:00
return ;
}
2015-09-28 21:30:31 +02:00
else if ( split [ 0 ]. compare ( "luastats" ) == 0 )
{
a_Output . Out ( cLuaStateTracker :: GetStats ());
a_Output . Finished ();
return ;
}
2012-10-19 22:09:33 +00:00
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
2014-08-21 15:29:54 +02:00
else if ( split [ 0 ]. compare ( "dumpmem" ) == 0 )
2012-10-19 22:09:33 +00:00
{
LeakFinderXmlOutput Output ( "memdump.xml" );
DumpUsedMemory ( & Output );
return ;
}
2015-04-29 19:24:14 +03:00
2014-08-21 15:29:54 +02:00
else if ( split [ 0 ]. compare ( "killmem" ) == 0 )
2013-03-23 19:04:39 +00:00
{
2014-01-07 16:00:19 +01:00
for (;;)
2013-03-23 19:04:39 +00:00
{
new char [ 100 * 1024 * 1024 ]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS
}
}
2012-10-19 22:09:33 +00:00
#endif
2014-01-21 15:58:17 +02:00
2015-03-11 04:14:17 +01:00
else if ( cPluginManager :: Get () -> ExecuteConsoleCommand ( split , a_Output , a_Cmd ))
2012-11-20 21:28:43 +00:00
{
2013-06-29 15:30:05 +00:00
a_Output . Finished ();
2012-11-20 21:28:43 +00:00
return ;
}
2015-04-29 19:24:14 +03:00
2013-06-29 15:30:05 +00:00
a_Output . Out ( "Unknown command, type 'help' for all commands." );
a_Output . Finished ();
2013-02-15 13:00:59 +00:00
}
2013-11-13 15:56:40 +01:00
void cServer :: PrintHelp ( const AStringVector & a_Split , cCommandOutputCallback & a_Output )
{
2013-12-22 14:42:17 +00:00
UNUSED ( a_Split );
2013-11-13 20:40:18 +01:00
typedef std :: pair < AString , AString > AStringPair ;
typedef std :: vector < AStringPair > AStringPairs ;
2015-04-29 19:24:14 +03:00
2013-11-13 20:40:18 +01:00
class cCallback :
public cPluginManager :: cCommandEnumCallback
{
public :
cCallback ( void ) : m_MaxLen ( 0 ) {}
2015-04-29 19:24:14 +03:00
2013-11-13 20:40:18 +01:00
virtual bool Command ( const AString & a_Command , const cPlugin * a_Plugin , const AString & a_Permission , const AString & a_HelpString ) override
{
2013-12-22 14:42:17 +00:00
UNUSED ( a_Plugin );
UNUSED ( a_Permission );
2013-11-13 20:40:18 +01: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 19:24:14 +03:00
2013-11-13 20:40:18 +01: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 18:20:37 +01: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 20:40:18 +01:00
} // for itr - Callback.m_Commands[]
2013-11-13 15:56:40 +01:00
}
2013-02-15 13:00:59 +00:00
void cServer :: BindBuiltInConsoleCommands ( void )
{
cPluginManager * PlgMgr = cPluginManager :: Get ();
2015-11-22 23:57:53 +01:00
PlgMgr -> BindConsoleCommand ( "help" , nullptr , "Shows the available commands" );
PlgMgr -> BindConsoleCommand ( "reload" , nullptr , "Reloads all plugins" );
PlgMgr -> BindConsoleCommand ( "restart" , nullptr , "Restarts the server cleanly" );
PlgMgr -> BindConsoleCommand ( "stop" , nullptr , "Stops the server cleanly" );
PlgMgr -> BindConsoleCommand ( "chunkstats" , nullptr , "Displays detailed chunk memory statistics" );
PlgMgr -> BindConsoleCommand ( "load <pluginname>" , nullptr , "Adds and enables the specified plugin" );
PlgMgr -> BindConsoleCommand ( "unload <pluginname>" , nullptr , "Disables the specified plugin" );
PlgMgr -> BindConsoleCommand ( "destroyentities" , nullptr , "Destroys all entities in all worlds" );
2014-05-17 19:39:16 +02:00
2013-02-15 13:00:59 +00:00
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
2014-10-20 21:55:07 +01:00
PlgMgr -> BindConsoleCommand ( "dumpmem" , nullptr , " - Dumps all used memory blocks together with their callstacks into memdump.xml" );
2013-11-04 21:51:24 +00:00
#endif
2012-06-14 13:06:06 +00:00
}
2013-08-11 19:46:27 +02:00
void cServer :: Shutdown ( void )
2012-06-14 13:06:06 +00:00
{
2015-01-24 20:17:00 +01:00
// Stop listening on all sockets:
for ( auto srv : m_ServerHandles )
{
srv -> Close ();
}
m_ServerHandles . clear ();
2015-04-29 19:24:14 +03:00
2015-01-24 20:17:00 +01:00
// Notify the tick thread and wait for it to terminate:
2012-06-14 13:06:06 +00:00
m_bRestarting = true ;
2013-08-11 19:46:27 +02:00
m_RestartEvent . Wait ();
2012-06-14 13:06:06 +00:00
cRoot :: Get () -> SaveAllChunks ();
2015-01-24 20:17:00 +01:00
// Remove all clients:
2012-06-14 13:06:06 +00:00
cCSLock Lock ( m_CSClients );
2015-01-24 20:17:00 +01:00
for ( auto itr = m_Clients . begin (); itr != m_Clients . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
2012-06-19 17:34:22 +00:00
( * itr ) -> Destroy ();
2012-06-14 13:06:06 +00:00
}
m_Clients . clear ();
}
void cServer :: KickUser ( int a_ClientID , const AString & a_Reason )
{
cCSLock Lock ( m_CSClients );
2015-01-24 20:17:00 +01:00
for ( auto itr = m_Clients . begin (); itr != m_Clients . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
if (( * itr ) -> GetUniqueID () == a_ClientID )
{
( * itr ) -> Kick ( a_Reason );
}
} // for itr - m_Clients[]
}
2014-07-16 00:03:47 +01:00
void cServer :: AuthenticateUser ( int a_ClientID , const AString & a_Name , const AString & a_UUID , const Json :: Value & a_Properties )
2012-06-14 13:06:06 +00:00
{
cCSLock Lock ( m_CSClients );
2015-01-24 20:17:00 +01:00
for ( auto itr = m_Clients . begin (); itr != m_Clients . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
if (( * itr ) -> GetUniqueID () == a_ClientID )
{
2014-07-14 19:49:31 +01:00
( * itr ) -> Authenticate ( a_Name , a_UUID , a_Properties );
2013-08-13 22:45:29 +02:00
return ;
2012-06-14 13:06:06 +00:00
}
} // for itr - m_Clients[]
}