2
0
Эх сурвалжийг харах

Support for saving/loading in player interfaces, including VCAI.
Minor changes.

Michał W. Urbańczyk 12 жил өмнө
parent
commit
5c2473d436

+ 2 - 2
AI/BattleAI/BattleAI.cpp

@@ -89,12 +89,12 @@ void CBattleAI::init( CBattleCallback * CB )
 	CB->unlockGsWhenWaiting = false;
 }
 
-void CBattleAI::actionFinished( const BattleAction *action )
+void CBattleAI::actionFinished(const BattleAction &action)
 {
 	print("actionFinished called");
 }
 
-void CBattleAI::actionStarted( const BattleAction *action )
+void CBattleAI::actionStarted(const BattleAction &action)
 {
 	print("actionStarted called");
 }

+ 2 - 2
AI/BattleAI/BattleAI.h

@@ -18,8 +18,8 @@ public:
 	~CBattleAI(void);
 
 	void init(CBattleCallback * CB) OVERRIDE;
-	void actionFinished(const BattleAction *action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
-	void actionStarted(const BattleAction *action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
+	void actionFinished(const BattleAction &action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
+	void actionStarted(const BattleAction &action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
 	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack
 
 	void battleAttack(const BattleAttack *ba) OVERRIDE; //called when stack is performing attack

+ 14 - 2
AI/StupidAI/StupidAI.cpp

@@ -25,12 +25,12 @@ void CStupidAI::init( CBattleCallback * CB )
 	cbc = cb = CB;
 }
 
-void CStupidAI::actionFinished( const BattleAction *action )
+void CStupidAI::actionFinished(const BattleAction &action)
 {
 	print("actionFinished called");
 }
 
-void CStupidAI::actionStarted( const BattleAction *action )
+void CStupidAI::actionStarted(const BattleAction &action)
 {
 	print("actionStarted called");
 }
@@ -312,3 +312,15 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
 	}
 }
 
+void CStupidAI::saveGame(COSer<CSaveFile> &h, const int version)
+{
+	//TODO to be implemented with saving/loading during the battles
+	assert(0);
+}
+
+void CStupidAI::loadGame(CISer<CLoadFile> &h, const int version)
+{
+	//TODO to be implemented with saving/loading during the battles
+	assert(0);
+}
+

+ 6 - 2
AI/StupidAI/StupidAI.h

@@ -13,8 +13,8 @@ public:
 	~CStupidAI(void);
 
 	void init(CBattleCallback * CB) OVERRIDE;
-	void actionFinished(const BattleAction *action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
-	void actionStarted(const BattleAction *action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
+	void actionFinished(const BattleAction &action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
+	void actionStarted(const BattleAction &action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
 	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack
 
 	void battleAttack(const BattleAttack *ba) OVERRIDE; //called when stack is performing attack
@@ -35,5 +35,9 @@ public:
 	void battleStacksRemoved(const BattleStacksRemoved & bsr) OVERRIDE; //called when certain stack is completely removed from battlefield
 
 	BattleAction goTowards(const CStack * stack, BattleHex hex );
+
+	virtual void saveGame(COSer<CSaveFile> &h, const int version) OVERRIDE;
+	virtual void loadGame(CISer<CLoadFile> &h, const int version) OVERRIDE;
+
 };
 

+ 14 - 7
AI/VCAI/VCAI.cpp

@@ -573,7 +573,6 @@ void VCAI::heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visi
 	NET_EVENT_HANDLER;
 	if (start)
 	{
-		visitedObject = const_cast<CGObjectInstance *>(visitedObj); // remember the object and wait for return
 		markObjectVisited (visitedObj);
 		erase_if_present(reservedObjs, visitedObj); //unreserve objects
 		erase_if_present(reservedHeroesMap[visitor], visitedObj);
@@ -677,6 +676,10 @@ void VCAI::objectRemoved(const CGObjectInstance *obj)
 	NET_EVENT_HANDLER;
 
 	erase_if_present(visitableObjs, obj);
+	erase_if_present(alreadyVisited, obj);
+	erase_if_present(reservedObjs, obj);
+
+
 	BOOST_FOREACH(auto &p, reservedHeroesMap)
 		erase_if_present(p.second, obj);
 
@@ -837,7 +840,7 @@ void VCAI::yourTurn()
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 	status.startedTurn();
-	makingTurn = new boost::thread(&VCAI::makeTurn, this);
+	makingTurn = make_unique<boost::thread>(&VCAI::makeTurn, this);
 }
 
 void VCAI::heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, int queryID)
@@ -894,16 +897,20 @@ void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *do
 	});
 }
 
-void VCAI::serialize(COSer<CSaveFile> &h, const int version)
+void VCAI::saveGame(COSer<CSaveFile> &h, const int version)
 {
 	LOG_TRACE_PARAMS(logAi, "version '%i'", version);
 	NET_EVENT_HANDLER;
+	CAdventureAI::saveGame(h, version);
+	serializeInternal(h, version);
 }
 
-void VCAI::serialize(CISer<CLoadFile> &h, const int version)
+void VCAI::loadGame(CISer<CLoadFile> &h, const int version)
 {
 	LOG_TRACE_PARAMS(logAi, "version '%i'", version);
 	NET_EVENT_HANDLER;
+	CAdventureAI::loadGame(h, version);
+	serializeInternal(h, version);
 }
 
 void makePossibleUpgrades(const CArmedInstance *obj)
@@ -982,7 +989,7 @@ void VCAI::makeTurn()
 		cb->recalculatePaths();
 
 	makeTurnInternal();
-	vstd::clear_pointer(makingTurn);
+	makingTurn.reset();
 
 	return;
 }
@@ -1067,6 +1074,7 @@ bool VCAI::goVisitObj(const CGObjectInstance * obj, HeroPtr h)
 
 void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 {
+	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getHoverText() % obj->pos);
 	switch (obj->ID)
 	{
 		case Obj::CREATURE_GENERATOR1:
@@ -1692,7 +1700,6 @@ bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies /
 bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 {
 	logAi->debugStream() << boost::format("Moving hero %s to tile %s") % h->name % dst;
-	visitedObject = NULL;
 	int3 startHpos = h->visitablePos();
 	bool ret = false;
 	if(startHpos == dst)
@@ -1750,7 +1757,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		}
 		ret = !i;
 	}
-	if (visitedObject) //we step into something interesting
+	if (auto visitedObject = frontOrNull(cb->getVisitableObjs(h->visitablePos()))) //we stand on something interesting
 	{
 		performObjectInteraction (visitedObject, h);
 		//BNLOG("Hero %s moved from %s to %s at %s", h->name % startHpos % visitedObject->hoverName % h->visitablePos());

+ 42 - 6
AI/VCAI/VCAI.h

@@ -2,6 +2,7 @@
 
 #include "../../lib/AI_Base.h"
 #include "../../CCallback.h"
+#include "../../lib/CDefObjInfoHandler.h"
 #include "../../lib/CObjectHandler.h"
 
 #include "../../lib/CThreadHelper.h"
@@ -50,6 +51,12 @@ public:
 
 	const CGHeroInstance *get(bool doWeExpectNull = false) const;
 	bool validAndSet() const;
+
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & this->h & hid & name;
+	}
 };
 
 enum BattleState
@@ -85,6 +92,12 @@ public:
 	bool haveTurn();
 	void attemptedAnsweringQuery(int queryID, int answerRequestID);
 	void receivedAnswerConfirmation(int answerRequestID, int result);
+
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & battle & remainingQueries & requestToQueryID & havingTurn;
+	}
 };
 
 enum EGoals
@@ -168,6 +181,13 @@ struct CGoal
 		}
 		return false;
 	}
+
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & goalType & isElementar & isAbstract & priority;
+		h & value & resID & objid & aid & tile & hero & town & bid;
+	}
 };
 
 enum {NOT_VISIBLE = 0, NOT_CHECKED = 1, NOT_AVAILABLE};
@@ -228,6 +248,12 @@ struct ObjectIdRef
 	ObjectIdRef(const CGObjectInstance *obj);
 
 	bool operator<(const ObjectIdRef &rhs) const;
+
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & id;
+	}
 };
 
 class ObjsVector : public std::vector<ObjectIdRef>
@@ -266,12 +292,11 @@ public:
 	std::string battlename;
 
 	CCallback *myCb;
-	VCAI(void);
-	~VCAI(void);
 
-	CGObjectInstance * visitedObject; //remember currently visted object
+	unique_ptr<boost::thread> makingTurn;
 
-	boost::thread *makingTurn;
+	VCAI(void);
+	~VCAI(void);
 
 	void tryRealize(CGoal g);
 
@@ -288,8 +313,8 @@ public:
 	virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, int queryID) OVERRIDE; //TODO
 	virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel) OVERRIDE; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, int queryID) OVERRIDE; //all stacks operations between these objects become allowed, interface has to call onEnd when done
-	virtual void serialize(COSer<CSaveFile> &h, const int version) OVERRIDE; //saving
-	virtual void serialize(CISer<CLoadFile> &h, const int version) OVERRIDE; //loading
+	virtual void saveGame(COSer<CSaveFile> &h, const int version) OVERRIDE; //saving
+	virtual void loadGame(CISer<CLoadFile> &h, const int version) OVERRIDE; //loading
 	virtual void finish() OVERRIDE;
 
 	virtual void availableCreaturesChanged(const CGDwelling *town) OVERRIDE;
@@ -403,6 +428,17 @@ public:
 	void answerQuery(int queryID, int selection);
 	//special function that can be called ONLY from game events handling thread and will send request ASAP
 	void requestActionASAP(boost::function<void()> whatToDo); 
+
+
+	template <typename Handler> void serializeInternal(Handler &h, const int version)
+	{
+		h & knownSubterraneanGates & townVisitsThisWeek & lockedHeroes & reservedHeroesMap;
+		h & visitableObjs & alreadyVisited & reservedObjs;
+		h & saving & status & battlename;
+
+
+		//myCB is restored after load by init call
+	}
 };
 
 class cannotFulfillGoalException : public std::exception

+ 0 - 1
client/CGameInfo.h

@@ -50,7 +50,6 @@ extern CClientState * CCS;
 /// for allowing different functions for accessing game informations
 class CGameInfo
 {
-	ConstTransitivePtr<CGameState> state; //don't touch it in client's code
 public:
 	ConstTransitivePtr<CModHandler> modh; //public?
 	ConstTransitivePtr<CArtHandler> arth;

+ 31 - 42
client/CPlayerInterface.cpp

@@ -1,5 +1,5 @@
 #include "StdInc.h"
-
+#include "../lib/CDefObjInfoHandler.h"
 #include "CAdvmapInterface.h"
 #include "battle/CBattleInterface.h"
 #include "battle/CBattleInterfaceClasses.h"
@@ -731,7 +731,7 @@ void CPlayerInterface::battleNewRound(int round) //called at the beginning of ea
 	battleInt->newRound(round);
 }
 
-void CPlayerInterface::actionStarted(const BattleAction* action)
+void CPlayerInterface::actionStarted(const BattleAction &action)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	if(LOCPLINT != this)
@@ -739,11 +739,11 @@ void CPlayerInterface::actionStarted(const BattleAction* action)
 		return;
 	}
 
-	curAction = new BattleAction(*action);
-	battleInt->startAction(action);
+	curAction = new BattleAction(action);
+	battleInt->startAction(curAction);
 }
 
-void CPlayerInterface::actionFinished(const BattleAction* action)
+void CPlayerInterface::actionFinished(const BattleAction &action)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	if(LOCPLINT != this)
@@ -751,9 +751,9 @@ void CPlayerInterface::actionFinished(const BattleAction* action)
 		return;
 	}
 
+	battleInt->endAction(curAction);
 	delete curAction;
 	curAction = NULL;
-	battleInt->endAction(action);
 }
 
 BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when it's turn of that stack
@@ -1160,55 +1160,44 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
 
 template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, const int version )
 {
-	h & playerID;
-	h & spellbookSettings;
 
-	//sleeping heroes
-	ui8 sleepingSize = 0; //fix for uninitialized warning
-	if (h.saving)
-		sleepingSize = sleepingHeroes.size();
-	h & sleepingSize;
-	for (int i = 0; i < sleepingSize; i++)
-	{
-		ObjectInstanceID hid;
-		if (h.saving)
-			hid = sleepingHeroes[i]->id;
-		h &	hid;
-		if (!h.saving)
-		{
-			const CGHeroInstance *hero = cb->getHero(hid);
-			sleepingHeroes += hero;
-		}
-	}
+	h & observerInDuelMode;
+
+	h & wanderingHeroes & towns & sleepingHeroes;
 
-	//hero list order
-	ui8 heroListSize = 0; //fix for uninitialized warning
-	if (h.saving)
-		heroListSize = wanderingHeroes.size();
+	std::map<const CGHeroInstance *, int3> pathsMap; //hero -> dest
+	if(h.saving)
+	{
+		BOOST_FOREACH(auto &p, paths)
+			pathsMap[p.first] = p.second.endPos();
+		h & pathsMap;
+	}
 	else
-		wanderingHeroes.clear();
-	h & heroListSize;
-	for (int i = 0; i < heroListSize; i++)
-	{
-		ObjectInstanceID hid;
-		if (h.saving)
-			hid = wanderingHeroes[i]->id;
-		h & hid;
-		if (!h.saving)
+	{
+		h & pathsMap;
+
+		CPathsInfo pathsInfo(cb->getMapSize());
+		BOOST_FOREACH(auto &p, pathsMap)
 		{
-			const CGHeroInstance *hero = cb->getHero(hid);
-			wanderingHeroes += hero;
+			cb->calculatePaths(p.first, pathsInfo);
+			CGPath path;
+			pathsInfo.getPath(p.second, path);
+			paths[p.first] = path;
+			logGlobal->traceStream() << boost::format("Restored path for hero %s leading to %s with %d nodes")
+				% p.first->nodeName() % p.second % path.nodes.size();
 		}
 	}
+
+	h & spellbookSettings;
 }
 
-void CPlayerInterface::serialize( COSer<CSaveFile> &h, const int version )
+void CPlayerInterface::saveGame( COSer<CSaveFile> &h, const int version )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	serializeTempl(h,version);
 }
 
-void CPlayerInterface::serialize( CISer<CLoadFile> &h, const int version )
+void CPlayerInterface::loadGame( CISer<CLoadFile> &h, const int version )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	serializeTempl(h,version);

+ 4 - 4
client/CPlayerInterface.h

@@ -182,12 +182,12 @@ public:
 	void gameOver(PlayerColor player, bool victory) OVERRIDE;
 	void playerStartsTurn(PlayerColor player) OVERRIDE; //called before yourTurn on active itnerface
 	void showComp(const Component &comp, std::string message) OVERRIDE; //display component in the advmapint infobox
-	void serialize(COSer<CSaveFile> &h, const int version) OVERRIDE; //saving
-	void serialize(CISer<CLoadFile> &h, const int version) OVERRIDE; //loading
+	void saveGame(COSer<CSaveFile> &h, const int version) OVERRIDE; //saving
+	void loadGame(CISer<CLoadFile> &h, const int version) OVERRIDE; //loading
 
 	//for battles
-	void actionFinished(const BattleAction* action) OVERRIDE;//occurs AFTER action taken by active stack or by the hero
-	void actionStarted(const BattleAction* action) OVERRIDE;//occurs BEFORE action taken by active stack or by the hero
+	void actionFinished(const BattleAction& action) OVERRIDE;//occurs AFTER action taken by active stack or by the hero
+	void actionStarted(const BattleAction& action) OVERRIDE;//occurs BEFORE action taken by active stack or by the hero
 	BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack
 	void battleAttack(const BattleAttack *ba) OVERRIDE; //stack performs attack
 	void battleEnd(const BattleResult *br) OVERRIDE; //end of battle

+ 40 - 27
client/Client.cpp

@@ -78,20 +78,20 @@ public:
 	}
 };
 
-static CApplier<CBaseForCLApply> *applier = NULL;
+static CApplier<CBaseForCLApply> *applier = nullptr;
 
 void CClient::init()
 {
 	hotSeat = false;
-	connectionHandler = NULL;
-	pathInfo = NULL;
+	connectionHandler = nullptr;
+	pathInfo = nullptr;
 	applier = new CApplier<CBaseForCLApply>;
 	registerTypes2(*applier);
 	IObjectInterface::cb = this;
-	serv = NULL;
-	gs = NULL;
-	cb = NULL;
-	erm = NULL;
+	serv = nullptr;
+	gs = nullptr;
+	cb = nullptr;
+	erm = nullptr;
 	terminate = false;
 }
 
@@ -108,7 +108,6 @@ CClient::CClient(CConnection *con, StartInfo *si)
 
 CClient::~CClient(void)
 {
-	delete pathInfo;
 	delete applier;
 }
 
@@ -202,11 +201,9 @@ void CClient::endGame( bool closeConnection /*= true*/ )
 		GH.statusbar = NULL;
         logNetwork->infoStream() << "Removed GUI.";
 
+		vstd::clear_pointer(const_cast<CGameInfo*>(CGI)->mh);
+		vstd::clear_pointer(gs);
 
-		delete CGI->mh;
-		const_cast<CGameInfo*>(CGI)->mh = NULL;
-
-		const_cast<CGameInfo*>(CGI)->state.dellNull();
         logNetwork->infoStream() << "Deleted mapHandler and gameState.";
 		LOCPLINT = NULL;
 	}
@@ -242,10 +239,9 @@ void CClient::loadGame( const std::string & fname )
 			loader = checkingLoader.decay();
 		}
         logNetwork->infoStream() << "Loaded common part of save " << tmh.getDiff();
-		const_cast<CGameInfo*>(CGI)->state = gs;
 		const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
 		const_cast<CGameInfo*>(CGI)->mh->map = gs->map;
-		pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel ? 2 : 1));
+		pathInfo = make_unique<CPathsInfo>(getMapSize());
 		CGI->mh->init();
         logNetwork->infoStream() <<"Initing maphandler: "<<tmh.getDiff();
 
@@ -295,12 +291,12 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 		networkMode = (con->connectionID == 1) ? HOST : GUEST;
 	}
 
-	CStopWatch tmh;
-	const_cast<CGameInfo*>(CGI)->state = new CGameState();
-    logNetwork->infoStream() <<"\tGamestate: "<<tmh.getDiff();
-	CConnection &c(*serv);
+	CConnection &c = *serv;
 	////////////////////////////////////////////////////
 
+	logNetwork->infoStream() <<"\tWill send info to server...";
+	CStopWatch tmh;
+
 	if(networkMode == SINGLE)
 	{
 		ui8 pom8;
@@ -319,7 +315,9 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 	c.disableSmartPointerSerialization();
 
 	// Initialize game state
-	gs = const_cast<CGameInfo*>(CGI)->state;
+	gs = new CGameState();
+	logNetwork->infoStream() <<"\tCreating gamestate: "<<tmh.getDiff();
+
 	gs->scenarioOps = si;
 	gs->init(si);
     logNetwork->infoStream() <<"Initializing GameState (together): "<<tmh.getDiff();
@@ -347,7 +345,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 		CGI->mh->map = gs->map;
         logNetwork->infoStream() <<"Creating mapHandler: "<<tmh.getDiff();
 		CGI->mh->init();
-		pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel ? 2 : 1));
+		pathInfo = make_unique<CPathsInfo>(getMapSize());
         logNetwork->infoStream() <<"Initializing mapHandler (together): "<<tmh.getDiff();
 	}
 
@@ -416,7 +414,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 		loadNeutralBattleAI();
 	}
 
-	serv->addStdVecItems(const_cast<CGameInfo*>(CGI)->state);
+	serv->addStdVecItems(gs);
 	hotSeat = (humanPlayers > 1);
 
 // 	std::vector<FileInfo> scriptModules;
@@ -445,8 +443,11 @@ void CClient::serialize( Handler &h, const int version )
 
 		for(auto i = playerint.begin(); i != playerint.end(); i++)
 		{
-			h & i->first & i->second->dllName;
-			i->second->serialize(h,version);
+			LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
+			assert(i->first == i->second->playerID);
+			h & i->first & i->second->dllName & i->second->human;
+			i->second->saveGame(dynamic_cast<COSer<CSaveFile>&>(h), version); 
+			//evil cast that i still like better than sfinae-magic. If I had a "static if"...
 		}
 	}
 	else
@@ -457,11 +458,13 @@ void CClient::serialize( Handler &h, const int version )
 		for(int i=0; i < players; i++)
 		{
 			std::string dllname;
-			PlayerColor pid = PlayerColor(0); //fix for uninitialized warning
-			h & pid & dllname;
+			PlayerColor pid; 
+			bool isHuman = false;
 
+			h & pid & dllname & isHuman;
+			LOG_TRACE_PARAMS(logGlobal, "Loading player %s interface", pid);
 
-			CGameInterface *nInt = NULL;
+			CGameInterface *nInt = nullptr;
 			if(dllname.length())
 			{
 				if(pid == PlayerColor::NEUTRAL)
@@ -474,15 +477,25 @@ void CClient::serialize( Handler &h, const int version )
 					continue;
 				}
 				else
+				{
+					assert(!isHuman);
 					nInt = CDynLibHandler::getNewAI(dllname);
+				}
 			}
 			else
+			{
+				assert(isHuman);
 				nInt = new CPlayerInterface(pid);
+			}
+
+			nInt->dllName = dllname;
+			nInt->human = isHuman;
+			nInt->playerID = pid;
 
 			battleCallbacks[pid] = callbacks[pid] = make_shared<CCallback>(gs,pid,this);
 			battleints[pid] = playerint[pid] = nInt;
 			nInt->init(callbacks[pid].get());
-			nInt->serialize(h, version);
+			nInt->loadGame(dynamic_cast<CISer<CLoadFile>&>(h), version); //another evil cast, check above
 		}
 
 		if(!vstd::contains(battleints, PlayerColor::NEUTRAL))

+ 3 - 5
client/Client.h

@@ -121,18 +121,16 @@ public:
 	std::map<PlayerColor,CBattleGameInterface *> battleints;
 	bool hotSeat;
 	CConnection *serv;
-	BattleAction *curbaction;
 
-	CPathsInfo *pathInfo;
+	boost::optional<BattleAction> curbaction;
+
+	unique_ptr<CPathsInfo> pathInfo;
 	boost::mutex pathMx; //protects the variable above
 
 	CScriptingModule *erm;
 
 	ThreadSafeVector<int> waitingRequest;
 
-	std::queue<CPack *> packs;
-	boost::mutex packsM;
-
 	void waitForMoveAndSend(PlayerColor color);
 	//void sendRequest(const CPackForServer *request, bool waitForRealization);
 	CClient(void);

+ 4 - 24
client/NetPacksClient.cpp

@@ -671,8 +671,8 @@ void BattleAttack::applyCl( CClient *cl )
 
 void StartAction::applyFirstCl( CClient *cl )
 {
-	cl->curbaction = new BattleAction(ba);
-	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionStarted, &ba);
+	cl->curbaction = ba;
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionStarted, ba);
 }
 
 void BattleSpellCast::applyCl( CClient *cl )
@@ -744,10 +744,8 @@ CGameState* CPackForClient::GS( CClient *cl )
 
 void EndAction::applyCl( CClient *cl )
 {
-	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionFinished, cl->curbaction);
-
-	delete cl->curbaction;
-	cl->curbaction = NULL;
+	BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(actionFinished, *cl->curbaction);
+	cl->curbaction.reset();
 }
 
 void PackageApplied::applyCl( CClient *cl )
@@ -793,24 +791,6 @@ void SaveGame::applyCl(CClient *cl)
 	{
         logNetwork->errorStream() << "Failed to save game:" << e.what();
 	}
-
-// 	try
-// 	{
-// 		auto clientPart = CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME));
-// 		auto libPart = CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME));
-// 		CLoadIntegrityValidator checker(clientPart, libPart);
-// 		
-// 		CMapHeader mh;
-// 		StartInfo *si;
-// 		LibClasses *lib;
-// 		CGameState *game;
-// 
-// 		checker.checkMagicBytes(SAVEGAME_MAGIC);
-// 		checker >> mh >> si >> lib >> game;
-// 	}
-// 	catch(...)
-// 	{
-// 	}
 }
 
 void PlayerMessage::applyCl(CClient *cl)

+ 41 - 2
lib/CGameInterface.cpp

@@ -10,6 +10,7 @@
 #else
 	#include <dlfcn.h>
 #endif
+#include "Connection.h"
 
 /*
  * CGameInterface.cpp, part of VCMI engine
@@ -133,7 +134,7 @@ void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> &
 	battleAI->battleStacksAttacked(bsa);
 }
 
-void CAdventureAI::actionStarted(const BattleAction *action)
+void CAdventureAI::actionStarted(const BattleAction &action)
 {
 	battleAI->actionStarted(action);
 }
@@ -143,7 +144,7 @@ void CAdventureAI::battleNewRoundFirst(int round)
 	battleAI->battleNewRoundFirst(round);
 }
 
-void CAdventureAI::actionFinished(const BattleAction *action)
+void CAdventureAI::actionFinished(const BattleAction &action)
 {
 	battleAI->actionFinished(action);
 }
@@ -203,3 +204,41 @@ void CAdventureAI::yourTacticPhase(int distance)
 {
 	battleAI->yourTacticPhase(distance);
 }
+
+void CAdventureAI::saveGame(COSer<CSaveFile> &h, const int version) /*saving */
+{
+	LOG_TRACE_PARAMS(logAi, "version '%i'", version);
+	CGlobalAI::saveGame(h, version);
+	bool hasBattleAI = battleAI;
+	h << hasBattleAI;
+	if(hasBattleAI)
+	{
+		h << std::string(battleAI->dllName);
+		battleAI->saveGame(h, version);
+	}
+}
+
+void CAdventureAI::loadGame(CISer<CLoadFile> &h, const int version) /*loading */
+{
+	LOG_TRACE_PARAMS(logAi, "version '%i'", version);
+	CGlobalAI::loadGame(h, version);
+	bool hasBattleAI = false;
+	h >> hasBattleAI;
+	if(hasBattleAI)
+	{
+		std::string dllName;
+		h >> dllName;
+		battleAI = CDynLibHandler::getNewBattleAI(dllName);
+		assert(cbc); //it should have been set by the one who new'ed us
+		battleAI->init(cbc);
+		//battleAI->loadGame(h, version);
+	}
+}
+
+void CBattleGameInterface::saveGame(COSer<CSaveFile> &h, const int version)
+{
+}
+
+void CBattleGameInterface::loadGame(CISer<CLoadFile> &h, const int version)
+{
+}

+ 10 - 5
lib/CGameInterface.h

@@ -53,7 +53,7 @@ template <typename Serializer> class COSer;
 struct ArtifactLocation;
 class CScriptingModule;
 
-class CBattleGameInterface : public IBattleEventsReceiver
+class DLL_LINKAGE CBattleGameInterface : public IBattleEventsReceiver
 {
 public:
 	bool human;
@@ -66,6 +66,10 @@ public:
 	//battle call-ins
 	virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack
 	virtual void yourTacticPhase(int distance){}; //called when interface has opportunity to use Tactics skill -> use cb->battleMakeTacticAction from this function
+
+	virtual void saveGame(COSer<CSaveFile> &h, const int version);
+	virtual void loadGame(CISer<CLoadFile> &h, const int version);
+
 };
 
 /// Central class for managing human player / AI interface logic
@@ -86,8 +90,6 @@ public:
 
 	// all stacks operations between these objects become allowed, interface has to call onEnd when done
 	virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, int queryID) = 0;
-	virtual void serialize(COSer<CSaveFile> &h, const int version){}; //saving
-	virtual void serialize(CISer<CLoadFile> &h, const int version){}; //loading
 	virtual void finish(){}; //if for some reason we want to end
 };
 
@@ -124,9 +126,9 @@ public:
 	virtual void battleCatapultAttacked(const CatapultAttack & ca);
 	virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side);
 	virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa);
-	virtual void actionStarted(const BattleAction *action);
+	virtual void actionStarted(const BattleAction &action);
 	virtual void battleNewRoundFirst(int round);
-	virtual void actionFinished(const BattleAction *action);
+	virtual void actionFinished(const BattleAction &action);
 	virtual void battleStacksEffectsSet(const SetStackEffect & sse);
 	//virtual void battleTriggerEffect(const BattleTriggerEffect & bte);
 	virtual void battleStacksRemoved(const BattleStacksRemoved & bsr);
@@ -137,4 +139,7 @@ public:
 	virtual void battleSpellCast(const BattleSpellCast *sc);
 	virtual void battleEnd(const BattleResult *br);
 	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom);
+
+	virtual void saveGame(COSer<CSaveFile> &h, const int version); //saving
+	virtual void loadGame(CISer<CLoadFile> &h, const int version); //loading
 };

+ 1 - 0
lib/Connection.cpp

@@ -511,6 +511,7 @@ int CLoadIntegrityValidator::read( const void * data, unsigned size )
 
 unique_ptr<CLoadFile> CLoadIntegrityValidator::decay()
 {
+	primaryFile->loadedPointers = this->loadedPointers;
 	return std::move(primaryFile);
 }
 

+ 13 - 13
lib/Connection.h

@@ -1160,19 +1160,19 @@ public:
 			data = boost::optional<T>();
 		}
 	}
-	void loadSerializable(CStackInstance *&s)
-	{
-		if(sendStackInstanceByIds)
-		{
-			CArmedInstance *armed;
-			SlotID slot;
-			*this >> armed >> slot;
-			assert(armed->hasStackAtSlot(slot));
-			s = armed->stacks[slot];
-		}
-		else
-			loadSerializableBySerializeCall(s);
-	}
+// 	void loadSerializable(CStackInstance *&s)
+// 	{
+// 		if(sendStackInstanceByIds)
+// 		{
+// 			CArmedInstance *armed;
+// 			SlotID slot;
+// 			*this >> armed >> slot;
+// 			assert(armed->hasStackAtSlot(slot));
+// 			s = armed->stacks[slot];
+// 		}
+// 		else
+// 			loadSerializableBySerializeCall(s);
+// 	}
 
 	template <typename E>
 	void loadEnum(E &data)

+ 2 - 2
lib/IGameEventsReceiver.h

@@ -49,8 +49,8 @@ struct CPackForServer;
 class DLL_LINKAGE IBattleEventsReceiver
 {
 public:
-	virtual void actionFinished(const BattleAction *action){};//occurs AFTER every action taken by any stack or by the hero
-	virtual void actionStarted(const BattleAction *action){};//occurs BEFORE every action taken by any stack or by the hero
+	virtual void actionFinished(const BattleAction &action){};//occurs AFTER every action taken by any stack or by the hero
+	virtual void actionStarted(const BattleAction &action){};//occurs BEFORE every action taken by any stack or by the hero
 	virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
 	virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa){}; //called when stack receives damage (after battleAttack())
 	virtual void battleEnd(const BattleResult *br){};