1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Redesign loading solution

This commit is contained in:
nordsoft 2023-08-22 20:10:20 +04:00
parent d9a2a7bfd0
commit dfaf778d16
10 changed files with 105 additions and 32 deletions

View File

@ -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());

View File

@ -133,6 +133,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack
if(auto w = GH.windows().topWindow<CLoadingScreen>())
{
w->finish();
w->tick(0);
w->redraw();
}
else
@ -144,6 +145,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyLoadProgress(LobbyLoadProgress
if(auto w = GH.windows().topWindow<CLoadingScreen>())
{
w->set(pack.progress);
w->tick(0);
w->redraw();
}
else

View File

@ -18,6 +18,11 @@ Progress::Progress(): _progress(std::numeric_limits<Type>::min())
setupSteps(100);
}
Progress::Progress(int steps): _progress(std::numeric_limits<Type>::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<boost::mutex> guard(_mx);
_progress.emplace_back(p);
}
void ProgressAccumulator::exclude(const Progress & p)
{
boost::unique_lock<boost::mutex> guard(_mx);
for(auto i = _progress.begin(); i != _progress.end(); ++i)
{
if(&i->get() == &p)
{
_accumulated += static_cast<long long>(p.get()) * p._maxSteps;
_steps += p._maxSteps;
_progress.erase(i);
return;
}
}
}
bool ProgressAccumulator::finished() const
{
boost::unique_lock<boost::mutex> guard(_mx);
for(auto i : _progress)
if(!i.get().finished())
return false;
return true;
}
Type ProgressAccumulator::get() const
{
boost::unique_lock<boost::mutex> guard(_mx);
auto sum = _accumulated;
auto totalSteps = _steps;
for(auto p : _progress)
{
sum += static_cast<long long>(p.get().get()) * p.get()._maxSteps;
totalSteps += p.get()._maxSteps;
}
if(totalSteps)
sum /= totalSteps;
return static_cast<Type>(sum);
}

View File

@ -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<Type> _progress, _target;
std::atomic<int> _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<std::reference_wrapper<const Progress>> _progress;
};
}

View File

@ -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<CMap> randomMap = mapGenerator.generate();
progressTracking = nullptr;
progressTracking.exclude(mapGenerator);
if(allowSavingRandomMap)
{

View File

@ -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<StartInfo> 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();

View File

@ -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)
{

View File

@ -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<CConnection> c);
void handleReceivedPack(CPackForServer * pack);
PlayerColor getPlayerAt(std::shared_ptr<CConnection> c) const;

View File

@ -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<boost::recursive_mutex> queueLock(mx);
std::unique_ptr<LobbyLoadProgress> loadProgress(new LobbyLoadProgress);
loadProgress->progress = progressTracking->get();
addToAnnounceQueue(std::move(loadProgress));
}
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
std::unique_ptr<LobbyLoadProgress> 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<CGameHandler>(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;
}

View File

@ -289,9 +289,6 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
result = false;
return;
}
// Announce loading
std::unique_ptr<LobbyLoadProgress> 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<StartInfo>(*srv.gh->getStartInfo(true));
pack.initializedGameState = srv.gh->gameState();
srv.state = EServerState::GAMEPLAY_STARTING;
result = true;
}