From f7f7fe1d328783fcd1da87bf9b79fbf65dc08c15 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Sun, 12 Mar 2017 10:54:24 +0300 Subject: [PATCH] Client: fix headless mode and add automated testing mode Command-line option --noGUI replaced with --headless. Added option --testmap that will run specified map with AI players --- client/CMT.cpp | 59 ++++++++++++++++++++++++++++----------- client/Client.cpp | 16 +++++++---- client/NetPacksClient.cpp | 41 +++++++++++++++------------ 3 files changed, 76 insertions(+), 40 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index ece9d6ed5..57532744f 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -91,7 +91,6 @@ SDL_Surface *screen = nullptr, //main screen surface std::queue events; boost::mutex eventsM; -bool gNoGUI = false; CondSh serverAlive(false); static po::variables_map vm; @@ -114,6 +113,29 @@ void endGame(); #include #endif +void startTestMap(const std::string &mapname) +{ + StartInfo si; + si.mapname = mapname; + si.mode = StartInfo::NEW_GAME; + for (int i = 0; i < 8; i++) + { + PlayerSettings &pset = si.playerInfos[PlayerColor(i)]; + pset.color = PlayerColor(i); + pset.name = CGI->generaltexth->allTexts[468];//Computer + pset.playerID = i; + pset.compOnly = true; + pset.castle = 0; + pset.hero = -1; + pset.heroPortrait = -1; + pset.handicap = PlayerSettings::NO_HANDICAP; + } + + while(GH.topInt()) + GH.popIntTotally(GH.topInt()); + startGame(&si); +} + void startGameFromFile(const bfs::path &fname) { StartInfo si; @@ -150,7 +172,7 @@ void init() logGlobal->infoStream()<<"Initializing VCMI_Lib: "<curh = new CCursorHandler; @@ -240,8 +262,9 @@ int main(int argc, char** argv) ("version,v", "display version information and exit") ("battle,b", po::value(), "runs game in duel mode (battle-only") ("start", po::value(), "starts game from saved StartInfo file") + ("testmap", po::value(), "") ("onlyAI", "runs without human player, all players will be default AI") - ("noGUI", "runs without GUI, implies --onlyAI") + ("headless", "runs without GUI, implies --onlyAI") ("ai", po::value>(), "AI to be used for the player, can be specified several times for the consecutive players") ("oneGoodAI", "puts one default AI and the rest will be EmptyAI") ("autoSkip", "automatically skip turns in GUI") @@ -281,11 +304,6 @@ int main(int argc, char** argv) prog_version(); return 0; } - if(vm.count("noGUI")) - { - gNoGUI = true; - vm.insert(std::pair("onlyAI", po::variable_value())); - } if(vm.count("donotstartserver")) { CServerHandler::DO_NOT_START_SERVER = true; @@ -314,9 +332,14 @@ int main(int argc, char** argv) // Init filesystem and settings preinitDLL(::console); settings.init(); + Settings session = settings.write["session"]; + if(vm.count("headless")) + { + session["headless"].Bool() = true; + vm.insert(std::pair("onlyAI", po::variable_value())); + } // Init special testing settings - Settings session = settings.write["session"]; session["serverport"].Integer() = vm.count("serverport") ? vm["serverport"].as() : 0; session["saveprefix"].String() = vm.count("saveprefix") ? vm["saveprefix"].as() : ""; session["savefrequency"].Integer() = vm.count("savefrequency") ? vm["savefrequency"].as() : 1; @@ -364,7 +387,7 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } - if(!gNoGUI) + if(!settings["session"]["headless"].Bool()) { if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE)) { @@ -427,7 +450,7 @@ int main(int argc, char** argv) #ifdef DISABLE_VIDEO CCS->videoh = new CEmptyVideoPlayer; #else - if (!gNoGUI && !vm.count("disable-video")) + if (!settings["session"]["headless"].Bool() && !vm.count("disable-video")) CCS->videoh = new CVideoPlayer; else CCS->videoh = new CEmptyVideoPlayer; @@ -456,7 +479,7 @@ int main(int argc, char** argv) init(); #endif - if(!gNoGUI ) + if(!settings["session"]["headless"].Bool()) { if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool()) playIntro(); @@ -480,7 +503,6 @@ int main(int argc, char** argv) if(!vm.count("battle")) { - Settings session = settings.write["session"]; session["autoSkip"].Bool() = vm.count("autoSkip"); session["oneGoodAI"].Bool() = vm.count("oneGoodAI"); session["aiSolo"].Bool() = false; @@ -488,8 +510,13 @@ int main(int argc, char** argv) bfs::path fileToStartFrom; //none by default if(vm.count("start")) fileToStartFrom = vm["start"].as(); + std::string testmap; + if(vm.count("testmap")) + testmap = vm["testmap"].as(); - if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom)) + if(!testmap.empty()) + startTestMap(testmap); + else if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom)) startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file else { @@ -511,7 +538,7 @@ int main(int argc, char** argv) startGame(si); } - if(!gNoGUI) + if(!settings["session"]["headless"].Bool()) { mainLoop(); } @@ -1327,7 +1354,7 @@ void handleQuit(bool ask/* = true*/) dispose(); vstd::clear_pointer(console); boost::this_thread::sleep(boost::posix_time::milliseconds(750)); - if(!gNoGUI) + if(!settings["session"]["headless"].Bool()) { cleanupRenderer(); SDL_Quit(); diff --git a/client/Client.cpp b/client/Client.cpp index f00c05eb1..75afa739d 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -432,10 +432,13 @@ void CClient::newGame( CConnection *con, StartInfo *si ) // Init map handler if(gs->map) { - const_cast(CGI)->mh = new CMapHandler(); - CGI->mh->map = gs->map; - logNetwork->infoStream() << "Creating mapHandler: " << tmh.getDiff(); - CGI->mh->init(); + if(!settings["session"]["headless"].Bool()) + { + const_cast(CGI)->mh = new CMapHandler(); + CGI->mh->map = gs->map; + logNetwork->infoStream() << "Creating mapHandler: " << tmh.getDiff(); + CGI->mh->init(); + } pathInfo = make_unique(getMapSize()); logNetwork->infoStream() << "Initializing mapHandler (together): " << tmh.getDiff(); } @@ -472,7 +475,7 @@ void CClient::newGame( CConnection *con, StartInfo *si ) if(si->mode == StartInfo::DUEL) { - if(!gNoGUI) + if(!settings["session"]["headless"].Bool()) { boost::unique_lock un(*CPlayerInterface::pim); auto p = std::make_shared(PlayerColor::NEUTRAL); @@ -744,7 +747,8 @@ void CClient::battleStarted(const BattleInfo * info) def = std::dynamic_pointer_cast( playerint[rightSide.color] ); } - if(!gNoGUI && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL)) + if(!settings["session"]["headless"].Bool() + && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL)) { boost::unique_lock un(*CPlayerInterface::pim); auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index fac4a627d..1a9949389 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -283,13 +283,13 @@ void GiveBonus::applyCl(CClient *cl) void ChangeObjPos::applyFirstCl(CClient *cl) { CGObjectInstance *obj = GS(cl)->getObjInstance(objid); - if(flags & 1) + if(flags & 1 && CGI->mh) CGI->mh->hideObject(obj); } void ChangeObjPos::applyCl(CClient *cl) { CGObjectInstance *obj = GS(cl)->getObjInstance(objid); - if(flags & 1) + if(flags & 1 && CGI->mh) CGI->mh->printObject(obj); cl->invalidatePaths(); @@ -335,7 +335,8 @@ void RemoveObject::applyFirstCl(CClient *cl) { const CGObjectInstance *o = cl->getObj(id); - CGI->mh->hideObject(o, true); + if(CGI->mh) + CGI->mh->hideObject(o, true); //notify interfaces about removal for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++) @@ -365,10 +366,12 @@ void TryMoveHero::applyFirstCl(CClient *cl) humanKnows = true; } + if(!CGI->mh) + return; + if(result == TELEPORTATION || result == EMBARK || result == DISEMBARK || !humanKnows) CGI->mh->hideObject(h, result == EMBARK && humanKnows); - if(result == DISEMBARK) CGI->mh->printObject(h->boat); } @@ -378,13 +381,14 @@ void TryMoveHero::applyCl(CClient *cl) const CGHeroInstance *h = cl->getHero(id); cl->invalidatePaths(); - if(result == TELEPORTATION || result == EMBARK || result == DISEMBARK) + if(CGI->mh) { - CGI->mh->printObject(h, result == DISEMBARK); - } + if(result == TELEPORTATION || result == EMBARK || result == DISEMBARK) + CGI->mh->printObject(h, result == DISEMBARK); - if(result == EMBARK) - CGI->mh->hideObject(h->boat); + if(result == EMBARK) + CGI->mh->hideObject(h->boat); + } PlayerColor player = h->tempOwner; @@ -403,10 +407,10 @@ void TryMoveHero::applyCl(CClient *cl) } } - if(!humanKnows) //maphandler didn't get update from playerint, do it now - { //TODO: restructure nicely + //maphandler didn't get update from playerint, do it now + //TODO: restructure nicely + if(!humanKnows && CGI->mh) CGI->mh->printObject(h); - } } void NewStructures::applyCl(CClient *cl) @@ -482,22 +486,22 @@ void HeroRecruited::applyCl(CClient *cl) needsPrinting = false; } } - if (needsPrinting) - { + if(needsPrinting && CGI->mh) CGI->mh->printObject(h); - } } void GiveHero::applyCl(CClient *cl) { CGHeroInstance *h = GS(cl)->getHero(id); - CGI->mh->printObject(h); + if(CGI->mh) + CGI->mh->printObject(h); cl->playerint[h->tempOwner]->heroCreated(h); } void GiveHero::applyFirstCl(CClient *cl) { - CGI->mh->hideObject(GS(cl)->getHero(id)); + if(CGI->mh) + CGI->mh->hideObject(GS(cl)->getHero(id)); } void InfoWindow::applyCl(CClient *cl) @@ -904,7 +908,8 @@ void NewObject::applyCl(CClient *cl) cl->invalidatePaths(); const CGObjectInstance *obj = cl->getObj(id); - CGI->mh->printObject(obj, true); + if(CGI->mh) + CGI->mh->printObject(obj, true); for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++) {