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 17:23:33 -04:00
# include "OSSupport/Timer.h"
2012-09-23 16:53:08 -04:00
# include "Mobs/Monster.h"
2012-09-23 17:23:33 -04:00
# include "OSSupport/Socket.h"
2012-09-23 18:09:57 -04:00
# include "Root.h"
# include "World.h"
2012-06-14 09:06:06 -04:00
# include "ChunkDef.h"
2013-12-08 06:17:54 -05:00
# include "Bindings/PluginManager.h"
2012-09-23 18:09:57 -04:00
# include "GroupManager.h"
# 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"
2012-06-14 09:06:06 -04:00
# include "MersenneTwister.h"
2013-11-27 02:40:59 -05:00
# include "inifile/iniFile.h"
2014-03-11 10:01:17 -04:00
# include "Vector3.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-10-19 18:09:33 -04:00
// For the "dumpmem" server command:
/// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory
2014-02-20 14:11:17 -05:00
// _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.
// #define ENABLE_LEAK_FINDER
2012-10-19 18:09:33 -04: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 09:06:06 -04:00
typedef std : : list < cClientHandle * > ClientList ;
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
{
2013-08-11 13:46:27 -04:00
cTimer Timer ;
2012-06-14 09:06:06 -04:00
2013-08-19 16:23:25 -04:00
long long msPerTick = 50 ;
2013-08-11 13:46:27 -04:00
long long LastTime = Timer . GetNowTime ( ) ;
while ( ! m_ShouldTerminate )
{
long long NowTime = Timer . GetNowTime ( ) ;
float DeltaTime = ( float ) ( NowTime - LastTime ) ;
m_ShouldTerminate = ! m_Server . Tick ( DeltaTime ) ;
long long TickTime = Timer . GetNowTime ( ) - NowTime ;
if ( TickTime < msPerTick )
{
// Stretch tick time until it's at least msPerTick
cSleep : : MilliSleep ( ( unsigned int ) ( msPerTick - TickTime ) ) ;
}
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-07-18 03:18:00 -04:00
m_ListenThreadIPv4 ( * this , cSocket : : IPv4 , " Client " ) ,
m_ListenThreadIPv6 ( * this , cSocket : : IPv6 , " Client " ) ,
2014-05-19 08:34:34 -04:00
m_PlayerCount ( 0 ) ,
m_PlayerCountDiff ( 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
{
}
2012-06-14 09:06:06 -04:00
void cServer : : ClientDestroying ( const cClientHandle * a_Client )
{
2014-01-19 13:31:43 -05:00
m_SocketThreads . RemoveClient ( a_Client ) ;
2012-06-14 09:06:06 -04:00
}
void cServer : : NotifyClientWrite ( const cClientHandle * a_Client )
{
m_NotifyWriteThread . NotifyClientWrite ( a_Client ) ;
}
2012-09-25 04:23:19 -04:00
void cServer : : WriteToClient ( const cClientHandle * a_Client , const AString & a_Data )
2012-06-14 09:06:06 -04:00
{
2012-09-25 04:23:19 -04:00
m_SocketThreads . Write ( a_Client , a_Data ) ;
2012-06-14 09:06:06 -04:00
}
2012-09-25 04:23:19 -04:00
void cServer : : RemoveClient ( const cClientHandle * a_Client )
2012-06-14 09:06:06 -04:00
{
2012-09-25 04:23:19 -04:00
m_SocketThreads . RemoveClient ( a_Client ) ;
2012-06-14 09:06: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
}
2013-08-14 04:24:34 -04:00
void cServer : : PlayerCreated ( const cPlayer * a_Player )
{
2013-12-22 09:42:17 -05:00
UNUSED ( a_Player ) ;
2013-08-14 04:24:34 -04: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 13:11:54 -04:00
void cServer : : PlayerDestroying ( const cPlayer * a_Player )
2013-08-14 04:24:34 -04:00
{
2013-12-22 09:42:17 -05:00
UNUSED ( a_Player ) ;
2013-08-14 04:24:34 -04: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 ;
}
2012-11-11 09:23:47 -05:00
bool cServer : : InitServer ( cIniFile & a_SettingsIni )
2012-06-14 09:06:06 -04:00
{
2014-04-18 15:09:44 -04:00
m_Description = a_SettingsIni . GetValueSet ( " Server " , " Description " , " MCServer - in C++! " ) ;
2013-11-04 16:51:24 -05:00
m_MaxPlayers = a_SettingsIni . GetValueSetI ( " Server " , " MaxPlayers " , 100 ) ;
2013-11-05 16:15:39 -05:00
m_bIsHardcore = a_SettingsIni . GetValueSetB ( " Server " , " HardcoreEnabled " , false ) ;
2013-08-14 04:24:34 -04:00
m_PlayerCount = 0 ;
m_PlayerCountDiff = 0 ;
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
2014-07-17 13:13:23 -04:00
if ( cSocket : : WSAStartup ( ) ! = 0 ) // Only does anything on Windows, but whatever
2012-06-14 09:06:06 -04:00
{
LOGERROR ( " WSAStartup() != 0 " ) ;
return false ;
}
2013-03-05 04:53:29 -05:00
bool HasAnyPorts = false ;
2013-03-04 16:13:08 -05:00
AString Ports = a_SettingsIni . GetValueSet ( " Server " , " Port " , " 25565 " ) ;
2014-07-20 08:49:15 -04:00
m_ListenThreadIPv4 . SetReuseAddr ( true ) ;
2013-03-05 04:53:29 -05:00
if ( m_ListenThreadIPv4 . Initialize ( Ports ) )
2012-06-14 09:06:06 -04:00
{
2013-03-05 04:53:29 -05:00
HasAnyPorts = true ;
}
Ports = a_SettingsIni . GetValueSet ( " Server " , " PortsIPv6 " , " 25565 " ) ;
2014-07-20 08:49:15 -04:00
m_ListenThreadIPv6 . SetReuseAddr ( true ) ;
2013-03-05 04:53:29 -05:00
if ( m_ListenThreadIPv6 . Initialize ( Ports ) )
{
HasAnyPorts = true ;
}
if ( ! HasAnyPorts )
{
LOGERROR ( " Couldn't open any ports. Aborting the server " ) ;
2012-06-14 09:06:06 -04:00
return false ;
}
2013-06-27 11:14:20 -04:00
m_RCONServer . Initialize ( a_SettingsIni ) ;
2012-06-14 09:06:06 -04:00
m_bIsConnected = true ;
2013-08-11 13:46:27 -04:00
m_ServerID = " - " ;
2014-01-28 17:53:07 -05:00
m_ShouldAuthenticate = a_SettingsIni . GetValueSetB ( " Authentication " , " Authenticate " , true ) ;
if ( m_ShouldAuthenticate )
2012-11-11 09:23:47 -05:00
{
MTRand mtrand1 ;
2013-08-11 13:46:27 -04:00
unsigned int r1 = ( mtrand1 . randInt ( ) % 1147483647 ) + 1000000000 ;
unsigned int r2 = ( mtrand1 . randInt ( ) % 1147483647 ) + 1000000000 ;
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-07-11 07:13:10 -04:00
m_ShouldLoadOfflinePlayerData = a_SettingsIni . GetValueSetB ( " PlayerData " , " LoadOfflinePlayerData " , false ) ;
m_ShouldLoadNamedPlayerData = a_SettingsIni . GetValueSetB ( " PlayerData " , " LoadNamedPlayerData " , true ) ;
2012-11-11 09:23:47 -05:00
m_ClientViewDistance = a_SettingsIni . GetValueSetI ( " Server " , " DefaultViewDistance " , cClientHandle : : DEFAULT_VIEW_DISTANCE ) ;
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
}
m_NotifyWriteThread . Start ( this ) ;
2012-08-30 17:06:13 -04:00
PrepareKeys ( ) ;
2012-06-14 09:06:06 -04:00
return true ;
}
2014-04-19 11:53:02 -04:00
int cServer : : GetNumPlayers ( void ) const
2013-08-14 04:24:34 -04:00
{
cCSLock Lock ( m_CSPlayerCount ) ;
return m_PlayerCount ;
}
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
}
2013-03-04 16:13:08 -05:00
void cServer : : OnConnectionAccepted ( cSocket & a_Socket )
2012-06-14 09:06:06 -04:00
{
2013-03-04 16:13:08 -05:00
if ( ! a_Socket . IsValid ( ) )
2012-06-14 09:06:06 -04:00
{
return ;
}
2013-03-04 16:13:08 -05:00
const AString & ClientIP = a_Socket . GetIPString ( ) ;
2012-06-14 09:06:06 -04:00
if ( ClientIP . empty ( ) )
{
LOGWARN ( " cServer: A client connected, but didn't present its IP, disconnecting. " ) ;
2013-03-04 16:13:08 -05:00
a_Socket . CloseSocket ( ) ;
2012-06-14 09:06:06 -04:00
return ;
}
2013-11-04 16:51:24 -05:00
LOGD ( " Client \" %s \" connected! " , ClientIP . c_str ( ) ) ;
2012-06-14 09:06:06 -04:00
2013-03-04 16:13:08 -05:00
cClientHandle * NewHandle = new cClientHandle ( & a_Socket , m_ClientViewDistance ) ;
if ( ! m_SocketThreads . AddClient ( a_Socket , NewHandle ) )
2012-06-14 09:06:06 -04:00
{
// For some reason SocketThreads have rejected the handle, clean it up
2013-03-04 16:13:08 -05:00
LOGERROR ( " Client \" %s \" cannot be handled, server probably unstable " , ClientIP . c_str ( ) ) ;
a_Socket . CloseSocket ( ) ;
2012-06-14 09:06:06 -04:00
delete NewHandle ;
2014-06-19 04:49:56 -04:00
NewHandle = NULL ;
2012-06-14 09:06:06 -04:00
return ;
}
cCSLock Lock ( m_CSClients ) ;
2013-03-04 16:13:08 -05:00
m_Clients . push_back ( NewHandle ) ;
}
2012-06-14 09:06:06 -04:00
bool cServer : : Tick ( float a_Dt )
{
2013-08-14 04:24:34 -04: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 ;
}
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 ) ;
// Let the Root process all the queued commands:
2013-08-11 14:16:41 -04:00
cRoot : : Get ( ) - > TickCommands ( ) ;
2013-08-11 13:46:27 -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 )
{
2012-06-14 09:06:06 -04:00
cClientHandleList RemoveClients ;
{
cCSLock Lock ( m_CSClients ) ;
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)
for ( cClientHandleList : : const_iterator itr = m_ClientsToRemove . begin ( ) , end = m_ClientsToRemove . end ( ) ; itr ! = end ; + + itr )
{
m_Clients . remove ( * itr ) ;
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove . clear ( ) ;
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
2012-06-14 09:06:06 -04:00
for ( cClientHandleList : : iterator itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; )
{
if ( ( * itr ) - > IsDestroyed ( ) )
{
2014-07-21 09:19:48 -04:00
// Remove the client later, when CS is not held, to avoid deadlock: http://forum.mc-server.org/showthread.php?tid=374
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[]
}
2013-08-13 16:45:29 -04:00
// Delete the clients that have been destroyed
2012-06-14 09:06:06 -04:00
for ( cClientHandleList : : iterator itr = RemoveClients . begin ( ) ; itr ! = RemoveClients . end ( ) ; + + itr )
{
delete * itr ;
2014-07-17 13:13:23 -04:00
} // for itr - RemoveClients[]
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
{
2013-03-05 04:53:29 -05:00
if ( ! m_ListenThreadIPv4 . Start ( ) )
{
return false ;
}
if ( ! m_ListenThreadIPv6 . Start ( ) )
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
{
2012-08-18 06:38:15 -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
2013-02-15 08:00:59 -05:00
// Special handling: "stop" and "restart" are built in
2012-08-18 06:38:15 -04:00
if ( ( split [ 0 ] . compare ( " stop " ) = = 0 ) | | ( split [ 0 ] . compare ( " restart " ) = = 0 ) )
2012-06-14 09:06:06 -04:00
{
return ;
}
2013-02-15 08:00:59 -05: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 ) ;
return ;
}
2014-02-08 18:13:25 -05:00
if ( split [ 0 ] = = " reload " )
{
cPluginManager : : Get ( ) - > ReloadPlugins ( ) ;
cRoot : : Get ( ) - > ReloadGroups ( ) ;
return ;
}
2014-02-08 18:06:37 -05:00
if ( split [ 0 ] = = " reloadplugins " )
2013-11-13 09:56:40 -05:00
{
cPluginManager : : Get ( ) - > ReloadPlugins ( ) ;
return ;
}
2014-02-08 18:06:37 -05:00
if ( split [ 0 ] = = " reloadgroups " )
{
cRoot : : Get ( ) - > ReloadGroups ( ) ;
2014-02-21 08:53:46 -05:00
a_Output . Out ( " Groups reloaded! " ) ;
a_Output . Finished ( ) ;
2014-02-08 18:13:25 -05:00
return ;
2014-02-08 18:06:37 -05:00
}
2014-05-17 13:39:16 -04:00
if ( split [ 0 ] = = " load " )
2014-05-17 12:38:33 -04:00
{
2014-05-17 13:39:16 -04:00
if ( split . size ( ) > 1 )
{
cPluginManager : : Get ( ) - > LoadPlugin ( split [ 1 ] ) ;
2014-05-18 11:16:02 -04:00
2014-05-17 13:39:16 -04:00
return ;
}
else
{
a_Output . Out ( " No plugin given! Command: load <pluginname> " ) ;
a_Output . Finished ( ) ;
return ;
}
2014-05-17 12:38:33 -04:00
}
2014-05-18 11:16:02 -04:00
2014-05-17 13:39:16 -04:00
if ( split [ 0 ] = = " unload " )
{
if ( split . size ( ) > 1 )
{
2014-05-18 11:16:02 -04:00
cPluginManager : : Get ( ) - > RemovePlugin ( cPluginManager : : Get ( ) - > GetPlugin ( split [ 1 ] ) ) ;
return ;
2014-05-17 13:39:16 -04:00
}
else
{
2014-05-18 11:16:02 -04:00
a_Output . Out ( " No plugin given! Command: unload <pluginname> " ) ;
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):
2012-06-14 09:06:06 -04:00
if ( split [ 0 ] . compare ( " chunkstats " ) = = 0 )
{
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 ;
}
2012-10-19 18:09:33 -04:00
# if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
if ( split [ 0 ] . compare ( " dumpmem " ) = = 0 )
{
LeakFinderXmlOutput Output ( " memdump.xml " ) ;
DumpUsedMemory ( & Output ) ;
return ;
}
2013-03-23 15:04:39 -04:00
if ( split [ 0 ] . compare ( " killmem " ) = = 0 )
{
2014-01-07 10:00:19 -05:00
for ( ; ; )
2013-03-23 15:04:39 -04:00
{
new char [ 100 * 1024 * 1024 ] ; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS
}
}
2012-10-19 18:09:33 -04:00
# endif
2014-01-21 08:58:17 -05:00
2013-06-29 11:30:05 -04:00
if ( cPluginManager : : Get ( ) - > ExecuteConsoleCommand ( split , a_Output ) )
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 ;
}
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 ;
class cCallback :
public cPluginManager : : cCommandEnumCallback
{
public :
cCallback ( void ) : m_MaxLen ( 0 ) { }
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 ;
}
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 ;
2014-03-11 17:43:14 -04: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[]
a_Output . Finished ( ) ;
2013-11-13 09:56:40 -05:00
}
2013-02-15 08:00:59 -05:00
void cServer : : BindBuiltInConsoleCommands ( void )
{
cPluginManager * PlgMgr = cPluginManager : : Get ( ) ;
2013-11-13 09:56:40 -05:00
PlgMgr - > BindConsoleCommand ( " help " , NULL , " - Shows the available commands " ) ;
PlgMgr - > BindConsoleCommand ( " reload " , NULL , " - Reloads all plugins " ) ;
2013-08-03 12:24:06 -04:00
PlgMgr - > BindConsoleCommand ( " restart " , NULL , " - Restarts the server cleanly " ) ;
PlgMgr - > BindConsoleCommand ( " stop " , NULL , " - Stops the server cleanly " ) ;
PlgMgr - > BindConsoleCommand ( " chunkstats " , NULL , " - Displays detailed chunk memory statistics " ) ;
2014-05-17 13:39:16 -04:00
PlgMgr - > BindConsoleCommand ( " load <pluginname> " , NULL , " - Adds and enables the specified plugin " ) ;
PlgMgr - > BindConsoleCommand ( " unload <pluginname> " , NULL , " - Disables the specified plugin " ) ;
2013-02-15 08:00:59 -05:00
# if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
2013-08-03 12:24:06 -04:00
PlgMgr - > BindConsoleCommand ( " dumpmem " , NULL , " - Dumps all used memory blocks together with their callstacks into memdump.xml " ) ;
2013-11-04 16:51:24 -05:00
# endif
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
{
2013-03-05 04:53:29 -05:00
m_ListenThreadIPv4 . Stop ( ) ;
m_ListenThreadIPv6 . Stop ( ) ;
2013-03-04 16:13:08 -05:00
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 ( ) ;
cCSLock Lock ( m_CSClients ) ;
2014-07-21 09:19:48 -04:00
for ( ClientList : : iterator 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
delete * itr ;
}
m_Clients . clear ( ) ;
}
void cServer : : KickUser ( int a_ClientID , const AString & a_Reason )
{
cCSLock Lock ( m_CSClients ) ;
for ( ClientList : : iterator itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; + + itr )
{
if ( ( * itr ) - > GetUniqueID ( ) = = a_ClientID )
{
( * itr ) - > Kick ( a_Reason ) ;
}
} // for itr - m_Clients[]
}
2014-07-15 19:03:47 -04:00
void cServer : : 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
{
cCSLock Lock ( m_CSClients ) ;
for ( ClientList : : iterator itr = m_Clients . begin ( ) ; itr ! = m_Clients . end ( ) ; + + itr )
{
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[]
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-08-11 13:46:27 -04:00
// cServer::cNotifyWriteThread:
2012-06-14 09:06:06 -04:00
cServer : : cNotifyWriteThread : : cNotifyWriteThread ( void ) :
super ( " ClientPacketThread " ) ,
m_Server ( NULL )
{
}
cServer : : cNotifyWriteThread : : ~ cNotifyWriteThread ( )
{
m_ShouldTerminate = true ;
m_Event . Set ( ) ;
Wait ( ) ;
}
bool cServer : : cNotifyWriteThread : : Start ( cServer * a_Server )
{
m_Server = a_Server ;
return super : : Start ( ) ;
}
void cServer : : cNotifyWriteThread : : Execute ( void )
{
cClientHandleList Clients ;
while ( ! m_ShouldTerminate )
{
cCSLock Lock ( m_CS ) ;
while ( m_Clients . size ( ) = = 0 )
{
cCSUnlock Unlock ( Lock ) ;
m_Event . Wait ( ) ;
if ( m_ShouldTerminate )
{
return ;
}
}
// Copy the clients to notify and unlock the CS:
Clients . splice ( Clients . begin ( ) , m_Clients ) ;
Lock . Unlock ( ) ;
for ( cClientHandleList : : iterator itr = Clients . begin ( ) ; itr ! = Clients . end ( ) ; + + itr )
{
m_Server - > m_SocketThreads . NotifyWrite ( * itr ) ;
} // for itr - Clients[]
Clients . clear ( ) ;
} // while (!mShouldTerminate)
}
void cServer : : cNotifyWriteThread : : NotifyClientWrite ( const cClientHandle * a_Client )
{
{
cCSLock Lock ( m_CS ) ;
m_Clients . remove ( const_cast < cClientHandle * > ( a_Client ) ) ; // Put it there only once
m_Clients . push_back ( const_cast < cClientHandle * > ( a_Client ) ) ;
}
m_Event . Set ( ) ;
}