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 21:23:33 +00:00
#include "OSSupport/Timer.h"
2012-09-23 20:53:08 +00:00
#include "Mobs/Monster.h"
2012-09-23 21:23:33 +00:00
#include "OSSupport/Socket.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 "GroupManager.h"
#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
#include "MersenneTwister.h"
2013-11-27 07:40:59 +00:00
#include "inifile/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>
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
2012-10-19 22:09:33 +00: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 20:11:17 +01: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 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 ;
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
{
2013-08-11 19:46:27 +02:00
cTimer Timer ;
2012-06-14 13:06:06 +00:00
2013-08-19 22:23:25 +02:00
long long msPerTick = 50 ;
2013-08-11 19:46:27 +02: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 13:06:06 +00:00
2013-08-11 19:46:27 +02:00
LastTime = NowTime ;
}
}
2012-06-14 13:06:06 +00:00
2013-08-11 19:18:06 +02:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cServer:
cServer :: cServer ( void ) :
2013-09-21 20:44:16 +02:00
m_ListenThreadIPv4 ( * this , cSocket :: IPv4 , "Client IPv4" ),
m_ListenThreadIPv6 ( * this , cSocket :: IPv6 , "Client IPv6" ),
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 ),
m_TickThread ( * this )
2013-08-11 19:18:06 +02:00
{
}
2012-06-14 13:06:06 +00:00
void cServer :: ClientDestroying ( const cClientHandle * a_Client )
{
2014-01-19 19:31:43 +01:00
m_SocketThreads . RemoveClient ( a_Client );
2012-06-14 13:06:06 +00:00
}
void cServer :: NotifyClientWrite ( const cClientHandle * a_Client )
{
m_NotifyWriteThread . NotifyClientWrite ( a_Client );
}
2012-09-25 08:23:19 +00:00
void cServer :: WriteToClient ( const cClientHandle * a_Client , const AString & a_Data )
2012-06-14 13:06:06 +00:00
{
2012-09-25 08:23:19 +00:00
m_SocketThreads . Write ( a_Client , a_Data );
2012-06-14 13:06:06 +00:00
}
2012-09-25 08:23:19 +00:00
void cServer :: RemoveClient ( const cClientHandle * a_Client )
2012-06-14 13:06:06 +00:00
{
2012-09-25 08:23:19 +00:00
m_SocketThreads . RemoveClient ( a_Client );
2012-06-14 13:06:06 +00: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 ;
}
2012-11-11 14:23:47 +00:00
bool cServer :: InitServer ( cIniFile & a_SettingsIni )
2012-06-14 13:06:06 +00:00
{
2014-04-18 21:09:44 +02:00
m_Description = a_SettingsIni . GetValueSet ( "Server" , "Description" , "MCServer - in C++!" );
2013-11-04 21:51:24 +00:00
m_MaxPlayers = a_SettingsIni . GetValueSetI ( "Server" , "MaxPlayers" , 100 );
2013-11-05 21:15:39 +00:00
m_bIsHardcore = a_SettingsIni . GetValueSetB ( "Server" , "HardcoreEnabled" , false );
2013-08-14 10:24:34 +02:00
m_PlayerCount = 0 ;
m_PlayerCountDiff = 0 ;
2013-08-11 19:18:06 +02:00
2014-01-25 14:42:26 +00: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
2012-11-11 14:23:47 +00:00
if ( cSocket :: WSAStartup () != 0 ) // Only does anything on Windows, but whatever
2012-06-14 13:06:06 +00:00
{
LOGERROR ( "WSAStartup() != 0" );
return false ;
}
2013-03-05 09:53:29 +00:00
bool HasAnyPorts = false ;
2013-03-04 21:13:08 +00:00
AString Ports = a_SettingsIni . GetValueSet ( "Server" , "Port" , "25565" );
2013-03-05 09:53:29 +00:00
m_ListenThreadIPv4 . SetReuseAddr ( true );
if ( m_ListenThreadIPv4 . Initialize ( Ports ))
2012-06-14 13:06:06 +00:00
{
2013-03-05 09:53:29 +00:00
HasAnyPorts = true ;
}
Ports = a_SettingsIni . GetValueSet ( "Server" , "PortsIPv6" , "25565" );
m_ListenThreadIPv6 . SetReuseAddr ( true );
if ( m_ListenThreadIPv6 . Initialize ( Ports ))
{
HasAnyPorts = true ;
}
if ( ! HasAnyPorts )
{
LOGERROR ( "Couldn't open any ports. Aborting the server" );
2012-06-14 13:06:06 +00:00
return false ;
}
2013-06-27 15:14:20 +00:00
m_RCONServer . Initialize ( a_SettingsIni );
2012-06-14 13:06:06 +00:00
m_bIsConnected = true ;
2013-08-11 19:46:27 +02:00
m_ServerID = "-" ;
2014-01-28 23:53:07 +01:00
m_ShouldAuthenticate = a_SettingsIni . GetValueSetB ( "Authentication" , "Authenticate" , true );
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
}
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 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
}
m_NotifyWriteThread . Start ( this );
2012-08-30 21:06:13 +00:00
PrepareKeys ();
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 ;
}
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
}
2013-03-04 21:13:08 +00:00
void cServer :: OnConnectionAccepted ( cSocket & a_Socket )
2012-06-14 13:06:06 +00:00
{
2013-03-04 21:13:08 +00:00
if ( ! a_Socket . IsValid ())
2012-06-14 13:06:06 +00:00
{
return ;
}
2013-03-04 21:13:08 +00:00
const AString & ClientIP = a_Socket . GetIPString ();
2012-06-14 13:06:06 +00:00
if ( ClientIP . empty ())
{
LOGWARN ( "cServer: A client connected, but didn't present its IP, disconnecting." );
2013-03-04 21:13:08 +00:00
a_Socket . CloseSocket ();
2012-06-14 13:06:06 +00:00
return ;
}
2013-11-04 21:51:24 +00:00
LOGD ( "Client \" %s \" connected!" , ClientIP . c_str ());
2012-06-14 13:06:06 +00:00
2013-03-04 21:13:08 +00:00
cClientHandle * NewHandle = new cClientHandle ( & a_Socket , m_ClientViewDistance );
if ( ! m_SocketThreads . AddClient ( a_Socket , NewHandle ))
2012-06-14 13:06:06 +00:00
{
// For some reason SocketThreads have rejected the handle, clean it up
2013-03-04 21:13:08 +00:00
LOGERROR ( "Client \" %s \" cannot be handled, server probably unstable" , ClientIP . c_str ());
a_Socket . CloseSocket ();
2012-06-14 13:06:06 +00:00
delete NewHandle ;
return ;
}
cCSLock Lock ( m_CSClients );
2013-03-04 21:13:08 +00:00
m_Clients . push_back ( NewHandle );
}
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 ;
}
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 );
// Let the Root process all the queued commands:
2013-08-11 20:16:41 +02:00
cRoot :: Get () -> TickCommands ();
2013-08-11 19:46:27 +02: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 )
{
2012-06-14 13:06:06 +00:00
cClientHandleList RemoveClients ;
{
cCSLock Lock ( m_CSClients );
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)
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 13:06:06 +00:00
for ( cClientHandleList :: iterator itr = m_Clients . begin (); itr != m_Clients . end ();)
{
if (( * itr ) -> IsDestroyed ())
{
2013-08-13 22:45:29 +02:00
// Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 )
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[]
}
2013-08-13 22:45:29 +02:00
// Delete the clients that have been destroyed
2012-06-14 13:06:06 +00:00
for ( cClientHandleList :: iterator itr = RemoveClients . begin (); itr != RemoveClients . end (); ++ itr )
{
delete * itr ;
} // for itr - RemoveClients[]
}
2013-03-04 21:13:08 +00:00
bool cServer :: Start ( void )
2012-06-14 13:06:06 +00:00
{
2013-03-05 09:53:29 +00:00
if ( ! m_ListenThreadIPv4 . Start ())
{
return false ;
}
if ( ! m_ListenThreadIPv6 . Start ())
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
{
2012-08-18 10:38:15 +00: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
2013-02-15 13:00:59 +00:00
// Special handling: "stop" and "restart" are built in
2012-08-18 10:38:15 +00:00
if (( split [ 0 ]. compare ( "stop" ) == 0 ) || ( split [ 0 ]. compare ( "restart" ) == 0 ))
2012-06-14 13:06:06 +00:00
{
return ;
}
2013-02-15 13:00:59 +00: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 );
return ;
}
2014-02-09 00:13:25 +01:00
if ( split [ 0 ] == "reload" )
{
cPluginManager :: Get () -> ReloadPlugins ();
cRoot :: Get () -> ReloadGroups ();
return ;
}
2014-02-09 00:06:37 +01:00
if ( split [ 0 ] == "reloadplugins" )
2013-11-13 15:56:40 +01:00
{
cPluginManager :: Get () -> ReloadPlugins ();
return ;
}
2014-02-09 00:06:37 +01:00
if ( split [ 0 ] == "reloadgroups" )
{
cRoot :: Get () -> ReloadGroups ();
2014-02-21 14:53:46 +01:00
a_Output . Out ( "Groups reloaded!" );
a_Output . Finished ();
2014-02-09 00:13:25 +01:00
return ;
2014-02-09 00:06:37 +01:00
}
2013-11-13 15:56:40 +01: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):
2012-06-14 13:06:06 +00:00
if ( split [ 0 ]. compare ( "chunkstats" ) == 0 )
{
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 ;
}
2012-10-19 22:09:33 +00: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 19:04:39 +00:00
if ( split [ 0 ]. compare ( "killmem" ) == 0 )
{
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
2013-06-29 15:30:05 +00:00
if ( cPluginManager :: Get () -> ExecuteConsoleCommand ( split , a_Output ))
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 ;
}
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 ;
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 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 ;
}
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 14:43:14 -07: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[]
a_Output . Finished ();
2013-11-13 15:56:40 +01:00
}
2013-02-15 13:00:59 +00:00
void cServer :: BindBuiltInConsoleCommands ( void )
{
cPluginManager * PlgMgr = cPluginManager :: Get ();
2013-11-13 15:56:40 +01:00
PlgMgr -> BindConsoleCommand ( "help" , NULL , " - Shows the available commands" );
PlgMgr -> BindConsoleCommand ( "reload" , NULL , " - Reloads all plugins" );
2013-08-03 17:24:06 +01: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" );
2013-02-15 13:00:59 +00:00
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
2013-08-03 17:24:06 +01:00
PlgMgr -> BindConsoleCommand ( "dumpmem" , NULL , " - 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
{
2013-03-05 09:53:29 +00:00
m_ListenThreadIPv4 . Stop ();
m_ListenThreadIPv6 . Stop ();
2013-03-04 21:13:08 +00:00
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 ();
cCSLock Lock ( m_CSClients );
for ( ClientList :: iterator itr = m_Clients . begin (); itr != m_Clients . end (); ++ itr )
{
2012-06-19 17:34:22 +00:00
( * itr ) -> Destroy ();
2012-06-14 13:06:06 +00: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-04-13 13:04:56 +02:00
void cServer :: AuthenticateUser ( int a_ClientID , const AString & a_Name , const AString & a_UUID )
2012-06-14 13:06:06 +00:00
{
cCSLock Lock ( m_CSClients );
for ( ClientList :: iterator itr = m_Clients . begin (); itr != m_Clients . end (); ++ itr )
{
if (( * itr ) -> GetUniqueID () == a_ClientID )
{
2014-04-13 13:04:56 +02:00
( * itr ) -> Authenticate ( a_Name , a_UUID );
2013-08-13 22:45:29 +02:00
return ;
2012-06-14 13:06:06 +00:00
}
} // for itr - m_Clients[]
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2013-08-11 19:46:27 +02:00
// cServer::cNotifyWriteThread:
2012-06-14 13:06:06 +00: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 ();
}