浏览代码

Redesign loading solution

nordsoft 2 年之前
父节点
当前提交
dfaf778d16

+ 2 - 2
client/Client.cpp

@@ -182,8 +182,8 @@ void CClient::newGame(CGameState * initializedGameState)
 	logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff());
 	logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff());
 	if(!initializedGameState)
 	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());
 	logNetwork->trace("Initializing GameState (together): %d ms", CSH->th->getDiff());
 
 

+ 2 - 0
client/NetPacksLobbyClient.cpp

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

+ 51 - 0
lib/LoadProgress.cpp

@@ -18,6 +18,11 @@ Progress::Progress(): _progress(std::numeric_limits<Type>::min())
 	setupSteps(100);
 	setupSteps(100);
 }
 }
 
 
+Progress::Progress(int steps): _progress(std::numeric_limits<Type>::min())
+{
+	setupSteps(steps);
+}
+
 Type Progress::get() const
 Type Progress::get() const
 {
 {
 	if(_step >= _maxSteps)
 	if(_step >= _maxSteps)
@@ -82,3 +87,49 @@ void Progress::step(int count)
 		_step += 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);
+}

+ 24 - 1
lib/LoadProgress.h

@@ -18,6 +18,8 @@ namespace Load
 
 
 using Type = unsigned char;
 using Type = unsigned char;
 
 
+class ProgressAccumulator;
+
 /*
 /*
  * Purpose of that class is to track progress of computations
  * Purpose of that class is to track progress of computations
  * Derive from this class if you want to translate user or system
  * Derive from this class if you want to translate user or system
@@ -29,8 +31,9 @@ class DLL_LINKAGE Progress
 public:
 public:
 	
 	
 	//Sets current state to 0.
 	//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();
+	Progress(int steps);
 	virtual ~Progress() = default;
 	virtual ~Progress() = default;
 
 
 	//Returns current state of the progress
 	//Returns current state of the progress
@@ -67,5 +70,25 @@ public:
 private:
 private:
 	std::atomic<Type> _progress, _target;
 	std::atomic<Type> _progress, _target;
 	std::atomic<int> _step, _maxSteps;
 	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;
+};
+
 }
 }

+ 4 - 4
lib/gameState/CGameState.cpp

@@ -404,7 +404,7 @@ void CGameState::preInit(Services * services)
 	this->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();
 	preInitAuto();
 	logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed);
 	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())
 	if(scenarioOps->createRandomMap())
 	{
 	{
@@ -544,10 +544,10 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
 
 
 		// Gen map
 		// Gen map
 		CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
 		CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
-		progressTracking = &mapGenerator;
+		progressTracking.include(mapGenerator);
 
 
 		std::unique_ptr<CMap> randomMap = mapGenerator.generate();
 		std::unique_ptr<CMap> randomMap = mapGenerator.generate();
-		progressTracking = nullptr;
+		progressTracking.exclude(mapGenerator);
 
 
 		if(allowSavingRandomMap)
 		if(allowSavingRandomMap)
 		{
 		{

+ 2 - 2
lib/gameState/CGameState.h

@@ -90,7 +90,7 @@ public:
 
 
 	void preInit(Services * services);
 	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);
 	void updateOnLoad(StartInfo * si);
 
 
 	ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
 	ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
@@ -167,7 +167,7 @@ public:
 private:
 private:
 	// ----- initialization -----
 	// ----- initialization -----
 	void preInitAuto();
 	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 checkMapChecksum();
 	void initGlobalBonuses();
 	void initGlobalBonuses();
 	void initGrailPosition();
 	void initGrailPosition();

+ 1 - 1
server/CGameHandler.cpp

@@ -573,7 +573,7 @@ void CGameHandler::reinitScripting()
 #endif
 #endif
 }
 }
 
 
-void CGameHandler::init(StartInfo *si, Load::Progress *& progressTracking)
+void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTracking)
 {
 {
 	if (si->seedToBeUsed == 0)
 	if (si->seedToBeUsed == 0)
 	{
 	{

+ 1 - 1
server/CGameHandler.h

@@ -199,7 +199,7 @@ public:
 	void expGiven(const CGHeroInstance *hero); //triggers needed level-ups, handles also commander of this hero
 	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 handleClientDisconnection(std::shared_ptr<CConnection> c);
 	void handleReceivedPack(CPackForServer * pack);
 	void handleReceivedPack(CPackForServer * pack);
 	PlayerColor getPlayerAt(std::shared_ptr<CConnection> c) const;
 	PlayerColor getPlayerAt(std::shared_ptr<CConnection> c) const;

+ 17 - 18
server/CVCMIServer.cpp

@@ -279,23 +279,20 @@ void CVCMIServer::prepareToRestart()
 
 
 bool CVCMIServer::prepareToStartGame()
 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);
 	gh = std::make_shared<CGameHandler>(this);
 	switch(si->mode)
 	switch(si->mode)
@@ -315,7 +312,11 @@ bool CVCMIServer::prepareToStartGame()
 	case StartInfo::LOAD_GAME:
 	case StartInfo::LOAD_GAME:
 		logNetwork->info("Preparing to start loaded game");
 		logNetwork->info("Preparing to start loaded game");
 		if(!gh->load(si->mapname))
 		if(!gh->load(si->mapname))
+		{
+			current.finish();
+			progressTrackingThread.join();
 			return false;
 			return false;
+		}
 		break;
 		break;
 	default:
 	default:
 		logNetwork->error("Wrong mode in StartInfo!");
 		logNetwork->error("Wrong mode in StartInfo!");
@@ -323,11 +324,9 @@ bool CVCMIServer::prepareToStartGame()
 		break;
 		break;
 	}
 	}
 	
 	
-	progressTrackingFinished = true;
-	while(progressTrackingFinished)
-		continue;
+	current.finish();
+	progressTrackingThread.join();
 	
 	
-	state = EServerState::GAMEPLAY_STARTING;
 	return true;
 	return true;
 }
 }
 
 

+ 1 - 3
server/NetPacksLobbyServer.cpp

@@ -289,9 +289,6 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 		result = false;
 		result = false;
 		return;
 		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
 	// Server will prepare gamestate and we announce StartInfo to clients
 	if(!srv.prepareToStartGame())
 	if(!srv.prepareToStartGame())
@@ -303,6 +300,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getStartInfo(true));
 	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getStartInfo(true));
 	pack.initializedGameState = srv.gh->gameState();
 	pack.initializedGameState = srv.gh->gameState();
 
 
+	srv.state = EServerState::GAMEPLAY_STARTING;
 	result = true;
 	result = true;
 }
 }