From fe7ef6bbe168f943b68439207f5a9506c1828e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=2E=20Urba=C5=84czyk?= Date: Tue, 6 Oct 2009 00:32:33 +0000 Subject: [PATCH] Smart map object serialization over net. Fixed doubling click sound on map selection. --- CCallback.cpp | 12 +++--- client/CPreGame.cpp | 11 +++-- client/Client.cpp | 2 + client/NetPacksClient.cpp | 4 +- global.h | 2 +- hch/CObjectHandler.cpp | 3 ++ lib/CGameState.cpp | 19 +++------ lib/CGameState.h | 5 ++- lib/Connection.cpp | 30 +++++++++++++ lib/Connection.h | 90 +++++++++++++++++++++++++++++++++++++++ lib/NetPacksLib.cpp | 7 +-- server/CGameHandler.cpp | 20 ++++----- server/CVCMIServer.cpp | 4 ++ 13 files changed, 169 insertions(+), 40 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index 8903bcb51..829b747a4 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -572,9 +572,9 @@ bool CCallback::battleCanCastSpell() return false; if(gs->curB->side1 == player) - return gs->curB->castSpells[0] == 0 && gs->getHero(gs->curB->hero1)->getArt(17); + return gs->curB->castSpells[0] == 0 && gs->curB->heroes[0]->getArt(17); else - return gs->curB->castSpells[1] == 0 && gs->getHero(gs->curB->hero2)->getArt(17); + return gs->curB->castSpells[1] == 0 && gs->curB->heroes[1]->getArt(17); } bool CCallback::battleCanFlee() @@ -617,13 +617,13 @@ std::pair CCallback::battleEstimateDamage(int attackerID, int defend if(gs->curB->side1 == player) { - attackerHero = gs->getHero(gs->curB->hero1); - defenderHero = gs->getHero(gs->curB->hero2); + attackerHero = gs->curB->heroes[0]; + defenderHero = gs->curB->heroes[1]; } else { - attackerHero = gs->getHero(gs->curB->hero2); - defenderHero = gs->getHero(gs->curB->hero1); + attackerHero = gs->curB->heroes[1]; + defenderHero = gs->curB->heroes[0]; } const CStack * attacker = gs->curB->getStack(attackerID, false), diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index e360c91c0..9e84b3b4d 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -744,9 +744,12 @@ void SelectionTab::showAll( SDL_Surface * to ) void SelectionTab::clickLeft( tribool down, bool previousState ) { - int line = getLine(); - if(line != -1) - select(line); + if(down) + { + int line = getLine(); + if(line != -1) + select(line); + } } void SelectionTab::wheelScrolled( bool down, bool in ) @@ -801,7 +804,7 @@ int SelectionTab::getLine() Point clickPos(GH.current->button.x, GH.current->button.y); clickPos = clickPos - pos.topLeft(); - if (clickPos.y > 115 && clickPos.y < 564 && clickPos.x > 52 && clickPos.x < 366) + if (clickPos.y > 115 && clickPos.y < 564 && clickPos.x > 22 && clickPos.x < 371) { line = (clickPos.y-115) / 25; //which line } diff --git a/client/Client.cpp b/client/Client.cpp index 6aad841ce..d2b7ba874 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -243,6 +243,7 @@ void CClient::load( const std::string & fname ) tlog0 <<"Waiting for server: "<setGS(gs); tlog0 <<"Setting up connection: "<state = new CGameState(); tlog0 <<"\tGamestate: "<setGS(CGI->state); CConnection &c(*con); //////////////////////////////////////////////////// ui8 pom8; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index dc2e23e4b..46196adae 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -347,10 +347,10 @@ void GarrisonDialog::applyCl(CClient *cl) void BattleStart::applyCl( CClient *cl ) { if(vstd::contains(cl->playerint,info->side1)) - cl->playerint[info->side1]->battleStart(&info->army1, &info->army2, info->tile, GS(cl)->getHero(info->hero1), GS(cl)->getHero(info->hero2), 0); + cl->playerint[info->side1]->battleStart(&info->army1, &info->army2, info->tile, info->heroes[0], info->heroes[1], 0); if(vstd::contains(cl->playerint,info->side2)) - cl->playerint[info->side2]->battleStart(&info->army1, &info->army2, info->tile, GS(cl)->getHero(info->hero1), GS(cl)->getHero(info->hero2), 1); + cl->playerint[info->side2]->battleStart(&info->army1, &info->army2, info->tile, info->heroes[0], info->heroes[1], 1); } void BattleNextRound::applyCl( CClient *cl ) diff --git a/global.h b/global.h index 3d9026159..b74897063 100644 --- a/global.h +++ b/global.h @@ -196,7 +196,7 @@ namespace vstd return std::find(c.begin(),c.end(),i); } template - bool operator-=(Container &c, const Item &i) //removes element i from container c, returns false if c does not contain i + typename Container::size_type operator-=(Container &c, const Item &i) //removes element i from container c, returns false if c does not contain i { typename Container::iterator itr = find(c,i); if(itr == c.end()) diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index 0facaa8e2..dacb73b72 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -1155,6 +1155,9 @@ int CGHeroInstance::valOfBonuses( HeroBonus::BonusType type, int subtype /*= -1* bool CGHeroInstance::hasBonusOfType(HeroBonus::BonusType type, int subtype /*= -1*/) const { + if(!this) //to allow calls on NULL and avoid checking duplication + return false; //if hero doesn't exist then bonus neither can + if(subtype == -1) //any subtype { for(std::list::const_iterator i=bonuses.begin(); i != bonuses.end(); i++) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 27618144d..d991e27c4 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1587,11 +1587,8 @@ bool CGameState::battleCanFlee(int player) if(!curB) //there is no battle return false; - const CGHeroInstance *h1 = getHero(curB->hero1); - const CGHeroInstance *h2 = getHero(curB->hero2); - - if(h1 && h1->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE) //eg. one of heroes is wearing shakles of war - || h2 && h2->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE)) + if(curB->heroes[0]->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE) //eg. one of heroes is wearing shakles of war + || curB->heroes[0]->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE)) return false; return true; @@ -1687,9 +1684,7 @@ const CGHeroInstance * CGameState::battleGetOwner(int stackID) if(!curB) return NULL; - si32 ourHero = curB->getStack(stackID)->attackerOwned ? curB->hero1 : curB->hero2; - return getHero(ourHero); - + return curB->heroes[!curB->getStack(stackID)->attackerOwned]; } UpgradeInfo CGameState::getUpgradeInfo(const CArmedInstance *obj, int stackPos) @@ -2515,7 +2510,7 @@ si8 CGameState::battleMaxSpellLevel() si8 levelLimit = SPELL_LEVELS; - const CGHeroInstance *h1 = getHero(curB->hero1); + const CGHeroInstance *h1 = curB->heroes[0]; if(h1) { for(std::list::const_iterator i = h1->bonuses.begin(); i != h1->bonuses.end(); i++) @@ -2523,7 +2518,7 @@ si8 CGameState::battleMaxSpellLevel() amin(levelLimit, i->val); } - const CGHeroInstance *h2 = getHero(curB->hero2); + const CGHeroInstance *h2 = curB->heroes[1]; if(h2) { for(std::list::const_iterator i = h2->bonuses.begin(); i != h2->bonuses.end(); i++) @@ -2759,8 +2754,8 @@ bool CGameState::battleCanShoot(int ID, int dest) if(our->hasFeatureOfType(StackFeature::SHOOTER)//it's shooter && our->owner != dst->owner && dst->alive() - && (!curB->isStackBlocked(ID) || - ( ourHero && ourHero->hasBonusOfType(HeroBonus::FREE_SHOOTING) ) ) + && (!curB->isStackBlocked(ID) + || ourHero->hasBonusOfType(HeroBonus::FREE_SHOOTING)) && our->shots ) return true; diff --git a/lib/CGameState.h b/lib/CGameState.h index 06d3f414f..946360b49 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -125,7 +125,7 @@ struct DLL_EXPORT BattleInfo ui8 siege; // = 0 ordinary battle = 1 a siege with a Fort = 2 a siege with a Citadel = 3 a siege with a Castle si32 tid; //used during town siege - id of attacked town; -1 if not town defence int3 tile; //for background and bonuses - si32 hero1, hero2; + CGHeroInstance *heroes[2]; CCreatureSet army1, army2; std::vector stacks; std::vector obstacles; @@ -134,8 +134,9 @@ struct DLL_EXPORT BattleInfo template void serialize(Handler &h, const int version) { - h & side1 & side2 & round & activeStack & siege & tid & tile & stacks & army1 & army2 & hero1 & hero2 & obstacles + h & side1 & side2 & round & activeStack & siege & tid & tile & stacks & army1 & army2 & obstacles & castSpells & si; + h & heroes; } const CStack * getNextStack() const; //which stack will have turn after current one void getStackQueue(std::vector &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action diff --git a/lib/Connection.cpp b/lib/Connection.cpp index 797efabab..93b5a61a2 100644 --- a/lib/Connection.cpp +++ b/lib/Connection.cpp @@ -4,10 +4,18 @@ #include #include #include + #ifndef _MSC_VER #include "../lib/RegisterTypes.cpp" +#include "../hch/CObjectHandler.h" #endif +//for smart objs serialization over net +#include "CGameState.h" +#include "map.h" +#include "../hch/CObjectHandler.h" + + /* * Connection.cpp, part of VCMI engine * @@ -54,6 +62,7 @@ void CConnection::init() tlog0 << "Established connection with "<> id; + assert(id >= 0 && id < gs->map->objects.size()); + return gs->map->objects[id]; +} + +void CConnection::saveObject( const CGObjectInstance *data ) +{ + assert(gs); + assert(data); + *this << data->id; +} + +void CConnection::setGS( CGameState *state ) +{ + gs = state; +} + CSaveFile::CSaveFile( const std::string &fname ) :sfile(new std::ofstream((USER_DIR "/" + fname).c_str(),std::ios::binary)) { diff --git a/lib/Connection.h b/lib/Connection.h index f2a6a21ff..ee479df0f 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -21,6 +21,8 @@ #include const ui32 version = 710; class CConnection; +class CGObjectInstance; +class CGameState; namespace mpl = boost::mpl; /* @@ -305,6 +307,13 @@ public: ui16 tid = typeList.getTypeID(data); *this << tid; + This()->savePointerHlp(tid, data); + } + + //that part of ptr serialization was extracted to allow customization of its behavior in derived classes + template + void savePointerHlp(ui16 tid, const T &data) + { if(!tid) *this << *data; //if type is unregistered simply write all data in a standard way else @@ -491,6 +500,10 @@ public: template void loadSerializable(T &data) { + ////that const cast would be evil because it allows to implicitly overwrite const objects when deserializing + //typedef typename boost::remove_const::type nonConstT; + //nonConstT &hlp = const_cast(data); + //hlp.serialize(*this,myVersion); data.serialize(*this,myVersion); } template @@ -514,7 +527,13 @@ public: //get type id ui16 tid; *this >> tid; + This()->loadPointerHlp(tid, data); + } + //that part of ptr deserialization was extracted to allow customization of its behavior in derived classes + template + void loadPointerHlp( ui16 tid, T & data ) + { if(!tid) { typedef typename boost::remove_pointer::type npT; @@ -526,6 +545,7 @@ public: loaders[tid]->loadPtr(*this,&data); } } + template void loadSerializable(std::vector &data) { @@ -620,7 +640,9 @@ public: class DLL_EXPORT CConnection :public CISer, public COSer { + CGameState *gs; CConnection(void); + void init(); public: boost::mutex *rmx, *wmx; // read/write mutexes @@ -646,6 +668,74 @@ public: template CConnection &operator&(const T&); ~CConnection(void); + void setGS(CGameState *state); + + CGObjectInstance *loadObject(); //reads id from net and returns that obj + void saveObject(const CGObjectInstance *data); + + + template + struct loadObjectHelper + { + static void invoke(CConnection &s, T &data, ui16 tid) + { + data = static_cast(s.loadObject()); + } + }; + template + struct loadRestHelper + { + static void invoke(CConnection &s, T &data, ui16 tid) + { + s.CISer::loadPointerHlp(tid, data); + } + }; + template + struct saveObjectHelper + { + static void invoke(CConnection &s, const T &data, ui16 tid) + { + //CGObjectInstance *&hlp = const_cast(data); //for loading pointer to const obj we must remove the qualifier + s.saveObject(data); + } + }; + template + struct saveRestHelper + { + static void invoke(CConnection &s, const T &data, ui16 tid) + { + s.COSer::savePointerHlp(tid, data); + } + }; + + + //"overload" loading pointer procedure + template + void loadPointerHlp( ui16 tid, T & data ) + { + typedef typename + //if + mpl::eval_if< boost::is_base_of::type>, + mpl::identity >, + //else + mpl::identity > + >::type typex; + typex::invoke(*this, data, tid); + } + + //"overload" saving pointer procedure + template + void savePointerHlp( ui16 tid, const T & data ) + { + typedef typename + //if + mpl::eval_if< boost::is_base_of::type>, + mpl::identity >, + //else + mpl::identity > + >::type typex; + typex::invoke(*this, data, tid); + } }; #endif // __CONNECTION_H__ diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index f3484c261..f9e268dd1 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -666,10 +666,11 @@ void BattleResult::applyGs( CGameState *gs ) //remove any "until next battle" bonuses CGHeroInstance *h; - h = gs->getHero(gs->curB->hero1); + h = gs->curB->heroes[0]; if(h) h->bonuses.remove_if(HeroBonus::OneBattle); - h = gs->getHero(gs->curB->hero2); + + h = gs->curB->heroes[1]; if(h) h->bonuses.remove_if(HeroBonus::OneBattle); @@ -748,7 +749,7 @@ DLL_EXPORT void StartAction::applyGs( CGameState *gs ) DLL_EXPORT void SpellCast::applyGs( CGameState *gs ) { - CGHeroInstance *h = (side) ? gs->getHero(gs->curB->hero2) : gs->getHero(gs->curB->hero1); + CGHeroInstance *h = (side) ? gs->curB->heroes[1] : gs->curB->heroes[0]; if(h) { int spellCost = 0; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index cd8431c62..921e83f9e 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -358,7 +358,7 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance //check for bad morale => freeze if(next->Morale() < 0 && - !((hero1 && hero1->hasBonusOfType(HeroBonus::BLOCK_MORALE)) || (hero2 && hero2->hasBonusOfType(HeroBonus::BLOCK_MORALE))) //checking if heroes have (or don't have) morale blocking bonuses) + !((hero1->hasBonusOfType(HeroBonus::BLOCK_MORALE)) || (hero2->hasBonusOfType(HeroBonus::BLOCK_MORALE))) //checking if heroes have (or don't have) morale blocking bonuses) ) { if( rand()%24 < (-next->Morale())*2 ) @@ -461,7 +461,7 @@ askInterfaceForMove: && !vstd::contains(next->state,WAITING) && next->alive() && next->Morale() > 0 - && !((hero1 && hero1->hasBonusOfType(HeroBonus::BLOCK_MORALE)) || (hero2 && hero2->hasBonusOfType(HeroBonus::BLOCK_MORALE)) ) //checking if heroes have (or don't have) morale blocking bonuses + && !((hero1->hasBonusOfType(HeroBonus::BLOCK_MORALE)) || (hero2->hasBonusOfType(HeroBonus::BLOCK_MORALE)) ) //checking if heroes have (or don't have) morale blocking bonuses ) if(rand()%24 < next->Morale()) //this stack hasn't got morale this turn goto askInterfaceForMove; //move this stack once more @@ -757,7 +757,7 @@ void CGameHandler::init(StartInfo *si, int Seed) tlog0 << "Map loaded!" << std::endl; gs = new CGameState(); tlog0 << "Gamestate created!" << std::endl; - gs->init(si,map,Seed); + gs->init(si,map,Seed); tlog0 << "Gamestate initialized!" << std::endl; for(std::map::iterator i = gs->players.begin(); i != gs->players.end(); i++) @@ -978,8 +978,8 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet curB->tile = tile; curB->army1=army1; curB->army2=army2; - curB->hero1=(hero1)?(hero1->id):(-1); - curB->hero2=(hero2)?(hero2->id):(-1); + curB->heroes[0] = const_cast(hero1); + curB->heroes[1] = const_cast(hero2); curB->round = -2; curB->activeStack = -1; @@ -2609,7 +2609,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) case 9: //catapult { sendAndApply(&StartAction(ba)); - const CGHeroInstance * attackingHero = (ba.side) ? gs->getHero(gs->curB->hero2) : gs->getHero(gs->curB->hero1); + const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side]; CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(20)]; //artillery int attackedPart = gs->curB->hexToWallPart(ba.destinationTile); @@ -3008,8 +3008,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) { case 1: //hero casts spell { - CGHeroInstance *h = (ba.side) ? gs->getHero(gs->curB->hero2) : gs->getHero(gs->curB->hero1); - CGHeroInstance *secondHero = (!ba.side) ? gs->getHero(gs->curB->hero2) : gs->getHero(gs->curB->hero1); + const CGHeroInstance *h = gs->curB->heroes[ba.side]; + const CGHeroInstance *secondHero = gs->curB->heroes[!ba.side]; if(!h) { tlog2 << "Wrong caster!\n"; @@ -3028,7 +3028,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) || (h->mana < gs->curB->getSpellCost(s, h)) //not enough mana || (ba.additionalInfo < 10) //it's adventure spell (not combat) || (gs->curB->castSpells[ba.side]) //spell has been cast - || (secondHero && secondHero->hasBonusOfType(HeroBonus::SPELL_IMMUNITY, s->id)) //non - casting hero provides immunity for this spell + || (secondHero->hasBonusOfType(HeroBonus::SPELL_IMMUNITY, s->id)) //non - casting hero provides immunity for this spell || (gs->battleMaxSpellLevel() < s->level) //non - casting hero stops caster from casting this spell ) { @@ -3179,7 +3179,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) checkForBattleEnd(gs->curB->stacks); if(battleResult.get()) { - endBattle(gs->curB->tile, getHero(gs->curB->hero1), getHero(gs->curB->hero2)); + endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]); } return true; } diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 47f05c10e..771d18198 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -88,6 +88,7 @@ void CVCMIServer::newGame(CConnection *c) } gh.init(si,rand()); + c->setGS(gh.gs); CConnection* cc; //tcp::socket * ss; for(int i=0; isetGS(gh.gs); } gh.conns.insert(cc); } @@ -192,6 +194,7 @@ void CVCMIServer::loadGame( CConnection *c ) tlog0 <<"Reading handlers"<> (gh.gs); + c->setGS(gh.gs); tlog0 <<"Reading gamestate"<setGS(gh.gs); } gh.conns.insert(cc); }