2008-07-25 20:28:28 +03:00
# include "../CCallback.h"
# include "../CConsoleHandler.h"
2009-05-20 13:08:56 +03:00
# include "CGameInfo.h"
# include "../lib/CGameState.h"
# include "CPlayerInterface.h"
2008-11-09 00:29:19 +02:00
# include "../StartInfo.h"
# include "../hch/CArtHandler.h"
2009-01-11 00:08:18 +02:00
# include "../hch/CDefObjInfoHandler.h"
2008-11-09 00:29:19 +02:00
# include "../hch/CGeneralTextHandler.h"
2009-01-11 00:08:18 +02:00
# include "../hch/CHeroHandler.h"
# include "../hch/CTownHandler.h"
2008-11-09 00:29:19 +02:00
# include "../hch/CObjectHandler.h"
2009-01-11 00:08:18 +02:00
# include "../hch/CBuildingHandler.h"
# include "../hch/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"
2009-05-20 13:08:56 +03:00
# include "../lib/map.h"
2008-11-09 00:29:19 +02:00
# include "../mapHandler.h"
# include "CConfigHandler.h"
# include "Client.h"
2009-11-01 03:15:16 +02:00
# include "GUIBase.h"
2008-07-27 20:07:37 +03:00
# include <boost/bind.hpp>
2008-08-01 14:21:15 +03:00
# include <boost/foreach.hpp>
2008-11-09 00:29:19 +02:00
# include <boost/thread.hpp>
2008-07-30 20:51:19 +03:00
# include <boost/thread/shared_mutex.hpp>
2008-12-27 03:01:59 +02:00
# include <sstream>
2009-03-07 00:25:19 +02:00
# undef DLL_EXPORT
# define DLL_EXPORT
2009-03-15 14:53:58 +02:00
# include "../lib/RegisterTypes.cpp"
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
*
*/
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 ( ) { }
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 ) ;
}
} ;
class CCLApplier
{
public :
std : : map < ui16 , CBaseForCLApply * > apps ;
CCLApplier ( )
{
registerTypes2 ( * this ) ;
}
2010-01-02 03:48:44 +02:00
~ CCLApplier ( )
{
std : : map < ui16 , CBaseForCLApply * > : : iterator iter ;
for ( iter = apps . begin ( ) ; iter ! = apps . end ( ) ; iter + + )
delete iter - > second ;
}
2009-03-07 00:25:19 +02:00
template < typename T > void registerType ( const T * t = NULL )
{
2009-03-07 17:54:12 +02:00
ui16 ID = typeList . registerType ( t ) ;
2009-03-07 00:25:19 +02:00
apps [ ID ] = new CApplyOnCL < T > ;
}
2009-03-07 17:54:12 +02:00
} * 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 ;
2009-03-07 17:54:12 +02:00
applier = new CCLApplier ;
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 ;
2009-11-01 03:15:16 +02:00
terminate = false ;
2009-12-19 14:31:57 +02: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
2009-01-31 00:23:13 +02:00
try
{
shared = new SharedMem ( ) ;
2009-12-19 14:31:57 +02:00
} HANDLE_EXCEPTIONC ( tlog1 < < " Cannot open interprocess memory: " ; )
2009-01-30 23:28:02 +02:00
}
CClient : : CClient ( void )
2009-08-04 02:53:18 +03:00
: waitingRequest ( false )
2009-01-30 23:28:02 +02:00
{
init ( ) ;
2008-07-25 20:28:28 +03:00
}
CClient : : CClient ( CConnection * con , StartInfo * si )
2009-08-04 02:53:18 +03:00
: waitingRequest ( false )
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
}
CClient : : ~ CClient ( void )
{
2009-08-30 15:47:40 +03:00
delete pathInfo ;
2009-03-07 17:54:12 +02:00
delete applier ;
2009-01-30 23:28:02 +02:00
delete shared ;
2008-07-25 20:28:28 +03:00
}
2008-08-04 18:56:36 +03:00
void CClient : : waitForMoveAndSend ( int color )
{
2008-08-30 00:41:32 +03:00
try
{
BattleAction ba = playerint [ color ] - > activeStack ( gs - > curB - > activeStack ) ;
2009-03-09 21:40:43 +02:00
* serv < < & MakeAction ( ba ) ;
2008-08-30 00:41:32 +03:00
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 ( )
{
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-01-02 03:48:44 +02:00
pack = retreivePack ( ) ; //get the package from the server
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
}
catch ( const std : : exception & e )
{
tlog3 < < " Lost connection to server, ending listening thread! \n " ;
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
}
2009-11-01 03:15:16 +02:00
void CClient : : stop ( )
2008-08-25 13:25:16 +03:00
{
2009-11-01 03:15:16 +02:00
// Game is ending
2010-01-02 03:48:44 +02:00
// Tell the network thread to reach a stable state
2009-11-01 03:15:16 +02:00
terminate = true ;
2010-02-13 06:47:31 +02:00
GH . curInt = NULL ;
LOCPLINT - > terminate_cond . setn ( true ) ;
2009-12-31 13:04:29 +02:00
LOCPLINT - > pim - > lock ( ) ;
2009-12-28 06:08:24 +02:00
endGame ( ) ;
2010-02-13 23:45:46 +02:00
tlog0 < < " Client stopped. " < < std : : endl ;
2008-08-25 13:25:16 +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 ;
}
2009-03-09 12:37:49 +02:00
* serv < < & SaveGame ( fname ) ;
2008-11-16 03:06:15 +02:00
}
2008-12-27 03:01:59 +02:00
2009-11-01 03:15:16 +02:00
void CClient : : endGame ( )
2009-01-11 00:08:18 +02:00
{
2009-11-01 03:15:16 +02:00
tlog0 < < " \n \n Ending current game! " < < std : : endl ;
2010-01-30 14:46:15 +02:00
if ( GH . topInt ( ) )
GH . topInt ( ) - > deactivate ( ) ;
2009-12-28 06:08:24 +02:00
GH . listInt . clear ( ) ;
GH . objsToBlit . clear ( ) ;
2010-02-20 15:24:38 +02:00
GH . statusbar = NULL ;
2010-02-13 23:45:46 +02:00
tlog0 < < " Removed GUI. " < < std : : endl ;
2009-01-11 00:08:18 +02:00
delete CGI - > mh ;
2009-11-01 03:15:16 +02:00
CGI - > mh = NULL ;
2009-06-23 11:14:49 +03:00
2009-11-01 03:15:16 +02:00
delete CGI - > state ;
CGI - > state = NULL ;
2010-02-13 23:45:46 +02:00
tlog0 < < " Deleted mapHandler and gameState. " < < std : : endl ;
2009-01-11 00:08:18 +02:00
2009-12-28 06:08:24 +02:00
LOCPLINT = NULL ;
2009-11-01 03:15:16 +02:00
while ( ! playerint . empty ( ) )
2009-01-11 00:08:18 +02:00
{
2009-11-01 03:15:16 +02:00
delete playerint . begin ( ) - > second ;
playerint . erase ( playerint . begin ( ) ) ;
2009-03-19 16:17:19 +02:00
}
2009-06-23 11:14:49 +03:00
BOOST_FOREACH ( CCallback * cb , callbacks )
{
delete cb ;
}
2010-02-13 23:45:46 +02:00
tlog0 < < " Deleted playerInts. " < < std : : endl ;
2009-11-01 03:15:16 +02:00
2009-12-28 06:08:24 +02:00
if ( serv )
{
2010-02-13 23:45:46 +02:00
tlog0 < < " Connection has been requested to be closed. \n " ;
2009-11-01 03:15:16 +02:00
boost : : unique_lock < boost : : mutex > ( * serv - > wmx ) ;
* serv < < & CloseServer ( ) ;
2010-02-13 23:45:46 +02:00
tlog0 < < " Sent closing signal to the server \n " ;
2009-11-01 03:15:16 +02:00
serv - > close ( ) ;
delete serv ;
serv = NULL ;
tlog3 < < " Our socket has been closed. " < < std : : endl ;
}
2009-12-28 06:08:24 +02:00
connectionHandler - > join ( ) ;
2010-02-13 23:45:46 +02:00
tlog0 < < " Connection handler thread joined " < < std : : endl ;
2009-12-28 06:08:24 +02:00
delete connectionHandler ;
connectionHandler = NULL ;
2009-11-01 03:15:16 +02:00
}
void CClient : : loadGame ( const std : : string & fname )
{
tlog0 < < " \n \n Loading procedure started! \n \n " ;
timeHandler tmh ;
2009-01-11 00:08:18 +02:00
char portc [ 10 ] ;
SDL_itoa ( conf . cc . port , portc , 10 ) ;
runServer ( portc ) ; //create new server
tlog0 < < " Restarting server: " < < tmh . getDif ( ) < < std : : endl ;
{
2009-02-01 16:11:41 +02:00
ui32 ver ;
2009-01-11 00:08:18 +02:00
char sig [ 8 ] ;
CMapHeader dum ;
CGI - > mh = new CMapHandler ( ) ;
2010-02-24 15:03:36 +02:00
StartInfo * si ;
2009-01-11 00:08:18 +02:00
CLoadFile lf ( fname + " .vlgm1 " ) ;
2010-02-24 15:03:36 +02:00
lf > > sig > > dum > > si ;
2009-01-11 00:08:18 +02:00
tlog0 < < " Reading save signature: " < < tmh . getDif ( ) < < std : : endl ;
2010-02-24 15:03:36 +02:00
2009-01-11 00:08:18 +02:00
lf > > * VLC ;
CGI - > setFromLib ( ) ;
tlog0 < < " Reading handlers: " < < tmh . getDif ( ) < < std : : endl ;
lf > > gs ;
tlog0 < < " Reading gamestate: " < < tmh . getDif ( ) < < std : : endl ;
CGI - > state = gs ;
CGI - > mh - > map = gs - > map ;
2009-08-30 15:47:40 +03:00
pathInfo = new CPathsInfo ( int3 ( gs - > map - > width , gs - > map - > height , gs - > map - > twoLevel + 1 ) ) ;
2009-01-11 00:08:18 +02:00
CGI - > mh - > init ( ) ;
tlog0 < < " Initing maphandler: " < < tmh . getDif ( ) < < std : : endl ;
}
waitForServer ( ) ;
tlog0 < < " Waiting for server: " < < tmh . getDif ( ) < < std : : endl ;
serv = new CConnection ( conf . cc . server , portc , NAME ) ;
2009-10-06 03:32:33 +03:00
serv - > setGS ( gs ) ;
2009-01-11 00:08:18 +02:00
tlog0 < < " Setting up connection: " < < tmh . getDif ( ) < < std : : endl ;
ui8 pom8 ;
* serv < < ui8 ( 3 ) < < ui8 ( 1 ) ; //load game; one client
* serv < < fname ;
* serv > > pom8 ;
if ( pom8 )
throw " Server cannot open the savegame! " ;
else
tlog0 < < " Server opened savegame properly. \n " ;
* serv < < ui8 ( gs - > scenarioOps - > playerInfos . size ( ) + 1 ) ; //number of players + neutral
for ( size_t i = 0 ; i < gs - > scenarioOps - > playerInfos . size ( ) ; i + + )
{
* serv < < ui8 ( gs - > scenarioOps - > playerInfos [ i ] . color ) ; //players
}
* serv < < ui8 ( 255 ) ; // neutrals
tlog0 < < " Sent info to server: " < < tmh . getDif ( ) < < std : : endl ;
2009-07-18 06:13:13 +03:00
{
CLoadFile lf ( fname + " .vcgm1 " ) ;
lf > > * this ;
2009-01-11 00:08:18 +02:00
}
}
2008-12-27 03:01:59 +02:00
int CClient : : getCurrentPlayer ( )
{
return gs - > currentPlayer ;
}
int CClient : : getSelectedHero ( )
{
return IGameCallback : : getSelectedHero ( getCurrentPlayer ( ) ) - > id ;
2009-01-11 00:08:18 +02:00
}
void CClient : : newGame ( CConnection * con , StartInfo * si )
{
2009-12-20 19:14:14 +02:00
if ( con = = NULL )
{
2009-11-01 03:15:16 +02:00
timeHandler pomtime ;
char portc [ 10 ] ;
SDL_itoa ( conf . cc . port , portc , 10 ) ;
CClient : : runServer ( portc ) ;
tlog0 < < " Preparing shared memory and starting server: " < < pomtime . getDif ( ) < < std : : endl ;
pomtime . getDif ( ) ; //reset timers
//wait until server is ready
tlog0 < < " Waiting for server... " ;
waitForServer ( ) ;
tlog0 < < pomtime . getDif ( ) < < std : : endl ;
while ( ! con )
{
try
{
tlog0 < < " Establishing connection... \n " ;
con = new CConnection ( conf . cc . server , portc , NAME ) ;
}
catch ( . . . )
{
tlog1 < < " \n Cannot establish connection! Retrying within 2 seconds " < < std : : endl ;
SDL_Delay ( 2000 ) ;
}
}
THC tlog0 < < " \t Connecting to the server: " < < pomtime . getDif ( ) < < std : : endl ;
}
2009-01-11 00:08:18 +02:00
timeHandler tmh ;
CGI - > state = new CGameState ( ) ;
tlog0 < < " \t Gamestate: " < < tmh . getDif ( ) < < std : : endl ;
serv = con ;
2009-10-06 03:32:33 +03:00
serv - > setGS ( CGI - > state ) ;
2009-01-11 00:08:18 +02:00
CConnection & c ( * con ) ;
////////////////////////////////////////////////////
ui8 pom8 ;
c < < ui8 ( 2 ) < < ui8 ( 1 ) ; //new game; one client
c < < * si ;
c > > pom8 ;
if ( pom8 )
throw " Server cannot open the map! " ;
else
tlog0 < < " Server opened map properly. \n " ;
c < < ui8 ( si - > playerInfos . size ( ) + 1 ) ; //number of players + neutral
for ( size_t i = 0 ; i < si - > playerInfos . size ( ) ; i + + )
{
c < < ui8 ( si - > playerInfos [ i ] . color ) ; //players
}
c < < ui8 ( 255 ) ; // neutrals
ui32 seed , sum ;
std : : string mapname ;
c > > mapname > > sum > > seed ;
tlog0 < < " \t Sending/Getting info to/from the server: " < < tmh . getDif ( ) < < std : : endl ;
Mapa * mapa = new Mapa ( mapname ) ;
tlog0 < < " Reading and detecting map file (together): " < < tmh . getDif ( ) < < std : : endl ;
tlog0 < < " \t Server checksum for " < < mapname < < " : " < < sum < < std : : endl ;
tlog0 < < " \t Our checksum for the map: " < < mapa - > checksum < < std : : endl ;
if ( mapa - > checksum ! = sum )
{
tlog1 < < " Wrong map checksum!!! " < < std : : endl ;
throw std : : string ( " Wrong checksum " ) ;
}
tlog0 < < " \t Using random seed: " < < seed < < std : : endl ;
gs = CGI - > state ;
gs - > scenarioOps = si ;
gs - > init ( si , mapa , seed ) ;
CGI - > mh = new CMapHandler ( ) ;
tlog0 < < " Initializing GameState (together): " < < tmh . getDif ( ) < < std : : endl ;
CGI - > mh - > map = mapa ;
tlog0 < < " Creating mapHandler: " < < tmh . getDif ( ) < < std : : endl ;
CGI - > mh - > init ( ) ;
2009-08-30 15:47:40 +03:00
pathInfo = new CPathsInfo ( int3 ( mapa - > width , mapa - > height , mapa - > twoLevel + 1 ) ) ;
2009-01-11 00:08:18 +02:00
tlog0 < < " Initializing mapHandler (together): " < < tmh . getDif ( ) < < std : : endl ;
2010-02-20 15:24:38 +02:00
int humanPlayers = 0 ;
2009-08-07 01:36:51 +03:00
for ( size_t i = 0 ; i < gs - > scenarioOps - > playerInfos . size ( ) ; + + i ) //initializing interfaces for players
2009-01-11 00:08:18 +02:00
{
ui8 color = gs - > scenarioOps - > playerInfos [ i ] . color ;
CCallback * cb = new CCallback ( gs , color , this ) ;
2010-02-20 15:24:38 +02:00
if ( ! gs - > scenarioOps - > playerInfos [ i ] . human )
{
2009-01-11 00:08:18 +02:00
playerint [ color ] = static_cast < CGameInterface * > ( CAIHandler : : getNewAI ( cb , conf . cc . defaultAI ) ) ;
}
2010-02-20 15:24:38 +02:00
else
{
2009-01-11 00:08:18 +02:00
playerint [ color ] = new CPlayerInterface ( color , i ) ;
2010-02-20 15:24:38 +02:00
humanPlayers + + ;
2009-01-11 00:08:18 +02:00
}
gs - > currentPlayer = color ;
playerint [ color ] - > init ( cb ) ;
}
2010-02-20 15:24:38 +02:00
hotSeat = ( humanPlayers > 1 ) ;
2009-01-11 00:08:18 +02:00
playerint [ 255 ] = CAIHandler : : getNewAI ( cb , conf . cc . defaultAI ) ;
playerint [ 255 ] - > init ( new CCallback ( gs , 255 , this ) ) ;
}
void CClient : : runServer ( const char * portc )
{
2009-10-17 16:13:09 +03:00
static std : : string comm = std : : string ( BIN_DIR PATH_SEPARATOR SERVER_NAME " " ) + portc + " > server_log.txt " ; //needs to be static, if not - will be probably destroyed before new thread reads it
2009-01-11 00:08:18 +02:00
boost : : thread servthr ( boost : : bind ( system , comm . c_str ( ) ) ) ; //runs server executable; //TODO: will it work on non-windows platforms?
}
void CClient : : waitForServer ( )
{
2009-01-30 23:28:02 +02:00
intpr : : scoped_lock < intpr : : interprocess_mutex > slock ( shared - > sr - > mutex ) ;
while ( ! shared - > sr - > ready )
2009-01-11 00:08:18 +02:00
{
2009-01-30 23:28:02 +02:00
shared - > sr - > cond . wait ( slock ) ;
2009-01-11 00:08:18 +02:00
}
2009-01-30 23:28:02 +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 ;
for ( std : : map < ui8 , CGameInterface * > : : iterator i = playerint . begin ( ) ; i ! = playerint . end ( ) ; i + + )
{
h & i - > first & i - > second - > dllName ;
i - > second - > serialize ( h , version ) ;
}
}
else
{
ui8 players ;
h & players ;
for ( int i = 0 ; i < players ; i + + )
{
std : : string dllname ;
ui8 pid ;
h & pid & dllname ;
2009-07-18 06:13:13 +03:00
CCallback * callback = new CCallback ( gs , pid , this ) ;
callbacks . insert ( callback ) ;
CGameInterface * nInt = NULL ;
2009-03-28 20:46:20 +02:00
if ( dllname . length ( ) )
2009-07-18 06:13:13 +03:00
nInt = CAIHandler : : getNewAI ( callback , dllname ) ;
else
nInt = new CPlayerInterface ( pid , i ) ;
playerint [ pid ] = nInt ;
nInt - > init ( callback ) ;
nInt - > serialize ( h , version ) ;
2009-03-28 20:46:20 +02:00
}
}
}
2010-01-02 03:48:44 +02:00
CPack * CClient : : retreivePack ( )
{
CPack * ret = NULL ;
boost : : unique_lock < boost : : mutex > lock ( * serv - > rmx ) ;
tlog5 < < " Listening... " ;
* serv > > ret ;
tlog5 < < " \t received server message of type " < < typeid ( * ret ) . name ( ) < < std : : endl ;
return ret ;
}
void CClient : : handlePack ( CPack * pack )
{
CBaseForCLApply * apply = applier - > apps [ typeList . getTypeID ( pack ) ] ; //find the applier
if ( apply )
{
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
{
tlog1 < < " Message cannot be applied, cannot find applier! \n " ;
}
delete pack ;
}
2009-08-04 02:53:18 +03:00
2010-03-11 01:16:30 +02:00
void CClient : : updatePaths ( )
{
const CGHeroInstance * h = getHero ( getSelectedHero ( ) ) ;
if ( h ) //if we have selected hero...
gs - > calculatePaths ( h , * pathInfo ) ;
}
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 ) ;