2011-12-14 00:23:17 +03:00
# include "StdInc.h"
2013-01-12 13:32:03 +03:00
# include <boost/asio.hpp>
2013-07-28 17:49:50 +03:00
# include "../lib/filesystem/Filesystem.h"
2013-04-07 13:48:07 +03:00
# include "../lib/mapping/CCampaignHandler.h"
2011-12-14 00:23:17 +03:00
# include "../lib/CThreadHelper.h"
2016-09-10 02:32:40 +02:00
# include "../lib/serializer/Connection.h"
2012-08-10 16:07:53 +03:00
# include "../lib/CModHandler.h"
2010-12-20 23:22:53 +02:00
# include "../lib/CArtHandler.h"
# include "../lib/CGeneralTextHandler.h"
# include "../lib/CHeroHandler.h"
# include "../lib/CTownHandler.h"
# include "../lib/CBuildingHandler.h"
2015-02-02 10:25:26 +02:00
# include "../lib/spells/CSpellHandler.h"
2010-12-20 23:22:53 +02:00
# include "../lib/CCreatureHandler.h"
2008-07-09 20:22:28 +03:00
# include "zlib.h"
2008-07-25 20:28:28 +03:00
# include "CVCMIServer.h"
2011-12-14 00:23:17 +03:00
# include "../lib/StartInfo.h"
2013-04-07 13:48:07 +03:00
# include "../lib/mapping/CMap.h"
2016-09-10 02:32:40 +02:00
# include "../lib/rmg/CMapGenOptions.h"
2017-05-25 19:57:20 +02:00
# ifdef VCMI_ANDROID
# include "lib/CAndroidVMHelper.h"
# else
2008-09-12 11:51:46 +03:00
# include "../lib/Interprocess.h"
2014-02-20 21:53:18 +03:00
# endif
2008-07-25 20:28:28 +03:00
# include "../lib/VCMI_Lib.h"
2009-10-10 08:47:59 +03:00
# include "../lib/VCMIDirs.h"
2008-07-25 20:28:28 +03:00
# include "CGameHandler.h"
2013-04-07 13:48:07 +03:00
# include "../lib/mapping/CMapInfo.h"
2011-12-14 00:23:17 +03:00
# include "../lib/GameConstants.h"
2013-04-10 20:18:01 +03:00
# include "../lib/logging/CBasicLogConfigurator.h"
# include "../lib/CConfigHandler.h"
2013-04-21 19:38:31 +03:00
# include "../lib/ScopeGuard.h"
2011-12-14 00:23:17 +03:00
2012-02-20 00:03:43 +03:00
# include "../lib/UnlockGuard.h"
2010-10-24 14:35:14 +03:00
2014-08-26 13:19:04 +03:00
# if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
2013-12-28 15:47:55 +03:00
# include <execinfo.h>
# endif
2009-07-31 23:10:22 +03:00
std : : string NAME_AFFIX = " server " ;
2011-12-14 00:23:17 +03:00
std : : string NAME = GameConstants : : VCMI_VERSION + std : : string ( " ( " ) + NAME_AFFIX + ' ) ' ; //application name
2014-08-26 13:19:04 +03:00
# ifndef VCMI_ANDROID
2008-09-12 11:51:46 +03:00
namespace intpr = boost : : interprocess ;
2014-02-20 21:53:18 +03:00
# endif
2008-08-04 12:05:52 +03:00
bool end2 = false ;
2008-07-25 20:28:28 +03:00
2013-06-21 23:59:32 +03:00
boost : : program_options : : variables_map cmdLineOptions ;
2009-04-15 17:03:31 +03:00
/*
* CVCMIServer . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2015-12-29 01:14:08 +02:00
static void vaccept ( boost : : asio : : ip : : tcp : : acceptor * ac , boost : : asio : : ip : : tcp : : socket * s , boost : : system : : error_code * error )
2008-09-12 11:51:46 +03:00
{
ac - > accept ( * s , * error ) ;
}
2010-10-24 14:35:14 +03:00
2013-06-26 14:18:27 +03:00
CPregameServer : : CPregameServer ( CConnection * Host , TAcceptor * Acceptor /*= nullptr*/ )
: host ( Host ) , listeningThreads ( 0 ) , acceptor ( Acceptor ) , upcomingConnection ( nullptr ) ,
curmap ( nullptr ) , curStartInfo ( nullptr ) , state ( RUNNING )
2010-10-24 14:35:14 +03:00
{
initConnection ( host ) ;
}
void CPregameServer : : handleConnection ( CConnection * cpc )
{
2013-02-14 18:19:35 +03:00
setThreadName ( " CPregameServer::handleConnection " ) ;
2010-10-24 14:35:14 +03:00
try
{
while ( ! cpc - > receivedStop )
{
2013-06-26 14:18:27 +03:00
CPackForSelectionScreen * cpfs = nullptr ;
2010-10-24 14:35:14 +03:00
* cpc > > cpfs ;
2016-03-12 03:41:27 +02:00
logNetwork - > infoStream ( ) < < " Got package to announce " < < typeid ( * cpfs ) . name ( ) < < " from " < < * cpc ;
2010-10-24 14:35:14 +03:00
boost : : unique_lock < boost : : recursive_mutex > queueLock ( mx ) ;
2015-10-31 17:04:06 +02:00
bool quitting = dynamic_ptr_cast < QuitMenuWithoutStarting > ( cpfs ) ,
startingGame = dynamic_ptr_cast < StartWithCurrentSettings > ( cpfs ) ;
2010-10-24 14:35:14 +03:00
if ( quitting | | startingGame ) //host leaves main menu or wants to start game -> we end
{
cpc - > receivedStop = true ;
if ( ! cpc - > sendStop )
sendPack ( cpc , * cpfs ) ;
if ( cpc = = host )
toAnnounce . push_back ( cpfs ) ;
}
else
toAnnounce . push_back ( cpfs ) ;
if ( startingGame )
{
//wait for sending thread to announce start
2012-02-20 00:03:43 +03:00
auto unlock = vstd : : makeUnlockGuard ( mx ) ;
2010-10-24 14:35:14 +03:00
while ( state = = RUNNING ) boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 50 ) ) ;
}
2016-12-04 11:54:26 +02:00
else if ( quitting ) // Server must be stopped if host is leaving from lobby to avoid crash
{
end2 = true ;
}
2010-10-24 14:35:14 +03:00
}
2014-06-17 14:57:47 +03:00
}
2010-10-24 14:35:14 +03:00
catch ( const std : : exception & e )
{
boost : : unique_lock < boost : : recursive_mutex > queueLock ( mx ) ;
2016-03-12 03:41:27 +02:00
logNetwork - > errorStream ( ) < < * cpc < < " dies... \n What happened: " < < e . what ( ) ;
2010-10-24 14:35:14 +03:00
}
boost : : unique_lock < boost : : recursive_mutex > queueLock ( mx ) ;
if ( state ! = ENDING_AND_STARTING_GAME )
{
connections - = cpc ;
//notify other players about leaving
2013-06-29 16:05:48 +03:00
auto pl = new PlayerLeft ( ) ;
2010-10-24 14:35:14 +03:00
pl - > playerID = cpc - > connectionID ;
announceTxt ( cpc - > name + " left the game " ) ;
toAnnounce . push_back ( pl ) ;
2013-11-03 15:51:25 +03:00
if ( connections . empty ( ) )
2010-10-24 14:35:14 +03:00
{
2016-08-30 00:11:54 +02:00
logNetwork - > error ( " Last connection lost, server will close itself... " ) ;
2010-10-24 14:35:14 +03:00
boost : : this_thread : : sleep ( boost : : posix_time : : seconds ( 2 ) ) ; //we should never be hasty when networking
state = ENDING_WITHOUT_START ;
}
}
2016-03-12 03:41:27 +02:00
logNetwork - > infoStream ( ) < < " Thread listening for " < < * cpc < < " ended " ;
2010-10-24 14:35:14 +03:00
listeningThreads - - ;
2011-12-14 00:23:17 +03:00
vstd : : clear_pointer ( cpc - > handler ) ;
2010-10-24 14:35:14 +03:00
}
void CPregameServer : : run ( )
{
startListeningThread ( host ) ;
start_async_accept ( ) ;
while ( state = = RUNNING )
{
{
boost : : unique_lock < boost : : recursive_mutex > myLock ( mx ) ;
2013-11-03 15:51:25 +03:00
while ( ! toAnnounce . empty ( ) )
2010-10-24 14:35:14 +03:00
{
processPack ( toAnnounce . front ( ) ) ;
toAnnounce . pop_front ( ) ;
}
// //we end sending thread if we ordered all our connections to stop
// ending = true;
2013-06-29 16:05:48 +03:00
// for(CPregameConnection *pc : connections)
2010-10-24 14:35:14 +03:00
// if(!pc->sendStop)
// ending = false;
if ( state ! = RUNNING )
{
2016-08-30 00:11:54 +02:00
logNetwork - > info ( " Stopping listening for connections... " ) ;
2010-10-24 14:35:14 +03:00
acceptor - > close ( ) ;
}
if ( acceptor )
{
2011-08-05 02:49:32 +03:00
acceptor - > get_io_service ( ) . reset ( ) ;
acceptor - > get_io_service ( ) . poll ( ) ;
2010-10-24 14:35:14 +03:00
}
} //frees lock
boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 50 ) ) ;
}
2016-08-30 00:11:54 +02:00
logNetwork - > info ( " Thread handling connections ended " ) ;
2010-10-24 14:35:14 +03:00
if ( state = = ENDING_AND_STARTING_GAME )
{
2016-08-30 00:11:54 +02:00
logNetwork - > info ( " Waiting for listening thread to finish... " ) ;
2010-10-24 14:35:14 +03:00
while ( listeningThreads ) boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 50 ) ) ;
2016-08-30 00:11:54 +02:00
logNetwork - > info ( " Preparing new game " ) ;
2010-10-24 14:35:14 +03:00
}
}
CPregameServer : : ~ CPregameServer ( )
{
delete acceptor ;
delete upcomingConnection ;
2013-06-29 16:05:48 +03:00
for ( CPackForSelectionScreen * pack : toAnnounce )
2010-10-24 14:35:14 +03:00
delete pack ;
toAnnounce . clear ( ) ;
//TODO pregameconnections
}
void CPregameServer : : connectionAccepted ( const boost : : system : : error_code & ec )
{
if ( ec )
{
2016-08-30 00:11:54 +02:00
logNetwork - > info ( " Something wrong during accepting: %s " , ec . message ( ) ) ;
2010-10-24 14:35:14 +03:00
return ;
}
2016-12-04 11:54:26 +02:00
try
{
logNetwork - > info ( " We got a new connection! :) " ) ;
std : : string name = NAME ;
CConnection * pc = new CConnection ( upcomingConnection , name . append ( " STATE_PREGAME " ) ) ;
initConnection ( pc ) ;
upcomingConnection = nullptr ;
2010-10-24 14:35:14 +03:00
2016-12-04 11:54:26 +02:00
startListeningThread ( pc ) ;
2010-10-24 14:35:14 +03:00
2016-12-04 11:54:26 +02:00
* pc < < ( ui8 ) pc - > connectionID < < curmap ;
2013-07-02 19:48:01 +03:00
2016-12-04 11:54:26 +02:00
announceTxt ( pc - > name + " joins the game " ) ;
auto pj = new PlayerJoined ( ) ;
pj - > playerName = pc - > name ;
pj - > connectionID = pc - > connectionID ;
toAnnounce . push_back ( pj ) ;
}
catch ( std : : exception & e )
{
upcomingConnection = nullptr ;
logNetwork - > info ( " I guess it was just my imagination! " ) ;
}
2010-10-24 14:35:14 +03:00
start_async_accept ( ) ;
}
void CPregameServer : : start_async_accept ( )
{
assert ( ! upcomingConnection ) ;
assert ( acceptor ) ;
2011-08-05 02:49:32 +03:00
upcomingConnection = new TSocket ( acceptor - > get_io_service ( ) ) ;
2014-08-04 21:33:59 +03:00
acceptor - > async_accept ( * upcomingConnection , std : : bind ( & CPregameServer : : connectionAccepted , this , _1 ) ) ;
2010-10-24 14:35:14 +03:00
}
void CPregameServer : : announceTxt ( const std : : string & txt , const std : : string & playerName /*= "system"*/ )
{
2016-08-30 00:11:54 +02:00
logNetwork - > info ( " %s says: %s " , playerName , txt ) ;
2010-10-24 14:35:14 +03:00
ChatMessage cm ;
cm . playerName = playerName ;
cm . message = txt ;
boost : : unique_lock < boost : : recursive_mutex > queueLock ( mx ) ;
toAnnounce . push_front ( new ChatMessage ( cm ) ) ;
}
void CPregameServer : : announcePack ( const CPackForSelectionScreen & pack )
{
2013-06-29 16:05:48 +03:00
for ( CConnection * pc : connections )
2010-10-24 14:35:14 +03:00
sendPack ( pc , pack ) ;
}
void CPregameServer : : sendPack ( CConnection * pc , const CPackForSelectionScreen & pack )
{
if ( ! pc - > sendStop )
{
2016-03-12 03:41:27 +02:00
logNetwork - > infoStream ( ) < < " \t Sending pack of type " < < typeid ( pack ) . name ( ) < < " to " < < * pc ;
2010-10-24 14:35:14 +03:00
* pc < < & pack ;
}
2015-10-31 17:04:06 +02:00
if ( dynamic_ptr_cast < QuitMenuWithoutStarting > ( & pack ) )
2010-10-24 14:35:14 +03:00
{
pc - > sendStop = true ;
}
2015-10-31 17:04:06 +02:00
else if ( dynamic_ptr_cast < StartWithCurrentSettings > ( & pack ) )
2010-10-24 14:35:14 +03:00
{
pc - > sendStop = true ;
}
}
void CPregameServer : : processPack ( CPackForSelectionScreen * pack )
{
2015-10-31 17:04:06 +02:00
if ( dynamic_ptr_cast < CPregamePackToHost > ( pack ) )
2010-10-24 14:35:14 +03:00
{
sendPack ( host , * pack ) ;
}
2015-10-31 17:04:06 +02:00
else if ( SelectMap * sm = dynamic_ptr_cast < SelectMap > ( pack ) )
2010-10-24 14:35:14 +03:00
{
2011-12-14 00:23:17 +03:00
vstd : : clear_pointer ( curmap ) ;
2010-10-24 14:35:14 +03:00
curmap = sm - > mapInfo ;
sm - > free = false ;
announcePack ( * pack ) ;
}
2015-10-31 17:04:06 +02:00
else if ( UpdateStartOptions * uso = dynamic_ptr_cast < UpdateStartOptions > ( pack ) )
2010-10-24 14:35:14 +03:00
{
2011-12-14 00:23:17 +03:00
vstd : : clear_pointer ( curStartInfo ) ;
2010-10-24 14:35:14 +03:00
curStartInfo = uso - > options ;
uso - > free = false ;
announcePack ( * pack ) ;
}
2015-10-31 17:04:06 +02:00
else if ( dynamic_ptr_cast < StartWithCurrentSettings > ( pack ) )
2010-10-24 14:35:14 +03:00
{
state = ENDING_AND_STARTING_GAME ;
announcePack ( * pack ) ;
}
else
announcePack ( * pack ) ;
delete pack ;
}
void CPregameServer : : initConnection ( CConnection * c )
{
* c > > c - > name ;
connections . insert ( c ) ;
2016-08-30 00:11:54 +02:00
logNetwork - > info ( " Pregame connection with player %s established! " , c - > name ) ;
2010-10-24 14:35:14 +03:00
}
void CPregameServer : : startListeningThread ( CConnection * pc )
2015-10-31 17:04:06 +02:00
{
2010-10-24 14:35:14 +03:00
listeningThreads + + ;
2013-02-14 18:19:35 +03:00
pc - > enterPregameConnectionMode ( ) ;
2010-10-24 14:35:14 +03:00
pc - > handler = new boost : : thread ( & CPregameServer : : handleConnection , this , pc ) ;
}
2008-07-25 20:28:28 +03:00
CVCMIServer : : CVCMIServer ( )
2016-12-04 11:54:26 +02:00
: port ( 3030 ) , io ( new boost : : asio : : io_service ( ) ) , firstConnection ( nullptr )
2008-07-02 11:39:56 +03:00
{
2016-08-30 00:11:54 +02:00
logNetwork - > trace ( " CVCMIServer created! " ) ;
2016-12-04 11:54:26 +02:00
if ( cmdLineOptions . count ( " port " ) )
port = cmdLineOptions [ " port " ] . as < ui16 > ( ) ;
logNetwork - > info ( " Port %d will be used " , port ) ;
try
{
acceptor = new TAcceptor ( * io , boost : : asio : : ip : : tcp : : endpoint ( boost : : asio : : ip : : tcp : : v4 ( ) , port ) ) ;
}
catch ( . . . )
{
logNetwork - > info ( " Port %d is busy, trying to use random port instead " , port ) ;
2017-05-31 06:13:24 +02:00
if ( cmdLineOptions . count ( " run-by-client " ) & & ! cmdLineOptions . count ( " use-shm " ) )
{
logNetwork - > error ( " Cant pass port number to client without shared memory! " , port ) ;
exit ( 0 ) ;
}
2016-12-04 11:54:26 +02:00
acceptor = new TAcceptor ( * io , boost : : asio : : ip : : tcp : : endpoint ( boost : : asio : : ip : : tcp : : v4 ( ) , 0 ) ) ;
port = acceptor - > local_endpoint ( ) . port ( ) ;
}
logNetwork - > info ( " Listening for connections at port %d " , port ) ;
2008-07-25 20:28:28 +03:00
}
CVCMIServer : : ~ CVCMIServer ( )
{
2008-07-27 20:07:37 +03:00
//delete io;
//delete acceptor;
2013-04-21 20:06:24 +03:00
//delete firstConnection;
2008-07-25 20:28:28 +03:00
}
2010-10-24 14:35:14 +03:00
CGameHandler * CVCMIServer : : initGhFromHostingConnection ( CConnection & c )
2008-07-25 20:28:28 +03:00
{
2013-06-29 16:05:48 +03:00
auto gh = new CGameHandler ( ) ;
2010-10-24 14:35:14 +03:00
StartInfo si ;
c > > si ; //get start options
2010-05-08 21:56:38 +03:00
2013-05-21 22:08:06 +03:00
if ( ! si . createRandomMap ( ) )
2008-07-09 20:22:28 +03:00
{
2012-11-20 20:53:45 +03:00
bool mapFound = CResourceHandler : : get ( ) - > existsResource ( ResourceID ( si . mapname , EResType : : MAP ) ) ;
//TODO some checking for campaigns
if ( ! mapFound & & si . mode = = StartInfo : : NEW_GAME )
{
c < < ui8 ( 1 ) ; //WRONG!
return nullptr ;
}
2008-07-02 11:39:56 +03:00
}
2008-07-25 20:28:28 +03:00
2012-11-20 20:53:45 +03:00
c < < ui8 ( 0 ) ; //OK!
2012-03-27 23:08:54 +03:00
gh - > init ( & si ) ;
2010-10-24 14:35:14 +03:00
gh - > conns . insert ( & c ) ;
2008-07-25 20:28:28 +03:00
2010-10-24 14:35:14 +03:00
return gh ;
}
void CVCMIServer : : newGame ( )
{
CConnection & c = * firstConnection ;
ui8 clients ;
2015-10-31 17:04:06 +02:00
c > > clients ; //how many clients should be connected
2010-10-24 14:35:14 +03:00
assert ( clients = = 1 ) ; //multi goes now by newPregame, TODO: custom lobbies
CGameHandler * gh = initGhFromHostingConnection ( c ) ;
2013-04-21 19:38:31 +03:00
auto onExit = vstd : : makeScopeGuard ( [ & ] ( )
{
vstd : : clear_pointer ( gh ) ;
} ) ;
2010-10-24 14:35:14 +03:00
gh - > run ( false ) ;
}
void CVCMIServer : : newPregame ( )
{
2013-06-29 16:05:48 +03:00
auto cps = new CPregameServer ( firstConnection , acceptor ) ;
2010-10-24 14:35:14 +03:00
cps - > run ( ) ;
if ( cps - > state = = CPregameServer : : ENDING_WITHOUT_START )
2008-07-02 11:39:56 +03:00
{
2010-10-24 14:35:14 +03:00
delete cps ;
return ;
2008-07-03 18:03:32 +03:00
}
2008-07-25 20:28:28 +03:00
2010-10-24 14:35:14 +03:00
if ( cps - > state = = CPregameServer : : ENDING_AND_STARTING_GAME )
{
CGameHandler gh ;
gh . conns = cps - > connections ;
2012-03-27 23:08:54 +03:00
gh . init ( cps - > curStartInfo ) ;
2010-10-24 14:35:14 +03:00
2013-06-29 16:05:48 +03:00
for ( CConnection * c : gh . conns )
2010-10-24 14:35:14 +03:00
c - > addStdVecItems ( gh . gs ) ;
gh . run ( false ) ;
}
2008-07-25 20:28:28 +03:00
}
2010-10-24 14:35:14 +03:00
2008-07-25 20:28:28 +03:00
void CVCMIServer : : start ( )
{
2014-08-26 13:19:04 +03:00
# ifndef VCMI_ANDROID
2013-06-26 14:18:27 +03:00
ServerReady * sr = nullptr ;
2008-09-12 11:51:46 +03:00
intpr : : mapped_region * mr ;
2017-05-31 06:13:24 +02:00
if ( cmdLineOptions . count ( " use-shm " ) )
2008-09-12 11:51:46 +03:00
{
2017-05-31 06:13:24 +02:00
try
{
intpr : : shared_memory_object smo ( intpr : : open_only , " vcmi_memory " , intpr : : read_write ) ;
smo . truncate ( sizeof ( ServerReady ) ) ;
mr = new intpr : : mapped_region ( smo , intpr : : read_write ) ;
sr = reinterpret_cast < ServerReady * > ( mr - > get_address ( ) ) ;
}
catch ( . . . )
{
intpr : : shared_memory_object smo ( intpr : : create_only , " vcmi_memory " , intpr : : read_write ) ;
smo . truncate ( sizeof ( ServerReady ) ) ;
mr = new intpr : : mapped_region ( smo , intpr : : read_write ) ;
sr = new ( mr - > get_address ( ) ) ServerReady ( ) ;
}
2008-09-12 11:51:46 +03:00
}
2014-02-20 21:53:18 +03:00
# endif
2008-09-12 11:51:46 +03:00
2008-07-25 20:28:28 +03:00
boost : : system : : error_code error ;
2016-12-04 11:54:26 +02:00
for ( ; ; )
{
try
{
auto s = new boost : : asio : : ip : : tcp : : socket ( acceptor - > get_io_service ( ) ) ;
boost : : thread acc ( std : : bind ( vaccept , acceptor , s , & error ) ) ;
# ifdef VCMI_ANDROID
{ // in block to clean-up vm helper after use, because we don't need to keep this thread attached to vm
CAndroidVMHelper envHelper ;
envHelper . callStaticVoidMethod ( CAndroidVMHelper : : NATIVE_METHODS_DEFAULT_CLASS , " onServerReady " ) ;
logNetwork - > info ( " Sending server ready message to client " ) ;
}
2017-05-25 19:57:20 +02:00
# else
2017-05-31 06:13:24 +02:00
if ( cmdLineOptions . count ( " use-shm " ) )
{
sr - > setToTrueAndNotify ( port ) ;
delete mr ;
}
2014-02-20 21:53:18 +03:00
# endif
2010-10-24 14:35:14 +03:00
2016-12-04 11:54:26 +02:00
acc . join ( ) ;
if ( error )
{
logNetwork - > warnStream ( ) < < " Got connection but there is an error " < < error ;
return ;
}
logNetwork - > info ( " We've accepted someone... " ) ;
std : : string name = NAME ;
firstConnection = new CConnection ( s , name . append ( " STATE_WAITING " ) ) ;
logNetwork - > info ( " Got connection! " ) ;
while ( ! end2 )
{
ui8 mode ;
* firstConnection > > mode ;
switch ( mode )
{
case 0 :
firstConnection - > close ( ) ;
exit ( 0 ) ;
case 1 :
firstConnection - > close ( ) ;
return ;
case 2 :
newGame ( ) ;
break ;
case 3 :
loadGame ( ) ;
break ;
case 4 :
newPregame ( ) ;
break ;
}
}
2009-01-11 00:08:18 +02:00
break ;
2008-07-09 20:22:28 +03:00
}
2016-12-04 11:54:26 +02:00
catch ( std : : exception & e )
{
vstd : : clear_pointer ( firstConnection ) ;
logNetwork - > info ( " I guess it was just my imagination! " ) ;
}
2008-07-02 11:39:56 +03:00
}
2008-07-25 20:28:28 +03:00
}
2008-07-02 11:39:56 +03:00
2010-10-24 14:35:14 +03:00
void CVCMIServer : : loadGame ( )
2009-01-11 00:08:18 +02:00
{
2010-10-24 14:35:14 +03:00
CConnection & c = * firstConnection ;
2009-01-11 00:08:18 +02:00
std : : string fname ;
CGameHandler gh ;
boost : : system : : error_code error ;
ui8 clients ;
2010-10-24 14:35:14 +03:00
2014-10-19 14:47:09 +03:00
c > > clients > > fname ; //how many clients should be connected
2009-01-11 00:08:18 +02:00
{
2016-10-29 18:52:19 +02:00
CLoadFile lf ( * CResourceHandler : : get ( " local " ) - > getResourceName ( ResourceID ( fname , EResType : : SERVER_SAVEGAME ) ) , MINIMAL_SERIALIZATION_VERSION ) ;
2013-02-19 01:37:22 +03:00
gh . loadCommonState ( lf ) ;
2009-01-11 00:08:18 +02:00
lf > > gh ;
}
2010-10-24 14:35:14 +03:00
c < < ui8 ( 0 ) ;
2009-01-11 00:08:18 +02:00
2016-11-25 16:39:06 +02:00
gh . conns . insert ( firstConnection ) ;
for ( int i = 1 ; i < clients ; i + + )
2009-01-11 00:08:18 +02:00
{
2016-11-25 16:39:06 +02:00
auto s = make_unique < boost : : asio : : ip : : tcp : : socket > ( acceptor - > get_io_service ( ) ) ;
acceptor - > accept ( * s , error ) ;
if ( error ) //retry
2009-01-11 00:08:18 +02:00
{
2016-11-25 16:39:06 +02:00
logNetwork - > warn ( " Cannot establish connection - retrying... " ) ;
i - - ;
continue ;
2015-10-31 17:04:06 +02:00
}
2016-11-25 16:39:06 +02:00
gh . conns . insert ( new CConnection ( s . release ( ) , NAME ) ) ;
2009-01-11 00:08:18 +02:00
}
gh . run ( true ) ;
}
2017-05-25 19:57:20 +02:00
2013-06-21 23:59:32 +03:00
static void handleCommandOptions ( int argc , char * argv [ ] )
{
namespace po = boost : : program_options ;
po : : options_description opts ( " Allowed options " ) ;
opts . add_options ( )
( " help,h " , " display help and exit " )
( " version,v " , " display version information and exit " )
2017-05-31 06:13:24 +02:00
( " run-by-client " , " indicate that server launched by client on same machine " )
( " use-shm " , " enable usage of shared memory " )
2016-12-04 11:54:26 +02:00
( " port " , po : : value < ui16 > ( ) , " port at which server will listen to connections from client " )
2013-06-21 23:59:32 +03:00
( " resultsFile " , po : : value < std : : string > ( ) - > default_value ( " ./results.txt " ) , " file to which the battle result will be appended. Used only in the DUEL mode. " ) ;
if ( argc > 1 )
{
try
{
po : : store ( po : : parse_command_line ( argc , argv , opts ) , cmdLineOptions ) ;
}
2015-10-31 17:04:06 +02:00
catch ( std : : exception & e )
2013-06-21 23:59:32 +03:00
{
std : : cerr < < " Failure during parsing command-line options: \n " < < e . what ( ) < < std : : endl ;
}
}
po : : notify ( cmdLineOptions ) ;
2014-03-20 21:17:40 +03:00
if ( cmdLineOptions . count ( " help " ) )
{
printf ( " %s - A Heroes of Might and Magic 3 clone \n " , GameConstants : : VCMI_VERSION . c_str ( ) ) ;
printf ( " Copyright (C) 2007-2014 VCMI dev team - see AUTHORS file \n " ) ;
printf ( " This is free software; see the source for copying conditions. There is NO \n " ) ;
printf ( " warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. \n " ) ;
printf ( " \n " ) ;
printf ( " Usage: \n " ) ;
std : : cout < < opts ;
exit ( 0 ) ;
}
if ( cmdLineOptions . count ( " version " ) )
{
printf ( " %s \n " , GameConstants : : VCMI_VERSION . c_str ( ) ) ;
std : : cout < < VCMIDirs : : get ( ) . genHelpString ( ) ;
exit ( 0 ) ;
}
2013-06-21 23:59:32 +03:00
}
2014-08-26 13:19:04 +03:00
# if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
2013-12-28 15:47:55 +03:00
void handleLinuxSignal ( int sig )
{
const int STACKTRACE_SIZE = 100 ;
void * buffer [ STACKTRACE_SIZE ] ;
int ptrCount = backtrace ( buffer , STACKTRACE_SIZE ) ;
char * * strings ;
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Error: signal %d : " , sig ) ;
2013-12-28 15:47:55 +03:00
strings = backtrace_symbols ( buffer , ptrCount ) ;
if ( strings = = nullptr )
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " There are no symbols. " ) ;
2013-12-28 15:47:55 +03:00
}
else
{
for ( int i = 0 ; i < ptrCount ; + + i )
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( strings [ i ] ) ;
2013-12-28 15:47:55 +03:00
}
free ( strings ) ;
}
_exit ( EXIT_FAILURE ) ;
}
# endif
2008-09-10 00:10:24 +03:00
int main ( int argc , char * * argv )
2008-07-01 11:01:02 +03:00
{
2013-12-28 15:47:55 +03:00
// Installs a sig sev segmentation violation handler
// to log stacktrace
2014-08-26 13:19:04 +03:00
# if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
2013-12-28 15:47:55 +03:00
signal ( SIGSEGV , handleLinuxSignal ) ;
2017-05-25 19:57:20 +02:00
# endif
2013-12-28 15:47:55 +03:00
2008-09-17 13:18:22 +03:00
console = new CConsoleHandler ;
2014-08-11 00:42:39 +03:00
CBasicLogConfigurator logConfig ( VCMIDirs : : get ( ) . userCachePath ( ) / " VCMI_Server_log.txt " , console ) ;
2013-06-21 23:59:32 +03:00
logConfig . configureDefault ( ) ;
2016-10-27 16:14:20 +02:00
logGlobal - > info ( NAME ) ;
2013-04-14 21:52:05 +03:00
2013-06-21 23:59:32 +03:00
handleCommandOptions ( argc , argv ) ;
2014-09-02 19:42:38 +03:00
preinitDLL ( console ) ;
settings . init ( ) ;
logConfig . configure ( ) ;
2012-12-12 14:13:57 +03:00
loadDLLClasses ( ) ;
2013-06-26 14:18:27 +03:00
srand ( ( ui32 ) time ( nullptr ) ) ;
2008-07-25 20:28:28 +03:00
try
{
2015-12-29 01:14:08 +02:00
boost : : asio : : io_service io_service ;
2008-07-25 20:28:28 +03:00
CVCMIServer server ;
2012-11-16 00:29:22 +03:00
try
2008-09-12 11:51:46 +03:00
{
2017-05-25 19:57:20 +02:00
while ( ! end2 )
2012-11-16 00:29:22 +03:00
{
server . start ( ) ;
}
io_service . run ( ) ;
2008-09-12 11:51:46 +03:00
}
2017-05-25 19:57:20 +02:00
catch ( boost : : system : : system_error & e ) //for boost errors just log, not crash - probably client shut down connection
2012-11-16 00:29:22 +03:00
{
2016-08-30 00:11:54 +02:00
logNetwork - > error ( e . what ( ) ) ;
2012-11-16 00:29:22 +03:00
end2 = true ;
2013-12-28 15:47:55 +03:00
}
2017-05-25 19:57:20 +02:00
catch ( . . . )
2013-12-28 15:47:55 +03:00
{
handleException ( ) ;
}
2012-11-16 00:29:22 +03:00
}
catch ( boost : : system : : system_error & e )
2009-09-20 15:47:40 +03:00
{
2016-08-30 00:11:54 +02:00
logNetwork - > error ( e . what ( ) ) ;
2012-11-16 00:29:22 +03:00
//catch any startup errors (e.g. can't access port) errors
//and return non-zero status so client can detect error
throw ;
}
2017-05-25 19:57:20 +02:00
# ifdef VCMI_ANDROID
CAndroidVMHelper envHelper ;
envHelper . callStaticVoidMethod ( CAndroidVMHelper : : NATIVE_METHODS_DEFAULT_CLASS , " killServer " ) ;
# endif
2016-12-04 11:54:26 +02:00
vstd : : clear_pointer ( VLC ) ;
2013-04-21 19:38:31 +03:00
CResourceHandler : : clear ( ) ;
2016-12-04 11:54:26 +02:00
return 0 ;
2008-07-02 11:39:56 +03:00
}
2017-05-25 19:57:20 +02:00
# ifdef VCMI_ANDROID
void CVCMIServer : : create ( )
{
const char * foo [ 1 ] = { " android-server " } ;
main ( 1 , const_cast < char * * > ( foo ) ) ;
}
# endif