diff --git a/client/Client.cpp b/client/Client.cpp index ac20e1a1b..c650d3f4e 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -182,8 +182,8 @@ void CClient::newGame(CGameState * initializedGameState) logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff()); if(!initializedGameState) { - Load::Progress * progressTrackingPointer = nullptr; - gs->init(&mapService, CSH->si.get(), progressTrackingPointer, settings["general"]["saveRandomMaps"].Bool()); + Load::ProgressAccumulator progressTracking; + gs->init(&mapService, CSH->si.get(), progressTracking, settings["general"]["saveRandomMaps"].Bool()); } logNetwork->trace("Initializing GameState (together): %d ms", CSH->th->getDiff()); diff --git a/client/NetPacksLobbyClient.cpp b/client/NetPacksLobbyClient.cpp index 6dd2ef315..ab6f7e2c0 100644 --- a/client/NetPacksLobbyClient.cpp +++ b/client/NetPacksLobbyClient.cpp @@ -133,6 +133,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack if(auto w = GH.windows().topWindow()) { w->finish(); + w->tick(0); w->redraw(); } else @@ -144,6 +145,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyLoadProgress(LobbyLoadProgress if(auto w = GH.windows().topWindow()) { w->set(pack.progress); + w->tick(0); w->redraw(); } else diff --git a/lib/LoadProgress.cpp b/lib/LoadProgress.cpp index aa24a4c2e..70eadbdbb 100644 --- a/lib/LoadProgress.cpp +++ b/lib/LoadProgress.cpp @@ -18,6 +18,11 @@ Progress::Progress(): _progress(std::numeric_limits::min()) setupSteps(100); } +Progress::Progress(int steps): _progress(std::numeric_limits::min()) +{ + setupSteps(steps); +} + Type Progress::get() const { if(_step >= _maxSteps) @@ -82,3 +87,49 @@ void Progress::step(int count) _step += count; } } + +void ProgressAccumulator::include(const Progress & p) +{ + boost::unique_lock guard(_mx); + _progress.emplace_back(p); +} + +void ProgressAccumulator::exclude(const Progress & p) +{ + boost::unique_lock guard(_mx); + for(auto i = _progress.begin(); i != _progress.end(); ++i) + { + if(&i->get() == &p) + { + _accumulated += static_cast(p.get()) * p._maxSteps; + _steps += p._maxSteps; + _progress.erase(i); + return; + } + } +} + +bool ProgressAccumulator::finished() const +{ + boost::unique_lock guard(_mx); + for(auto i : _progress) + if(!i.get().finished()) + return false; + return true; +} + +Type ProgressAccumulator::get() const +{ + boost::unique_lock guard(_mx); + auto sum = _accumulated; + auto totalSteps = _steps; + for(auto p : _progress) + { + sum += static_cast(p.get().get()) * p.get()._maxSteps; + totalSteps += p.get()._maxSteps; + } + + if(totalSteps) + sum /= totalSteps; + return static_cast(sum); +} diff --git a/lib/LoadProgress.h b/lib/LoadProgress.h index 2911dab2a..1c3c21b0d 100644 --- a/lib/LoadProgress.h +++ b/lib/LoadProgress.h @@ -18,6 +18,8 @@ namespace Load using Type = unsigned char; +class ProgressAccumulator; + /* * Purpose of that class is to track progress of computations * Derive from this class if you want to translate user or system @@ -29,8 +31,9 @@ class DLL_LINKAGE Progress public: //Sets current state to 0. - //Amount of steps to finish progress will be equal to 100 + //Amount of steps to finish progress will be equal to 100 for default constructor Progress(); + Progress(int steps); virtual ~Progress() = default; //Returns current state of the progress @@ -67,5 +70,25 @@ public: private: std::atomic _progress, _target; std::atomic _step, _maxSteps; + + friend class ProgressAccumulator; }; + +class DLL_LINKAGE ProgressAccumulator +{ +public: + ProgressAccumulator() = default; + + void include(const Progress &); + void exclude(const Progress &); + + bool finished() const; + Type get() const; + +private: + mutable boost::mutex _mx; + long long _accumulated = 0, _steps = 0; + std::vector> _progress; +}; + } diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 3cea0fa07..898243378 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -404,7 +404,7 @@ void CGameState::preInit(Services * services) this->services = services; } -void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Progress *& progressTracking, bool allowSavingRandomMap) +void CGameState::init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator & progressTracking, bool allowSavingRandomMap) { preInitAuto(); logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed); @@ -535,7 +535,7 @@ void CGameState::preInitAuto() } } -void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::Progress *& progressTracking) +void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking) { if(scenarioOps->createRandomMap()) { @@ -544,10 +544,10 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan // Gen map CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed); - progressTracking = &mapGenerator; + progressTracking.include(mapGenerator); std::unique_ptr randomMap = mapGenerator.generate(); - progressTracking = nullptr; + progressTracking.exclude(mapGenerator); if(allowSavingRandomMap) { diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index 546e774ac..ca906f6ff 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -90,7 +90,7 @@ public: void preInit(Services * services); - void init(const IMapService * mapService, StartInfo * si, Load::Progress *&, bool allowSavingRandomMap = false); + void init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator &, bool allowSavingRandomMap = false); void updateOnLoad(StartInfo * si); ConstTransitivePtr scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized) @@ -167,7 +167,7 @@ public: private: // ----- initialization ----- void preInitAuto(); - void initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::Progress *& progressTracking); + void initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking); void checkMapChecksum(); void initGlobalBonuses(); void initGrailPosition(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 6f238b92c..9a4b35bdd 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -573,7 +573,7 @@ void CGameHandler::reinitScripting() #endif } -void CGameHandler::init(StartInfo *si, Load::Progress *& progressTracking) +void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTracking) { if (si->seedToBeUsed == 0) { diff --git a/server/CGameHandler.h b/server/CGameHandler.h index aacd6a5b8..f235cd430 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -199,7 +199,7 @@ public: void expGiven(const CGHeroInstance *hero); //triggers needed level-ups, handles also commander of this hero ////////////////////////////////////////////////////////////////////////// - void init(StartInfo *si, Load::Progress *& progressTracking); + void init(StartInfo *si, Load::ProgressAccumulator & progressTracking); void handleClientDisconnection(std::shared_ptr c); void handleReceivedPack(CPackForServer * pack); PlayerColor getPlayerAt(std::shared_ptr c) const; diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 0c1d255a4..d54d25050 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -279,23 +279,20 @@ void CVCMIServer::prepareToRestart() bool CVCMIServer::prepareToStartGame() { - Load::Progress * progressTracking = nullptr; - bool progressTrackingFinished = false; - std::thread progressTrackingThread([this, &progressTracking, &progressTrackingFinished](){ - while(!progressTrackingFinished) + Load::ProgressAccumulator progressTracking; + Load::Progress current(1); + progressTracking.include(current); + + auto progressTrackingThread = boost::thread([this, &progressTracking]() + { + while(!progressTracking.finished()) { - if(progressTracking) - { - boost::unique_lock queueLock(mx); - std::unique_ptr loadProgress(new LobbyLoadProgress); - loadProgress->progress = progressTracking->get(); - addToAnnounceQueue(std::move(loadProgress)); - } - boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + std::unique_ptr loadProgress(new LobbyLoadProgress); + loadProgress->progress = progressTracking.get(); + addToAnnounceQueue(std::move(loadProgress)); + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); } - progressTrackingFinished = false; }); - progressTrackingThread.detach(); gh = std::make_shared(this); switch(si->mode) @@ -315,7 +312,11 @@ bool CVCMIServer::prepareToStartGame() case StartInfo::LOAD_GAME: logNetwork->info("Preparing to start loaded game"); if(!gh->load(si->mapname)) + { + current.finish(); + progressTrackingThread.join(); return false; + } break; default: logNetwork->error("Wrong mode in StartInfo!"); @@ -323,11 +324,9 @@ bool CVCMIServer::prepareToStartGame() break; } - progressTrackingFinished = true; - while(progressTrackingFinished) - continue; + current.finish(); + progressTrackingThread.join(); - state = EServerState::GAMEPLAY_STARTING; return true; } diff --git a/server/NetPacksLobbyServer.cpp b/server/NetPacksLobbyServer.cpp index 70e485d93..ec7449e99 100644 --- a/server/NetPacksLobbyServer.cpp +++ b/server/NetPacksLobbyServer.cpp @@ -289,9 +289,6 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) result = false; return; } - // Announce loading - std::unique_ptr loadProgress(new LobbyLoadProgress); - srv.addToAnnounceQueue(std::move(loadProgress)); // Server will prepare gamestate and we announce StartInfo to clients if(!srv.prepareToStartGame()) @@ -303,6 +300,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) pack.initializedStartInfo = std::make_shared(*srv.gh->getStartInfo(true)); pack.initializedGameState = srv.gh->gameState(); + srv.state = EServerState::GAMEPLAY_STARTING; result = true; }