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"
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"
2017-06-13 20:35:30 +01:00
#include "FastRandom.h"
2012-06-14 13:06:06 +00:00
2014-10-23 15:15:10 +02:00
#include "IniFile.h"
2012-06-14 13:06:06 +00:00
#include <fstream>
#include <sstream>
#include <iostream>
2012-10-19 20:51:46 +00:00
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 ) :
2021-03-28 13:34:57 +01:00
Super ( "Server Ticker" ),
2013-08-11 19:46:27 +02:00
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 ();
2020-07-23 00:34:43 +01:00
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_ClientViewDistance ( 0 ),
2013-08-11 19:18:06 +02:00
m_bIsConnected ( 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 ),
2021-05-26 18:07:32 +02:00
m_ShouldAuthenticate ( false ),
m_UpTime ( 0 )
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
}
2017-07-28 17:54:40 +01:00
void cServer :: PlayerCreated ()
2013-08-14 10:24:34 +02:00
{
2017-07-28 17:54:40 +01:00
m_PlayerCount ++ ;
2013-08-14 10:24:34 +02:00
}
2017-07-28 17:54:40 +01:00
void cServer :: PlayerDestroyed ()
2013-08-14 10:24:34 +02:00
{
2017-07-28 17:54:40 +01:00
m_PlayerCount -- ;
2013-08-14 10:24:34 +02:00
}
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++!" );
2017-01-03 10:57:31 -08:00
m_ShutdownMessage = a_Settings . GetValueSet ( "Server" , "ShutdownMessage" , "Server shutdown" );
2017-07-28 17:54:40 +01:00
m_MaxPlayers = static_cast < size_t > ( a_Settings . GetValueSetI ( "Server" , "MaxPlayers" , 100 ));
2015-05-14 15:47:51 +01:00
m_bIsHardcore = a_Settings . GetValueSetB ( "Server" , "HardcoreEnabled" , false );
m_bAllowMultiLogin = a_Settings . GetValueSetB ( "Server" , "AllowMultiLogin" , false );
2020-04-08 00:23:54 +03:00
m_ResourcePackUrl = a_Settings . GetValueSet ( "Server" , "ResourcePackUrl" , "" );
2013-08-11 19:18:06 +02:00
2020-05-07 22:14:00 +03:00
m_FaviconData = Base64Encode ( cFile :: ReadWholeFile ( 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 );
2020-07-17 18:46:50 +01:00
LOGD ( "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
{
2017-06-13 20:35:30 +01: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 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
2016-07-21 12:00:30 +01:00
m_ShouldAllowMultiWorldTabCompletion = a_Settings . GetValueSetB ( "Server" , "AllowMultiWorldTabCompletion" , true );
2017-05-19 17:09:01 +03:00
m_ShouldLimitPlayerBlockChanges = a_Settings . GetValueSetB ( "AntiCheat" , "LimitPlayerBlockChanges" , true );
2015-04-29 19:24:14 +03:00
2020-12-23 22:39:53 +00:00
const auto ClientViewDistance = a_Settings . GetValueSetI ( "Server" , "DefaultViewDistance" , cClientHandle :: DEFAULT_VIEW_DISTANCE );
if ( ClientViewDistance < cClientHandle :: MIN_VIEW_DISTANCE )
2012-11-11 14:23:47 +00:00
{
m_ClientViewDistance = cClientHandle :: MIN_VIEW_DISTANCE ;
2020-10-05 13:09:42 +01:00
LOGINFO ( "Setting default view distance to the minimum of %d" , m_ClientViewDistance );
2012-11-11 14:23:47 +00:00
}
2020-12-23 22:39:53 +00:00
else if ( 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 ;
2020-10-05 13:09:42 +01:00
LOGINFO ( "Setting default view distance to the maximum of %d" , m_ClientViewDistance );
}
else
{
2020-12-23 22:39:53 +00:00
m_ClientViewDistance = 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 ;
}
2017-08-27 14:10:20 -07: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 );
}
}
2018-07-26 22:24:36 +01:00
2017-08-27 14:10:20 -07:00
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 ;
}
2018-07-26 22:24:36 +01:00
2017-08-27 14:10:20 -07:00
const AStringMap & cServer :: GetRegisteredForgeMods ( const UInt32 a_Protocol )
{
return RegisteredForgeMods ( a_Protocol );
}
2018-07-26 22:24:36 +01:00
2020-05-14 23:15:35 +01:00
bool cServer :: IsPlayerInQueue ( const AString & a_Username )
2014-11-29 00:36:15 -08:00
{
cCSLock Lock ( m_CSClients );
2020-05-14 23:15:35 +01:00
for ( const 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 );
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 );
2019-05-07 15:15:58 -04:00
return std :: move ( NewHandle );
2013-03-04 21:13:08 +00:00
}
2020-07-23 00:34:43 +01:00
void cServer :: Tick ( float a_Dt )
2012-06-14 13:06:06 +00:00
{
2021-05-26 18:07:32 +02:00
// Update server uptime
m_UpTime ++ ;
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
2020-07-23 00:34:43 +01:00
// Process all the queued commands:
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 );
2021-05-26 18:07:32 +02:00
// Process all queued tasks
TickQueuedTasks ();
2013-08-13 22:45:29 +02:00
}
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
{
2021-01-06 00:35:42 +00:00
auto & Client = * itr ;
Client -> ServerTick ( a_Dt );
if ( Client -> IsDestroyed ())
2012-06-14 13:06:06 +00:00
{
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
2021-01-06 00:35:42 +00:00
RemoveClients . push_back ( std :: move ( Client ));
2012-06-14 13:06:06 +00:00
itr = m_Clients . erase ( itr );
continue ;
}
2021-01-06 00:35:42 +00:00
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
{
2020-05-14 23:15:35 +01:00
for ( const 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 ;
}
2021-03-28 22:33:24 +01:00
m_TickThread . Start ();
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
{
2020-04-12 23:04:30 +01:00
bool Res = cRoot :: Get () -> DoWithPlayerByUUID (
a_Client . GetUUID (),
[ & ]( cPlayer & a_Player )
{
return cRoot :: Get () -> GetPluginManager () -> CallHookChat ( a_Player , a_Cmd );
}
);
return Res ;
2012-06-14 13:06:06 +00:00
}
2020-07-23 00:34:43 +01:00
void cServer :: QueueExecuteConsoleCommand ( const AString & a_Cmd , cCommandOutputCallback & a_Output )
{
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock ( m_CSPendingCommands );
m_PendingCommands . emplace_back ( a_Cmd , & a_Output );
}
2021-05-26 18:07:32 +02:00
void cServer :: ScheduleTask ( cTickTime a_DelayTicks , std :: function < void ( cServer & ) > a_Task )
{
const auto TargetTick = a_DelayTicks + m_UpTime ;
// Insert the task into the list of scheduled tasks
{
cCSLock Lock ( m_CSTasks );
m_Tasks . emplace_back ( TargetTick , std :: move ( a_Task ));
}
}
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
2020-08-27 15:55:11 +02:00
// "help" and "reload" are to be handled by Cuberite, so that they work no matter what
2013-11-13 15:56:40 +01:00
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
{
2020-09-28 00:15:03 +02:00
if ( split . size () > 1 )
{
cPluginManager :: Get () -> ReloadPlugin ( split [ 1 ]);
a_Output . Out ( "Plugin reload scheduled" );
}
else
{
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 ;
}
2020-08-27 15:55:11 +02:00
else if ( split [ 0 ] == "reloadweb" )
{
cRoot :: Get () -> GetWebAdmin () -> Reload ();
a_Output . Out ( "WebAdmin configuration reloaded" );
a_Output . Finished ();
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" )
{
2017-09-11 22:20:49 +01:00
cRoot :: Get () -> ForEachWorld ([]( cWorld & a_World )
2014-08-29 14:43:49 +01:00
{
2017-09-11 22:20:49 +01:00
a_World . ForEachEntity ([]( cEntity & a_Entity )
2014-08-29 14:43:49 +01:00
{
2017-09-11 22:20:49 +01:00
if ( ! a_Entity . IsPlayer ())
2014-08-29 14:43:49 +01:00
{
2017-09-11 22:20:49 +01:00
a_Entity . Destroy ();
2014-08-29 14:43:49 +01:00
}
return false ;
}
2017-09-11 22:20:49 +01:00
);
2014-08-29 14:43:49 +01:00
return false ;
}
2017-09-11 22:20:49 +01:00
);
2014-08-29 14:43:49 +01:00
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 ;
}
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 )
{
2016-06-12 18:11:40 +02: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 13:00:59 +00:00
cPluginManager * PlgMgr = cPluginManager :: Get ();
2016-06-12 18:11:40 +02:00
PlgMgr -> BindConsoleCommand ( "help" , nullptr , handler , "Shows the available commands" );
PlgMgr -> BindConsoleCommand ( "reload" , nullptr , handler , "Reloads all plugins" );
2020-08-27 15:55:11 +02:00
PlgMgr -> BindConsoleCommand ( "reloadweb" , nullptr , handler , "Reloads the webadmin configuration" );
2016-06-12 18:11:40 +02:00
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 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:
2020-05-14 23:15:35 +01:00
for ( const auto & srv : m_ServerHandles )
2015-01-24 20:17:00 +01:00
{
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:
2020-07-23 00:34:43 +01:00
m_TickThread . Stop ();
2012-06-14 13:06:06 +00:00
2020-09-21 12:12:09 +00:00
// Save all chunks in all worlds, wait for chunks to be sent to the ChunkStorage queue for each world:
cRoot :: Get () -> SaveAllChunksNow ();
2012-06-14 13:06:06 +00:00
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[]
}
2017-08-25 13:43:18 +01:00
void cServer :: AuthenticateUser ( int a_ClientID , const AString & a_Name , const cUUID & a_UUID , const Json :: Value & a_Properties )
2012-06-14 13:06:06 +00:00
{
cCSLock Lock ( m_CSClients );
2017-07-28 17:54:40 +01: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 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[]
}
2020-07-23 00:34:43 +01:00
void cServer :: TickCommands ( void )
{
decltype ( m_PendingCommands ) PendingCommands ;
{
cCSLock Lock ( m_CSPendingCommands );
std :: swap ( PendingCommands , m_PendingCommands );
}
// Execute any pending commands:
for ( const auto & Command : PendingCommands )
{
ExecuteConsoleCommand ( Command . first , * Command . second );
}
}
2021-05-26 18:07:32 +02:00
void cServer :: TickQueuedTasks ( void )
{
// Move the tasks to be executed to a seperate vector to avoid deadlocks on
// accessing m_Tasks
decltype ( m_Tasks ) Tasks ;
{
cCSLock Lock ( m_CSTasks );
if ( m_Tasks . empty ())
{
return ;
}
// Partition everything to be executed by returning false to move to end
// of list if time reached
auto MoveBeginIterator = std :: partition (
m_Tasks . begin (), m_Tasks . end (),
[ this ]( const decltype ( m_Tasks ) :: value_type & a_Task )
{
return a_Task . first >= m_UpTime ;
});
// Cut all the due tasks from m_Tasks into Tasks:
Tasks . insert (
Tasks . end (), std :: make_move_iterator ( MoveBeginIterator ),
std :: make_move_iterator ( m_Tasks . end ()));
m_Tasks . erase ( MoveBeginIterator , m_Tasks . end ());
}
// Execute each task:
for ( const auto & Task : Tasks )
{
Task . second ( * this );
} // for itr - m_Tasks[]
}