diff --git a/client/CMT.cpp b/client/CMT.cpp index 5151a8c39..f88491db0 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -224,7 +224,13 @@ int main(int argc, char** argv) ("oneGoodAI", "puts one default AI and the rest will be EmptyAI") ("autoSkip", "automatically skip turns in GUI") ("disable-video", "disable video player") - ("nointro,i", "skips intro movies"); + ("nointro,i", "skips intro movies") + ("loadserver","specifies we are the multiplayer server for loaded games") + ("loadnumplayers",po::value(),"specifies the number of players connecting to a multiplayer game") + ("loadhumanplayerindices",po::value>(),"Indexes of human players (0=Red, etc.)") + ("loadplayer", po::value(),"specifies which player we are in multiplayer loaded games (0=Red, etc.)") + ("loadserverip",po::value(),"IP for loaded game server") + ("loadserverport",po::value(),"port for loaded game server"); if(argc > 1) { @@ -1222,7 +1228,10 @@ void startGame(StartInfo * options, CConnection *serv/* = nullptr*/) case StartInfo::LOAD_GAME: std::string fname = options->mapname; boost::algorithm::erase_last(fname,".vlgm1"); - client->loadGame(fname); + if(!vm.count("loadplayer")) + client->loadGame(fname); + else + client->loadGame(fname,vm.count("loadserver"),vm.count("loadhumanplayerindices") ? vm["loadhumanplayerindices"].as>() : std::vector(),vm.count("loadnumplayers") ? vm["loadnumplayers"].as() : 1,vm["loadplayer"].as(),vm.count("loadserverip") ? vm["loadserverip"].as() : "", vm.count("loadserverport") ? vm["loadserverport"].as() : "3030"); break; } diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 152ce7004..6671d93e6 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1257,14 +1257,15 @@ template void CPlayerInterface::serializeTempl( Handler &h, c { h & pathsMap; - for(auto &p : pathsMap) - { - CGPath path; - cb->getPathsInfo(p.first)->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(); - } + if(cb) + for(auto &p : pathsMap) + { + CGPath path; + cb->getPathsInfo(p.first)->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; diff --git a/client/Client.cpp b/client/Client.cpp index f4d479375..aedebbb44 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -236,14 +236,21 @@ void CClient::endGame( bool closeConnection /*= true*/ ) logNetwork->infoStream() << "Client stopped."; } -void CClient::loadGame( const std::string & fname ) +#if 1 +void CClient::loadGame(const std::string & fname, const bool server, const std::vector& humanplayerindices, const int loadNumPlayers, int player_, const std::string & ipaddr, const std::string & port) { + PlayerColor player(player_); //intentional shadowing + logNetwork->infoStream() <<"Loading procedure started!"; CServerHandler sh; - sh.startServer(); + if(server) + sh.startServer(); + else + serv = sh.justConnectToServer(ipaddr,port=="" ? "3030" : port); CStopWatch tmh; + unique_ptr loader; try { std::string clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME)); @@ -264,7 +271,6 @@ void CClient::loadGame( const std::string & fname ) if(controlServerSaveName.empty()) throw std::runtime_error("Cannot open server part of " + fname); - unique_ptr loader; { CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, minSupportedVersion); loadCommonState(checkingLoader); @@ -276,9 +282,6 @@ void CClient::loadGame( const std::string & fname ) pathInfo = make_unique(getMapSize()); CGI->mh->init(); logNetwork->infoStream() <<"Initing maphandler: "<> *this; - logNetwork->infoStream() << "Loaded client part of save " << tmh.getDiff(); } catch(std::exception &e) { @@ -286,27 +289,60 @@ void CClient::loadGame( const std::string & fname ) throw; //obviously we cannot continue here } - serv = sh.connectToServer(); - serv->addStdVecItems(gs); +/* + if(!server) + player = PlayerColor(player_); +*/ - tmh.update(); - ui8 pom8; - *serv << ui8(3) << ui8(1); //load game; one client - *serv << fname; - *serv >> pom8; - if(pom8) - throw std::runtime_error("Server cannot open the savegame!"); - else - logNetwork->infoStream() << "Server opened savegame properly."; + std::set clientPlayers; + if(server) + serv = sh.connectToServer(); + //*loader >> *this; + + if(server) + { + tmh.update(); + ui8 pom8; + *serv << ui8(3) << ui8(loadNumPlayers); //load game; one client if single-player + *serv << fname; + *serv >> pom8; + if(pom8) + throw std::runtime_error("Server cannot open the savegame!"); + else + logNetwork->infoStream() << "Server opened savegame properly."; + } + + if(server) + { + for(auto & elem : gs->scenarioOps->playerInfos) + if(!std::count(humanplayerindices.begin(),humanplayerindices.end(),elem.first.getNum()) || elem.first==player) + { + clientPlayers.insert(elem.first); + } + clientPlayers.insert(PlayerColor::NEUTRAL); + } + else + { + clientPlayers.insert(player); + } + + std::cout << "CLIENTPLAYERS:\n"; + for(auto x : clientPlayers) + std::cout << x << std::endl; + std::cout << "ENDCLIENTPLAYERS\n"; + + serialize(*loader,0,clientPlayers); + *serv << ui32(clientPlayers.size()); + for(auto & elem : clientPlayers) + *serv << ui8(elem.getNum()); + serv->addStdVecItems(gs); /*why is this here?*/ + + //*loader >> *this; + logNetwork->infoStream() << "Loaded client part of save " << tmh.getDiff(); - *serv << ui32(gs->scenarioOps->playerInfos.size()+1); //number of players + neutral - for(auto & elem : gs->scenarioOps->playerInfos) - { - *serv << ui8(elem.first.getNum()); //players - } - *serv << ui8(PlayerColor::NEUTRAL.getNum()); logNetwork->infoStream() <<"Sent info to server: "<enableStackSendingByID(); serv->disableSmartPointerSerialization(); @@ -320,6 +356,7 @@ void CClient::loadGame( const std::string & fname ) // logGlobal->traceStream() << boost::format("\tindex=%5d --- nullptr") % i; // } } +#endif void CClient::newGame( CConnection *con, StartInfo *si ) { @@ -522,8 +559,77 @@ void CClient::serialize( Handler &h, const int version ) } } +template +void CClient::serialize( Handler &h, const int version, const std::set& playerIDs) +{ + h & hotSeat; + if(h.saving) + { + ui8 players = playerint.size(); + h & players; + + for(auto i = playerint.begin(); i != playerint.end(); i++) + { + 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&>(h), version); + //evil cast that i still like better than sfinae-magic. If I had a "static if"... + } + } + else + { + ui8 players = 0; //fix for uninitialized warning + h & players; + + for(int i=0; i < players; i++) + { + std::string dllname; + PlayerColor pid; + bool isHuman = false; + + h & pid & dllname & isHuman; + LOG_TRACE_PARAMS(logGlobal, "Loading player %s interface", pid); + + shared_ptr nInt; + if(dllname.length()) + { + if(pid == PlayerColor::NEUTRAL) + { + if(playerIDs.count(pid)) + installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid); + //TODO? consider serialization + continue; + } + else + { + assert(!isHuman); + nInt = CDynLibHandler::getNewAI(dllname); + } + } + else + { + assert(isHuman); + nInt = make_shared(pid); + } + + nInt->dllName = dllname; + nInt->human = isHuman; + nInt->playerID = pid; + + if(playerIDs.count(pid)) + installNewPlayerInterface(nInt, pid); + + nInt->loadGame(dynamic_cast&>(h), version); //another evil cast, check above + } + + if(playerIDs.count(PlayerColor::NEUTRAL)) + loadNeutralBattleAI(); + } +} + void CClient::handlePack( CPack * pack ) -{ +{ CBaseForCLApply *apply = applier->apps[typeList.getTypeID(pack)]; //find the applier if(apply) { @@ -740,7 +846,7 @@ void CClient::installNewPlayerInterface(shared_ptr gameInterface boost::unique_lock un(*LOCPLINT->pim); PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE); - if(!color) + if(!color) privilagedGameEventReceivers.push_back(gameInterface); playerint[colorUsed] = gameInterface; @@ -833,6 +939,7 @@ CConnection * CServerHandler::connectToServer() #endif th.update(); //put breakpoint here to attach to server before it does something stupid + CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port); if(verbose) diff --git a/client/Client.h b/client/Client.h index a099d1007..3740c3a1f 100644 --- a/client/Client.h +++ b/client/Client.h @@ -151,7 +151,7 @@ public: void endGame(bool closeConnection = true); void stopConnection(); void save(const std::string & fname); - void loadGame(const std::string & fname); + void loadGame(const std::string & fname, const bool server = true, const std::vector& humanplayerindices = std::vector(), const int loadnumplayers = 1, int player_ = -1, const std::string & ipaddr = "", const std::string & port = ""); void run(); void campaignMapFinished( shared_ptr camp ); void finishCampaign( shared_ptr camp ); @@ -237,5 +237,6 @@ public: ////////////////////////////////////////////////////////////////////////// template void serialize(Handler &h, const int version); + template void serialize(Handler &h, const int version, const std::set& playerIDs); void battleFinished(); }; diff --git a/config/artifacts.json b/config/artifacts.json index 36eb3fa02..13bb52605 100644 --- a/config/artifacts.json +++ b/config/artifacts.json @@ -1656,7 +1656,7 @@ "bonuses" : [ { "type" : "CREATURE_GROWTH", - "subtype" : 2, + "subtype" : 1, "val" : 5, "propagator": "VISITED_TOWN_AND_VISITOR" } @@ -1669,7 +1669,7 @@ "bonuses" : [ { "type" : "CREATURE_GROWTH", - "subtype" : 3, + "subtype" : 2, "val" : 4, "propagator": "VISITED_TOWN_AND_VISITOR" } @@ -1682,7 +1682,7 @@ "bonuses" : [ { "type" : "CREATURE_GROWTH", - "subtype" : 4, + "subtype" : 3, "val" : 3, "propagator": "VISITED_TOWN_AND_VISITOR" } @@ -1695,7 +1695,7 @@ "bonuses" : [ { "type" : "CREATURE_GROWTH", - "subtype" : 5, + "subtype" : 4, "val" : 2, "propagator": "VISITED_TOWN_AND_VISITOR" } @@ -1708,7 +1708,7 @@ "bonuses" : [ { "type" : "CREATURE_GROWTH", - "subtype" : 6, + "subtype" : 5, "val" : 1, "propagator": "VISITED_TOWN_AND_VISITOR" } diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 7dea9f9f0..55276af25 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -387,11 +387,12 @@ CStackInstance * CCreatureSet::detachStack(SlotID slot) CStackInstance *ret = stacks[slot]; //if(CArmedInstance *armedObj = castToArmyObj()) + if(ret) { ret->setArmyObj(nullptr); //detaches from current armyobj + assert(!ret->armyObj); //we failed detaching? } - assert(!ret->armyObj); //we failed detaching? stacks.erase(slot); armyChanged(); return ret; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index c013ccda7..235a8bd0d 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -101,7 +101,11 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro } } if (!nativeArmy) + { ret = VLC->heroh->terrCosts[from.terType]; + ret-=getSecSkillLevel(SecondarySkill::PATHFINDING)*25; + ret = ret < 100 ? 100 : ret; + } } return ret; } diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 3dcc5e959..9832f6120 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -463,7 +463,7 @@ void CVCMIServer::loadGame() boost::system::error_code error; ui8 clients; - c >> clients >> fname; //how many clients should be connected - TODO: support more than one + c >> clients >> fname; //how many clients should be connected // { // char sig[8]; @@ -508,7 +508,6 @@ void CVCMIServer::loadGame() continue; } cc = new CConnection(s,NAME); - cc->addStdVecItems(gh.gs); } gh.conns.insert(cc); }