2011-12-14 00:23:17 +03:00
# include "StdInc.h"
2010-12-20 23:22:53 +02:00
# include "CMusicHandler.h"
2013-01-03 15:19:20 +03:00
# include "../lib/Mapping/CCampaignHandler.h"
2008-07-25 20:28:28 +03:00
# include "../CCallback.h"
2011-12-14 00:23:17 +03:00
# include "../lib/CConsoleHandler.h"
2009-05-20 13:08:56 +03:00
# include "CGameInfo.h"
# include "../lib/CGameState.h"
# include "CPlayerInterface.h"
2011-12-14 00:23:17 +03:00
# include "../lib/StartInfo.h"
2010-12-25 21:23:30 +02:00
# include "../lib/BattleState.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/CDefObjInfoHandler.h"
# include "../lib/CGeneralTextHandler.h"
# include "../lib/CHeroHandler.h"
# include "../lib/CTownHandler.h"
# include "../lib/CObjectHandler.h"
# include "../lib/CBuildingHandler.h"
# include "../lib/CSpellHandler.h"
2008-11-09 00:29:19 +02:00
# include "../lib/Connection.h"
2009-01-11 00:08:18 +02:00
# include "../lib/Interprocess.h"
2008-07-26 16:57:32 +03:00
# include "../lib/NetPacks.h"
2008-11-09 00:29:19 +02:00
# include "../lib/VCMI_Lib.h"
2011-11-01 15:58:01 +03:00
# include "../lib/VCMIDirs.h"
2013-01-03 15:19:20 +03:00
# include "../lib/Mapping/CMap.h"
2011-09-01 04:40:46 +03:00
# include "../lib/JsonNode.h"
2010-04-06 16:19:54 +03:00
# include "mapHandler.h"
2012-09-29 13:59:43 +03:00
# include "../lib/CConfigHandler.h"
2008-11-09 00:29:19 +02:00
# include "Client.h"
2010-08-20 16:34:39 +03:00
# include "CPreGame.h"
2011-12-14 00:23:17 +03:00
# include "BattleInterface/CBattleInterface.h"
# include "../lib/CThreadHelper.h"
2011-06-20 14:41:04 +03:00
# include "../lib/CScriptingModule.h"
2011-12-14 00:23:17 +03:00
# include "../lib/RegisterTypes.h"
2011-12-17 21:59:59 +03:00
# include "UIFramework/CGuiHandler.h"
2011-05-10 01:20:47 +03:00
2009-01-11 00:08:18 +02:00
extern std : : string NAME ;
namespace intpr = boost : : interprocess ;
2008-07-28 15:44:08 +03:00
2009-04-15 17:03:31 +03:00
/*
* Client . 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
*
*/
2010-09-03 21:42:54 +03:00
template < typename T > class CApplyOnCL ;
2009-03-07 00:25:19 +02:00
class CBaseForCLApply
{
public :
virtual void applyOnClAfter ( CClient * cl , void * pack ) const = 0 ;
virtual void applyOnClBefore ( CClient * cl , void * pack ) const = 0 ;
2010-01-02 03:48:44 +02:00
virtual ~ CBaseForCLApply ( ) { }
2010-09-03 21:42:54 +03:00
template < typename U > static CBaseForCLApply * getApplier ( const U * t = NULL )
{
return new CApplyOnCL < U > ;
}
2009-03-07 00:25:19 +02:00
} ;
2010-09-03 21:42:54 +03:00
2009-03-07 00:25:19 +02:00
template < typename T > class CApplyOnCL : public CBaseForCLApply
{
public :
void applyOnClAfter ( CClient * cl , void * pack ) const
{
T * ptr = static_cast < T * > ( pack ) ;
ptr - > applyCl ( cl ) ;
}
void applyOnClBefore ( CClient * cl , void * pack ) const
{
T * ptr = static_cast < T * > ( pack ) ;
ptr - > applyFirstCl ( cl ) ;
}
} ;
2010-09-04 17:47:39 +03:00
static CApplier < CBaseForCLApply > * applier = NULL ;
2009-03-07 00:25:19 +02:00
2009-01-30 23:28:02 +02:00
void CClient : : init ( )
2008-07-25 20:28:28 +03:00
{
2010-02-20 15:24:38 +02:00
hotSeat = false ;
2009-12-28 06:08:24 +02:00
connectionHandler = NULL ;
2009-08-30 15:47:40 +03:00
pathInfo = NULL ;
2010-09-03 21:42:54 +03:00
applier = new CApplier < CBaseForCLApply > ;
registerTypes2 ( * applier ) ;
2009-02-01 16:11:41 +02:00
IObjectInterface : : cb = this ;
2009-01-11 00:08:18 +02:00
serv = NULL ;
gs = NULL ;
cb = NULL ;
2011-06-11 02:50:32 +03:00
erm = NULL ;
2009-11-01 03:15:16 +02:00
terminate = false ;
2009-01-30 23:28:02 +02:00
}
CClient : : CClient ( void )
{
init ( ) ;
2008-07-25 20:28:28 +03:00
}
2010-09-03 21:42:54 +03:00
2008-07-25 20:28:28 +03:00
CClient : : CClient ( CConnection * con , StartInfo * si )
2009-01-11 00:08:18 +02:00
{
2009-01-30 23:28:02 +02:00
init ( ) ;
2009-01-11 00:08:18 +02:00
newGame ( con , si ) ;
2008-07-25 20:28:28 +03:00
}
2010-09-03 21:42:54 +03:00
2008-07-25 20:28:28 +03:00
CClient : : ~ CClient ( void )
{
2009-08-30 15:47:40 +03:00
delete pathInfo ;
2009-03-07 17:54:12 +02:00
delete applier ;
2008-07-25 20:28:28 +03:00
}
2010-09-03 21:42:54 +03:00
2013-03-03 20:06:03 +03:00
void CClient : : waitForMoveAndSend ( PlayerColor color )
2008-08-04 18:56:36 +03:00
{
2008-08-30 00:41:32 +03:00
try
{
2012-09-24 02:10:56 +03:00
setThreadName ( " CClient::waitForMoveAndSend " ) ;
2010-12-25 03:43:40 +02:00
assert ( vstd : : contains ( battleints , color ) ) ;
2012-08-26 12:07:48 +03:00
BattleAction ba = battleints [ color ] - > activeStack ( gs - > curB - > battleGetStackByID ( gs - > curB - > activeStack , false ) ) ;
2011-06-11 07:54:41 +03:00
MakeAction temp_action ( ba ) ;
2012-03-26 01:46:14 +03:00
sendRequest ( & temp_action , color ) ;
2008-08-30 00:41:32 +03:00
return ;
2012-02-20 00:03:43 +03:00
}
catch ( boost : : thread_interrupted & )
{
tlog5 < < " Wait for move thread was interrupted and no action will be send. Was a battle ended by spell? \n " ;
return ;
}
HANDLE_EXCEPTION
2008-09-19 11:16:19 +03:00
tlog1 < < " We should not be here! " < < std : : endl ;
2008-08-04 18:56:36 +03:00
}
2009-12-28 06:08:24 +02:00
2008-07-25 20:28:28 +03:00
void CClient : : run ( )
{
2012-06-27 23:44:01 +03:00
setThreadName ( " CClient::run " ) ;
2009-04-11 04:32:50 +03:00
try
2008-07-25 20:28:28 +03:00
{
2010-01-02 03:48:44 +02:00
CPack * pack = NULL ;
2009-12-28 06:08:24 +02:00
while ( ! terminate )
2009-03-07 17:54:12 +02:00
{
2010-09-03 21:42:54 +03:00
pack = serv - > retreivePack ( ) ; //get the package from the server
2010-01-02 03:48:44 +02:00
2009-12-28 06:08:24 +02:00
if ( terminate )
{
2009-11-01 03:15:16 +02:00
delete pack ;
2010-01-02 03:48:44 +02:00
pack = NULL ;
2009-11-01 03:15:16 +02:00
break ;
}
2010-01-02 03:48:44 +02:00
handlePack ( pack ) ;
2009-04-11 04:32:50 +03:00
pack = NULL ;
2009-03-07 17:54:12 +02:00
}
2010-01-29 22:52:45 +02:00
}
2012-06-16 20:12:58 +03:00
//catch only asio exceptions
catch ( const boost : : system : : system_error & e )
2010-01-29 22:52:45 +02:00
{
2010-05-02 21:20:26 +03:00
tlog3 < < " Lost connection to server, ending listening thread! \n " ;
2010-01-29 22:52:45 +02:00
tlog1 < < e . what ( ) < < std : : endl ;
if ( ! terminate ) //rethrow (-> boom!) only if closing connection was unexpected
{
tlog1 < < " Something wrong, lost connection while game is still ongoing... \n " ;
throw ;
}
}
2008-08-04 12:05:52 +03:00
}
2008-11-16 03:06:15 +02:00
void CClient : : save ( const std : : string & fname )
{
2009-02-11 19:03:30 +02:00
if ( gs - > curB )
{
tlog1 < < " Game cannot be saved during battle! \n " ;
return ;
}
2011-06-11 07:54:41 +03:00
SaveGame save_game ( fname ) ;
2013-03-03 20:06:03 +03:00
sendRequest ( ( CPackForClient * ) & save_game , PlayerColor : : NEUTRAL ) ;
2008-11-16 03:06:15 +02:00
}
2008-12-27 03:01:59 +02:00
2010-08-20 16:34:39 +03:00
void CClient : : endGame ( bool closeConnection /*= true*/ )
2009-01-11 00:08:18 +02:00
{
2012-02-22 16:41:27 +03:00
//suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
BOOST_FOREACH ( auto i , playerint )
i . second - > finish ( ) ;
2010-08-20 16:34:39 +03:00
// Game is ending
// Tell the network thread to reach a stable state
2011-05-30 02:49:25 +03:00
if ( closeConnection )
stopConnection ( ) ;
tlog0 < < " Closed connection. " < < std : : endl ;
2010-08-20 16:34:39 +03:00
GH . curInt = NULL ;
LOCPLINT - > terminate_cond . setn ( true ) ;
2012-02-20 00:03:43 +03:00
{
boost : : unique_lock < boost : : recursive_mutex > un ( * LOCPLINT - > pim ) ;
tlog0 < < " \n \n Ending current game! " < < std : : endl ;
if ( GH . topInt ( ) )
GH . topInt ( ) - > deactivate ( ) ;
GH . listInt . clear ( ) ;
GH . objsToBlit . clear ( ) ;
GH . statusbar = NULL ;
tlog0 < < " Removed GUI. " < < std : : endl ;
delete CGI - > mh ;
const_cast < CGameInfo * > ( CGI ) - > mh = NULL ;
const_cast < CGameInfo * > ( CGI ) - > state . dellNull ( ) ;
tlog0 < < " Deleted mapHandler and gameState. " < < std : : endl ;
LOCPLINT = NULL ;
}
2009-11-01 03:15:16 +02:00
while ( ! playerint . empty ( ) )
2009-01-11 00:08:18 +02:00
{
2010-09-03 21:42:54 +03:00
CGameInterface * pint = playerint . begin ( ) - > second ;
2009-11-01 03:15:16 +02:00
playerint . erase ( playerint . begin ( ) ) ;
2010-09-03 21:42:54 +03:00
delete pint ;
2009-03-19 16:17:19 +02:00
}
2009-06-23 11:14:49 +03:00
2011-09-06 09:00:32 +03:00
callbacks . clear ( ) ;
2010-02-13 23:45:46 +02:00
tlog0 < < " Deleted playerInts. " < < std : : endl ;
2009-11-01 03:15:16 +02:00
2010-08-20 16:34:39 +03:00
tlog0 < < " Client stopped. " < < std : : endl ;
2009-11-01 03:15:16 +02:00
}
void CClient : : loadGame ( const std : : string & fname )
{
tlog0 < < " \n \n Loading procedure started! \n \n " ;
2010-09-03 21:42:54 +03:00
CServerHandler sh ;
sh . startServer ( ) ;
2009-01-11 00:08:18 +02:00
2011-12-17 21:59:59 +03:00
CStopWatch tmh ;
2009-01-11 00:08:18 +02:00
{
2013-02-19 01:37:22 +03:00
auto clientSaveName = CResourceHandler : : get ( ) - > getResourceName ( ResourceID ( fname , EResType : : CLIENT_SAVEGAME ) ) ;
auto controlServerSaveName = CResourceHandler : : get ( ) - > getResourceName ( ResourceID ( fname , EResType : : SERVER_SAVEGAME ) ) ;
2009-01-11 00:08:18 +02:00
2013-02-19 01:37:22 +03:00
unique_ptr < CLoadFile > loader ;
{
CLoadIntegrityValidator checkingLoader ( clientSaveName , controlServerSaveName ) ;
loadCommonState ( checkingLoader ) ;
loader = checkingLoader . decay ( ) ;
}
tlog0 < < " Loaded common part of save " < < tmh . getDiff ( ) < < std : : endl ;
2010-12-19 00:11:28 +02:00
const_cast < CGameInfo * > ( CGI ) - > state = gs ;
2013-02-19 01:37:22 +03:00
const_cast < CGameInfo * > ( CGI ) - > mh = new CMapHandler ( ) ;
2010-12-19 00:11:28 +02:00
const_cast < CGameInfo * > ( CGI ) - > mh - > map = gs - > map ;
2012-11-20 20:53:45 +03:00
pathInfo = new CPathsInfo ( int3 ( gs - > map - > width , gs - > map - > height , gs - > map - > twoLevel ? 2 : 1 ) ) ;
2009-01-11 00:08:18 +02:00
CGI - > mh - > init ( ) ;
2011-12-14 00:23:17 +03:00
tlog0 < < " Initing maphandler: " < < tmh . getDiff ( ) < < std : : endl ;
2013-02-19 01:37:22 +03:00
* loader > > * this ;
tlog0 < < " Loaded client part of save " < < tmh . getDiff ( ) < < std : : endl ;
2009-01-11 00:08:18 +02:00
}
2013-02-19 01:37:22 +03:00
2010-09-03 21:42:54 +03:00
serv = sh . connectToServer ( ) ;
2010-06-26 19:02:10 +03:00
serv - > addStdVecItems ( gs ) ;
2009-01-11 00:08:18 +02:00
2010-09-03 21:42:54 +03:00
tmh . update ( ) ;
2009-01-11 00:08:18 +02:00
ui8 pom8 ;
* serv < < ui8 ( 3 ) < < ui8 ( 1 ) ; //load game; one client
* serv < < fname ;
* serv > > pom8 ;
if ( pom8 )
2012-04-22 10:32:45 +03:00
throw std : : runtime_error ( " Server cannot open the savegame! " ) ;
2009-01-11 00:08:18 +02:00
else
tlog0 < < " Server opened savegame properly. \n " ;
2010-11-03 13:34:25 +02:00
* serv < < ui32 ( gs - > scenarioOps - > playerInfos . size ( ) + 1 ) ; //number of players + neutral
2012-09-24 19:14:53 +03:00
for ( auto it = gs - > scenarioOps - > playerInfos . begin ( ) ;
2010-08-03 14:36:52 +03:00
it ! = gs - > scenarioOps - > playerInfos . end ( ) ; + + it )
2009-01-11 00:08:18 +02:00
{
2013-03-03 20:06:03 +03:00
* serv < < ui8 ( it - > first . getNum ( ) ) ; //players
2009-01-11 00:08:18 +02:00
}
2013-03-03 20:06:03 +03:00
* serv < < ui8 ( PlayerColor : : NEUTRAL . getNum ( ) ) ;
2011-12-14 00:23:17 +03:00
tlog0 < < " Sent info to server: " < < tmh . getDiff ( ) < < std : : endl ;
2012-09-28 12:11:18 +03:00
serv - > enableStackSendingByID ( ) ;
serv - > disableSmartPointerSerialization ( ) ;
2009-01-11 00:08:18 +02:00
}
void CClient : : newGame ( CConnection * con , StartInfo * si )
{
2010-10-24 14:35:14 +03:00
enum { SINGLE , HOST , GUEST } networkMode = SINGLE ;
2009-12-20 19:14:14 +02:00
if ( con = = NULL )
{
2010-09-03 21:42:54 +03:00
CServerHandler sh ;
2010-10-24 14:35:14 +03:00
serv = sh . connectToServer ( ) ;
}
else
{
serv = con ;
networkMode = ( con - > connectionID = = 1 ) ? HOST : GUEST ;
2009-11-01 03:15:16 +02:00
}
2011-12-17 21:59:59 +03:00
CStopWatch tmh ;
2010-12-19 00:11:28 +02:00
const_cast < CGameInfo * > ( CGI ) - > state = new CGameState ( ) ;
2011-12-14 00:23:17 +03:00
tlog0 < < " \t Gamestate: " < < tmh . getDiff ( ) < < std : : endl ;
2010-10-24 14:35:14 +03:00
CConnection & c ( * serv ) ;
2009-01-11 00:08:18 +02:00
////////////////////////////////////////////////////
2010-10-24 14:35:14 +03:00
if ( networkMode = = SINGLE )
2009-01-11 00:08:18 +02:00
{
2010-10-24 14:35:14 +03:00
ui8 pom8 ;
c < < ui8 ( 2 ) < < ui8 ( 1 ) ; //new game; one client
c < < * si ;
c > > pom8 ;
if ( pom8 )
2012-04-22 10:32:45 +03:00
throw std : : runtime_error ( " Server cannot open the map! " ) ;
2010-10-24 14:35:14 +03:00
else
tlog0 < < " Server opened map properly. \n " ;
2009-01-11 00:08:18 +02:00
}
2010-10-24 14:35:14 +03:00
2012-04-14 05:20:22 +03:00
c > > si ;
2011-12-14 00:23:17 +03:00
tlog0 < < " \t Sending/Getting info to/from the server: " < < tmh . getDiff ( ) < < std : : endl ;
2012-09-25 21:00:55 +03:00
c . enableStackSendingByID ( ) ;
c . disableSmartPointerSerialization ( ) ;
2009-01-11 00:08:18 +02:00
2013-01-21 23:49:19 +03:00
// Initialize game state
2010-12-20 15:04:24 +02:00
gs = const_cast < CGameInfo * > ( CGI ) - > state ;
2009-01-11 00:08:18 +02:00
gs - > scenarioOps = si ;
2012-04-14 05:20:22 +03:00
gs - > init ( si ) ;
2011-12-14 00:23:17 +03:00
tlog0 < < " Initializing GameState (together): " < < tmh . getDiff ( ) < < std : : endl ;
2010-12-22 22:14:40 +02:00
2013-01-21 23:49:19 +03:00
// Now after possible random map gen, we know exact player count.
// Inform server about how many players client handles
2013-03-03 20:06:03 +03:00
std : : set < PlayerColor > myPlayers ;
2013-01-21 23:49:19 +03:00
for ( auto it = gs - > scenarioOps - > playerInfos . begin ( ) ; it ! = gs - > scenarioOps - > playerInfos . end ( ) ; + + it )
{
if ( ( networkMode = = SINGLE ) //single - one client has all player
| | ( networkMode ! = SINGLE & & serv - > connectionID = = it - > second . playerID ) //multi - client has only "its players"
| | ( networkMode = = HOST & & it - > second . playerID = = PlayerSettings : : PLAYER_AI ) ) //multi - host has all AI players
{
myPlayers . insert ( it - > first ) ; //add player
}
}
if ( networkMode ! = GUEST )
2013-03-03 20:06:03 +03:00
myPlayers . insert ( PlayerColor : : NEUTRAL ) ;
2013-01-21 23:49:19 +03:00
c < < myPlayers ;
// Init map handler
2010-12-23 02:33:48 +02:00
if ( gs - > map )
2010-12-22 22:14:40 +02:00
{
const_cast < CGameInfo * > ( CGI ) - > mh = new CMapHandler ( ) ;
CGI - > mh - > map = gs - > map ;
2011-12-14 00:23:17 +03:00
tlog0 < < " Creating mapHandler: " < < tmh . getDiff ( ) < < std : : endl ;
2011-09-03 06:04:06 +03:00
CGI - > mh - > init ( ) ;
2012-11-20 20:53:45 +03:00
pathInfo = new CPathsInfo ( int3 ( gs - > map - > width , gs - > map - > height , gs - > map - > twoLevel ? 2 : 1 ) ) ;
2011-12-14 00:23:17 +03:00
tlog0 < < " Initializing mapHandler (together): " < < tmh . getDiff ( ) < < std : : endl ;
2010-12-22 22:14:40 +02:00
}
2009-01-11 00:08:18 +02:00
2010-02-20 15:24:38 +02:00
int humanPlayers = 0 ;
2013-03-03 20:06:03 +03:00
int sensibleAILimit = settings [ " session " ] [ " oneGoodAI " ] . Bool ( ) ? 1 : PlayerColor : : PLAYER_LIMIT_I ;
2012-09-24 19:14:53 +03:00
for ( auto it = gs - > scenarioOps - > playerInfos . begin ( ) ;
2010-08-03 14:36:52 +03:00
it ! = gs - > scenarioOps - > playerInfos . end ( ) ; + + it ) //initializing interfaces for players
2013-02-19 02:10:46 +03:00
{
2013-03-03 20:06:03 +03:00
PlayerColor color = it - > first ;
2010-12-22 22:14:40 +02:00
gs - > currentPlayer = color ;
2010-10-24 14:35:14 +03:00
if ( ! vstd : : contains ( myPlayers , color ) )
continue ;
2013-03-03 20:06:03 +03:00
tlog5 < < " Preparing interface for player " < < color < < std : : endl ;
2010-12-22 22:14:40 +02:00
if ( si - > mode ! = StartInfo : : DUEL )
2010-02-20 15:24:38 +02:00
{
2012-03-06 19:59:55 +03:00
auto cb = make_shared < CCallback > ( gs , color , this ) ;
2012-12-02 15:21:44 +03:00
if ( it - > second . playerID = = PlayerSettings : : PLAYER_AI )
2010-12-22 22:14:40 +02:00
{
2012-01-12 18:23:00 +03:00
std : : string AItoGive = settings [ " server " ] [ " playerAI " ] . String ( ) ;
2012-01-03 04:55:26 +03:00
if ( ! sensibleAILimit )
2012-02-29 04:31:48 +03:00
AItoGive = " EmptyAI " ;
2012-01-03 04:55:26 +03:00
else
sensibleAILimit - - ;
playerint [ color ] = static_cast < CGameInterface * > ( CDynLibHandler : : getNewAI ( AItoGive ) ) ;
2013-03-14 23:44:00 +03:00
tlog1 < < " Player " < < static_cast < int > ( color . getNum ( ) ) < < " will be lead by " < < AItoGive < < std : : endl ;
2010-12-22 22:14:40 +02:00
}
else
{
playerint [ color ] = new CPlayerInterface ( color ) ;
humanPlayers + + ;
}
2010-12-25 03:43:40 +02:00
battleints [ color ] = playerint [ color ] ;
2010-12-22 22:14:40 +02:00
2013-02-19 02:10:46 +03:00
tlog5 < < " \t Initializing the interface \n " ;
2012-03-06 19:59:55 +03:00
playerint [ color ] - > init ( cb . get ( ) ) ;
2012-08-26 12:07:48 +03:00
battleCallbacks [ color ] = callbacks [ color ] = cb ;
2009-01-11 00:08:18 +02:00
}
2010-12-22 22:14:40 +02:00
else
2010-02-20 15:24:38 +02:00
{
2012-08-26 12:07:48 +03:00
auto cbc = make_shared < CBattleCallback > ( gs , color , this ) ;
battleCallbacks [ color ] = cbc ;
2013-03-03 20:06:03 +03:00
if ( color = = PlayerColor ( 0 ) )
2012-09-20 19:55:21 +03:00
battleints [ color ] = CDynLibHandler : : getNewBattleAI ( settings [ " server " ] [ " neutralAI " ] . String ( ) ) ;
else
battleints [ color ] = CDynLibHandler : : getNewBattleAI ( " StupidAI " ) ;
2012-08-26 12:07:48 +03:00
battleints [ color ] - > init ( cbc . get ( ) ) ;
2009-01-11 00:08:18 +02:00
}
}
2010-02-20 15:24:38 +02:00
2010-12-23 02:33:48 +02:00
if ( si - > mode = = StartInfo : : DUEL )
{
2012-07-26 03:48:44 +03:00
boost : : unique_lock < boost : : recursive_mutex > un ( * LOCPLINT - > pim ) ;
2013-03-03 20:06:03 +03:00
CPlayerInterface * p = new CPlayerInterface ( PlayerColor : : NEUTRAL ) ; //TODO: check if neutral really works -- was -1, but CPlayerInterface seems to cooperate with this value not too well
2010-12-23 02:33:48 +02:00
p - > observerInDuelMode = true ;
2013-03-03 20:06:03 +03:00
battleints [ PlayerColor : : UNFLAGGABLE ] = playerint [ PlayerColor : : UNFLAGGABLE ] = p ;
2012-07-26 03:48:44 +03:00
privilagedBattleEventReceivers . push_back ( p ) ;
2010-12-25 03:43:40 +02:00
GH . curInt = p ;
2013-03-03 20:06:03 +03:00
auto cb = make_shared < CCallback > ( gs , boost : : optional < PlayerColor > ( ) , this ) ;
battleCallbacks [ PlayerColor : : NEUTRAL ] = callbacks [ PlayerColor : : NEUTRAL ] = cb ;
2012-08-26 12:07:48 +03:00
p - > init ( cb . get ( ) ) ;
2010-12-23 02:33:48 +02:00
battleStarted ( gs - > curB ) ;
}
2010-12-25 03:43:40 +02:00
else
{
2011-02-27 21:58:14 +02:00
loadNeutralBattleAI ( ) ;
2010-12-25 03:43:40 +02:00
}
2010-12-22 22:14:40 +02:00
2010-12-20 15:04:24 +02:00
serv - > addStdVecItems ( const_cast < CGameInfo * > ( CGI ) - > state ) ;
2010-02-20 15:24:38 +02:00
hotSeat = ( humanPlayers > 1 ) ;
2011-07-17 21:49:05 +03:00
// std::vector<FileInfo> scriptModules;
// CFileUtility::getFilesWithExt(scriptModules, LIB_DIR "/Scripting", "." LIB_EXT);
// BOOST_FOREACH(FileInfo &m, scriptModules)
// {
// CScriptingModule * nm = CDynLibHandler::getNewScriptingModule(m.name);
// privilagedGameEventReceivers.push_back(nm);
// privilagedBattleEventReceivers.push_back(nm);
// nm->giveActionCB(this);
// nm->giveInfoCB(this);
// nm->init();
//
// erm = nm; //something tells me that there'll at most one module and it'll be ERM
// }
2009-01-11 00:08:18 +02:00
}
2009-03-28 20:46:20 +02:00
template < typename Handler >
void CClient : : serialize ( Handler & h , const int version )
{
2010-02-20 15:24:38 +02:00
h & hotSeat ;
2009-03-28 20:46:20 +02:00
if ( h . saving )
{
ui8 players = playerint . size ( ) ;
h & players ;
2013-03-03 20:06:03 +03:00
for ( auto i = playerint . begin ( ) ; i ! = playerint . end ( ) ; i + + )
2009-03-28 20:46:20 +02:00
{
h & i - > first & i - > second - > dllName ;
i - > second - > serialize ( h , version ) ;
}
}
else
{
2013-01-21 01:49:34 +03:00
ui8 players = 0 ; //fix for uninitialized warning
2009-03-28 20:46:20 +02:00
h & players ;
for ( int i = 0 ; i < players ; i + + )
{
std : : string dllname ;
2013-03-03 20:06:03 +03:00
PlayerColor pid = PlayerColor ( 0 ) ; //fix for uninitialized warning
2009-03-28 20:46:20 +02:00
h & pid & dllname ;
2009-07-18 06:13:13 +03:00
2011-02-23 05:57:45 +02:00
CGameInterface * nInt = NULL ;
2009-03-28 20:46:20 +02:00
if ( dllname . length ( ) )
2011-02-23 05:57:45 +02:00
{
2013-03-03 20:06:03 +03:00
if ( pid = = PlayerColor : : NEUTRAL )
2011-02-23 05:57:45 +02:00
{
2011-08-19 22:50:24 +03:00
//CBattleCallback * cbc = new CBattleCallback(gs, pid, this);//FIXME: unused?
2011-06-20 14:41:04 +03:00
CBattleGameInterface * cbgi = CDynLibHandler : : getNewBattleAI ( dllname ) ;
2011-02-23 05:57:45 +02:00
battleints [ pid ] = cbgi ;
cbgi - > init ( cb ) ;
//TODO? consider serialization
continue ;
}
else
2011-06-20 14:41:04 +03:00
nInt = CDynLibHandler : : getNewAI ( dllname ) ;
2011-02-23 05:57:45 +02:00
}
2009-07-18 06:13:13 +03:00
else
2010-08-03 14:36:52 +03:00
nInt = new CPlayerInterface ( pid ) ;
2009-07-18 06:13:13 +03:00
2012-08-26 22:13:57 +03:00
battleCallbacks [ pid ] = callbacks [ pid ] = make_shared < CCallback > ( gs , pid , this ) ;
2011-02-22 11:47:25 +02:00
battleints [ pid ] = playerint [ pid ] = nInt ;
2011-09-06 09:00:32 +03:00
nInt - > init ( callbacks [ pid ] . get ( ) ) ;
2009-07-18 06:13:13 +03:00
nInt - > serialize ( h , version ) ;
2009-03-28 20:46:20 +02:00
}
2011-02-27 21:58:14 +02:00
2013-03-03 20:06:03 +03:00
if ( ! vstd : : contains ( battleints , PlayerColor : : NEUTRAL ) )
2011-02-27 21:58:14 +02:00
loadNeutralBattleAI ( ) ;
2009-03-28 20:46:20 +02:00
}
}
2010-01-02 03:48:44 +02:00
void CClient : : handlePack ( CPack * pack )
{
CBaseForCLApply * apply = applier - > apps [ typeList . getTypeID ( pack ) ] ; //find the applier
if ( apply )
{
2012-04-06 18:02:15 +03:00
boost : : unique_lock < boost : : recursive_mutex > guiLock ( * LOCPLINT - > pim ) ;
2010-01-02 03:48:44 +02:00
apply - > applyOnClBefore ( this , pack ) ;
tlog5 < < " \t Made first apply on cl \n " ;
gs - > apply ( pack ) ;
tlog5 < < " \t Applied on gs \n " ;
apply - > applyOnClAfter ( this , pack ) ;
tlog5 < < " \t Made second apply on cl \n " ;
}
else
{
2011-06-18 07:56:32 +03:00
tlog1 < < " Message cannot be applied, cannot find applier! TypeID " < < typeList . getTypeID ( pack ) < < std : : endl ;
2010-01-02 03:48:44 +02:00
}
delete pack ;
}
2009-08-04 02:53:18 +03:00
2010-03-11 01:16:30 +02:00
void CClient : : updatePaths ( )
2011-08-25 18:24:37 +03:00
{
//TODO? lazy evaluation? paths now can get recalculated multiple times upon various game events
2011-05-22 21:46:52 +03:00
const CGHeroInstance * h = getSelectedHero ( ) ;
2010-03-11 01:16:30 +02:00
if ( h ) //if we have selected hero...
2011-06-25 17:22:19 +03:00
calculatePaths ( h ) ;
2010-03-11 01:16:30 +02:00
}
2010-08-20 16:34:39 +03:00
2012-09-21 20:59:54 +03:00
void CClient : : finishCampaign ( shared_ptr < CCampaignState > camp )
2010-08-20 16:34:39 +03:00
{
}
2012-09-21 20:59:54 +03:00
void CClient : : proposeNextMission ( shared_ptr < CCampaignState > camp )
2010-08-20 16:34:39 +03:00
{
GH . pushInt ( new CBonusSelection ( camp ) ) ;
}
void CClient : : stopConnection ( )
{
terminate = true ;
2011-01-17 18:07:08 +02:00
if ( serv ) //request closing connection
2010-08-20 16:34:39 +03:00
{
tlog0 < < " Connection has been requested to be closed. \n " ;
boost : : unique_lock < boost : : mutex > ( * serv - > wmx ) ;
2011-06-11 07:54:41 +03:00
CloseServer close_server ;
2013-03-03 20:06:03 +03:00
sendRequest ( & close_server , PlayerColor : : NEUTRAL ) ;
2010-08-20 16:34:39 +03:00
tlog0 < < " Sent closing signal to the server \n " ;
}
2011-01-17 18:07:08 +02:00
if ( connectionHandler ) //end connection handler
2010-08-20 16:34:39 +03:00
{
if ( connectionHandler - > get_id ( ) ! = boost : : this_thread : : get_id ( ) )
connectionHandler - > join ( ) ;
tlog0 < < " Connection handler thread joined " < < std : : endl ;
delete connectionHandler ;
connectionHandler = NULL ;
}
2011-01-17 18:07:08 +02:00
if ( serv ) //and delete connection
{
serv - > close ( ) ;
delete serv ;
serv = NULL ;
tlog3 < < " Our socket has been closed. " < < std : : endl ;
}
2010-08-20 16:34:39 +03:00
}
2010-12-23 02:33:48 +02:00
void CClient : : battleStarted ( const BattleInfo * info )
{
2012-08-26 12:07:48 +03:00
BOOST_FOREACH ( auto & battleCb , battleCallbacks )
{
2013-03-03 20:06:03 +03:00
if ( vstd : : contains ( info - > sides , battleCb . first ) | | battleCb . first > = PlayerColor : : PLAYER_LIMIT )
2012-08-26 12:07:48 +03:00
battleCb . second - > setBattle ( info ) ;
}
// BOOST_FOREACH(ui8 side, info->sides)
// if(battleCallbacks.count(side))
// battleCallbacks[side]->setBattle(info);
2010-12-23 02:33:48 +02:00
CPlayerInterface * att , * def ;
2011-01-09 19:41:46 +02:00
if ( vstd : : contains ( playerint , info - > sides [ 0 ] ) & & playerint [ info - > sides [ 0 ] ] - > human )
att = static_cast < CPlayerInterface * > ( playerint [ info - > sides [ 0 ] ] ) ;
2010-12-23 02:33:48 +02:00
else
att = NULL ;
2011-01-09 19:41:46 +02:00
if ( vstd : : contains ( playerint , info - > sides [ 1 ] ) & & playerint [ info - > sides [ 1 ] ] - > human )
def = static_cast < CPlayerInterface * > ( playerint [ info - > sides [ 1 ] ] ) ;
2010-12-23 02:33:48 +02:00
else
def = NULL ;
2011-07-17 21:49:05 +03:00
if ( att | | def | | gs - > scenarioOps - > mode = = StartInfo : : DUEL )
2012-02-20 00:03:43 +03:00
{
boost : : unique_lock < boost : : recursive_mutex > un ( * LOCPLINT - > pim ) ;
2012-01-12 18:23:00 +03:00
new CBattleInterface ( info - > belligerents [ 0 ] , info - > belligerents [ 1 ] , info - > heroes [ 0 ] , info - > heroes [ 1 ] ,
2012-04-20 00:21:11 +03:00
Rect ( ( screen - > w - 800 ) / 2 ,
( screen - > h - 600 ) / 2 , 800 , 600 ) , att , def ) ;
2012-02-20 00:03:43 +03:00
}
2010-12-23 02:33:48 +02:00
2011-01-09 19:41:46 +02:00
if ( vstd : : contains ( battleints , info - > sides [ 0 ] ) )
battleints [ info - > sides [ 0 ] ] - > battleStart ( info - > belligerents [ 0 ] , info - > belligerents [ 1 ] , info - > tile , info - > heroes [ 0 ] , info - > heroes [ 1 ] , 0 ) ;
if ( vstd : : contains ( battleints , info - > sides [ 1 ] ) )
battleints [ info - > sides [ 1 ] ] - > battleStart ( info - > belligerents [ 0 ] , info - > belligerents [ 1 ] , info - > tile , info - > heroes [ 0 ] , info - > heroes [ 1 ] , 1 ) ;
2013-03-03 20:06:03 +03:00
if ( vstd : : contains ( battleints , PlayerColor : : UNFLAGGABLE ) )
battleints [ PlayerColor : : UNFLAGGABLE ] - > battleStart ( info - > belligerents [ 0 ] , info - > belligerents [ 1 ] , info - > tile , info - > heroes [ 0 ] , info - > heroes [ 1 ] , 1 ) ;
2011-08-25 18:24:37 +03:00
if ( info - > tacticDistance & & vstd : : contains ( battleints , info - > sides [ info - > tacticsSide ] ) )
{
2011-08-26 00:08:53 +03:00
boost : : thread ( & CClient : : commenceTacticPhaseForInt , this , battleints [ info - > sides [ info - > tacticsSide ] ] ) ;
2011-08-25 18:24:37 +03:00
}
2010-12-23 02:33:48 +02:00
}
2012-08-26 12:07:48 +03:00
void CClient : : battleFinished ( )
{
2013-03-03 20:06:03 +03:00
BOOST_FOREACH ( PlayerColor side , gs - > curB - > sides )
2012-08-26 12:07:48 +03:00
if ( battleCallbacks . count ( side ) )
battleCallbacks [ side ] - > setBattle ( nullptr ) ;
}
2011-02-27 21:58:14 +02:00
void CClient : : loadNeutralBattleAI ( )
{
2013-03-03 20:06:03 +03:00
battleints [ PlayerColor : : NEUTRAL ] = CDynLibHandler : : getNewBattleAI ( settings [ " server " ] [ " neutralAI " ] . String ( ) ) ;
auto cbc = make_shared < CBattleCallback > ( gs , PlayerColor : : NEUTRAL , this ) ;
battleCallbacks [ PlayerColor : : NEUTRAL ] = cbc ;
battleints [ PlayerColor : : NEUTRAL ] - > init ( cbc . get ( ) ) ;
2011-02-27 21:58:14 +02:00
}
2011-05-22 21:46:52 +03:00
void CClient : : commitPackage ( CPackForClient * pack )
{
CommitPackage cp ;
2011-06-11 02:50:32 +03:00
cp . freePack = false ;
2011-05-22 21:46:52 +03:00
cp . packToCommit = pack ;
2013-03-03 20:06:03 +03:00
sendRequest ( & cp , PlayerColor : : NEUTRAL ) ;
2011-05-22 21:46:52 +03:00
}
2013-03-03 20:06:03 +03:00
PlayerColor CClient : : getLocalPlayer ( ) const
2011-06-11 02:50:32 +03:00
{
if ( LOCPLINT )
return LOCPLINT - > playerID ;
return getCurrentPlayer ( ) ;
}
2011-06-25 17:22:19 +03:00
void CClient : : calculatePaths ( const CGHeroInstance * h )
{
assert ( h ) ;
boost : : unique_lock < boost : : mutex > pathLock ( pathMx ) ;
gs - > calculatePaths ( h , * pathInfo ) ;
}
2011-08-25 18:24:37 +03:00
void CClient : : commenceTacticPhaseForInt ( CBattleGameInterface * battleInt )
{
2012-06-27 23:44:01 +03:00
setThreadName ( " CClient::commenceTacticPhaseForInt " ) ;
2011-08-25 18:24:37 +03:00
try
{
battleInt - > yourTacticPhase ( gs - > curB - > tacticDistance ) ;
2011-08-26 00:08:53 +03:00
if ( gs & & ! ! gs - > curB & & gs - > curB - > tacticDistance ) //while awaiting for end of tactics phase, many things can happen (end of battle... or game)
{
2013-03-03 20:06:03 +03:00
MakeAction ma ( BattleAction : : makeEndOFTacticPhase ( gs - > curB - > playerToSide ( battleInt - > playerID ) ) ) ;
2012-03-26 01:46:14 +03:00
sendRequest ( & ma , battleInt - > playerID ) ;
2011-08-26 00:08:53 +03:00
}
2011-08-25 18:24:37 +03:00
} HANDLE_EXCEPTION
}
2011-09-03 05:54:33 +03:00
void CClient : : invalidatePaths ( const CGHeroInstance * h /*= NULL*/ )
{
if ( ! h | | pathInfo - > hero = = h )
pathInfo - > isValid = false ;
}
2013-03-03 20:06:03 +03:00
int CClient : : sendRequest ( const CPack * request , PlayerColor player )
2012-03-26 01:46:14 +03:00
{
static ui32 requestCounter = 0 ;
ui32 requestID = requestCounter + + ;
tlog5 < < boost : : format ( " Sending a request \" %s \" . It'll have an ID=%d. \n " )
% typeid ( * request ) . name ( ) % requestID ;
waitingRequest . pushBack ( requestID ) ;
serv - > sendPackToServer ( * request , player , requestID ) ;
2012-07-15 18:34:00 +03:00
if ( vstd : : contains ( playerint , player ) )
playerint [ player ] - > requestSent ( dynamic_cast < const CPackForServer * > ( request ) , requestID ) ;
2012-03-26 01:46:14 +03:00
return requestID ;
}
2013-02-23 22:16:14 +03:00
void CClient : : campaignMapFinished ( shared_ptr < CCampaignState > camp )
{
endGame ( false ) ;
LOCPLINT = nullptr ; //TODO free res
GH . curInt = CGPreGame : : create ( ) ;
auto & epilogue = camp - > camp - > scenarios [ camp - > mapsConquered . back ( ) ] . epilog ;
auto finisher = [ & ] ( )
{
if ( camp - > mapsRemaining . size ( ) )
proposeNextMission ( camp ) ;
else
finishCampaign ( camp ) ;
} ;
if ( epilogue . hasPrologEpilog )
{
GH . pushInt ( new CPrologEpilogVideo ( epilogue , finisher ) ) ;
}
else
{
finisher ( ) ;
}
}
2009-03-28 20:46:20 +02:00
template void CClient : : serialize ( CISer < CLoadFile > & h , const int version ) ;
2009-06-06 18:17:07 +03:00
template void CClient : : serialize ( COSer < CSaveFile > & h , const int version ) ;
2010-09-03 21:42:54 +03:00
void CServerHandler : : startServer ( )
{
th . update ( ) ;
2011-06-03 06:23:50 +03:00
serverThread = new boost : : thread ( & CServerHandler : : callServer , this ) ; //runs server executable;
2010-09-03 21:42:54 +03:00
if ( verbose )
2011-12-14 00:23:17 +03:00
tlog0 < < " Setting up thread calling server: " < < th . getDiff ( ) < < std : : endl ;
2010-09-03 21:42:54 +03:00
}
void CServerHandler : : waitForServer ( )
{
if ( ! serverThread )
startServer ( ) ;
th . update ( ) ;
intpr : : scoped_lock < intpr : : interprocess_mutex > slock ( shared - > sr - > mutex ) ;
while ( ! shared - > sr - > ready )
{
shared - > sr - > cond . wait ( slock ) ;
}
if ( verbose )
2011-12-14 00:23:17 +03:00
tlog0 < < " Waiting for server: " < < th . getDiff ( ) < < std : : endl ;
2010-09-03 21:42:54 +03:00
}
CConnection * CServerHandler : : connectToServer ( )
{
if ( ! shared - > sr - > ready )
waitForServer ( ) ;
2012-09-22 18:16:45 +03:00
th . update ( ) ; //put breakpoint here to attach to server before it does something stupid
2012-01-12 18:23:00 +03:00
CConnection * ret = justConnectToServer ( settings [ " server " ] [ " server " ] . String ( ) , port ) ;
2010-10-24 14:35:14 +03:00
2010-09-03 21:42:54 +03:00
if ( verbose )
2011-12-14 00:23:17 +03:00
tlog0 < < " \t Connecting to the server: " < < th . getDiff ( ) < < std : : endl ;
2010-09-03 21:42:54 +03:00
return ret ;
}
2010-10-24 14:35:14 +03:00
CServerHandler : : CServerHandler ( bool runServer /*= false*/ )
2010-09-03 21:42:54 +03:00
{
serverThread = NULL ;
shared = NULL ;
2012-01-12 18:23:00 +03:00
port = boost : : lexical_cast < std : : string > ( settings [ " server " ] [ " port " ] . Float ( ) ) ;
2013-02-19 02:10:46 +03:00
verbose = true ;
2010-09-03 21:42:54 +03:00
boost : : interprocess : : shared_memory_object : : remove ( " vcmi_memory " ) ; //if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
try
{
shared = new SharedMem ( ) ;
} HANDLE_EXCEPTIONC ( tlog1 < < " Cannot open interprocess memory: " ; )
}
CServerHandler : : ~ CServerHandler ( )
{
delete shared ;
delete serverThread ; //detaches, not kills thread
}
void CServerHandler : : callServer ( )
{
2012-06-27 23:44:01 +03:00
setThreadName ( " CServerHandler::callServer " ) ;
2013-03-02 21:41:25 +03:00
std : : string logName = VCMIDirs : : get ( ) . localPath ( ) + " /server_log.txt " ;
std : : string comm = VCMIDirs : : get ( ) . serverPath ( ) + " " + port + " > " + logName ;
2012-06-02 18:16:54 +03:00
int result = std : : system ( comm . c_str ( ) ) ;
if ( result = = 0 )
tlog1 < < " Server closed correctly \n " ;
else
2012-11-16 00:29:22 +03:00
{
2012-06-02 18:16:54 +03:00
tlog0 < < " Error: server failed to close correctly or crashed! \n " ;
2012-11-16 00:29:22 +03:00
tlog0 < < " Check " < < logName < < " for more info \n " ;
exit ( 1 ) ; // exit in case of error. Othervice without working server VCMI will hang
}
2010-09-03 21:42:54 +03:00
}
2010-10-24 14:35:14 +03:00
CConnection * CServerHandler : : justConnectToServer ( const std : : string & host , const std : : string & port )
{
CConnection * ret = NULL ;
while ( ! ret )
{
try
{
tlog0 < < " Establishing connection... \n " ;
2012-01-12 18:23:00 +03:00
ret = new CConnection ( host . size ( ) ? host : settings [ " server " ] [ " server " ] . String ( ) ,
port . size ( ) ? port : boost : : lexical_cast < std : : string > ( settings [ " server " ] [ " port " ] . Float ( ) ) ,
2010-10-24 14:35:14 +03:00
NAME ) ;
}
catch ( . . . )
{
tlog1 < < " \n Cannot establish connection! Retrying within 2 seconds " < < std : : endl ;
SDL_Delay ( 2000 ) ;
}
}
return ret ;
2010-10-31 00:53:41 +03:00
}