Pārlūkot izejas kodu

Add system message about mods incompatibility

nordsoft 3 gadi atpakaļ
vecāks
revīzija
e74890c4b1

+ 5 - 0
client/CServerHandler.cpp

@@ -546,6 +546,11 @@ void CServerHandler::startCampaignScenario(std::shared_ptr<CCampaignState> cs)
 	SDL_PushEvent(&event);
 }
 
+void CServerHandler::showServerError(std::string txt)
+{
+	CInfoWindow::showInfoDialog(txt, {});
+}
+
 int CServerHandler::howManyPlayerInterfaces()
 {
 	int playerInts = 0;

+ 1 - 0
client/CServerHandler.h

@@ -137,6 +137,7 @@ public:
 	void startGameplay();
 	void endGameplay(bool closeConnection = true, bool restart = false);
 	void startCampaignScenario(std::shared_ptr<CCampaignState> cs = {});
+	void showServerError(std::string txt);
 
 	// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle
 	int howManyPlayerInterfaces();

+ 6 - 0
client/NetPacksLobbyClient.cpp

@@ -137,3 +137,9 @@ void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler *
 	if(hostChanged)
 		lobby->toggleMode(handler->isHost());
 }
+
+void LobbyShowMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+{
+	lobby->buttonStart->block(false);
+	handler->showServerError(message);
+}

+ 37 - 15
lib/CModHandler.h

@@ -276,11 +276,34 @@ class DLL_LINKAGE CModHandler
 	void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods);
 public:
 	
-	class Incompatibility: public std::logic_error
+	class DLL_LINKAGE Incompatibility: public std::logic_error
 	{
 	public:
-		Incompatibility(const std::string & w): std::logic_error(w)
-		{}
+		using StringPair = std::pair<const std::string, const std::string>;
+		using ModList = std::list<StringPair>;
+		
+		Incompatibility(ModList && _missingMods):
+			std::logic_error("Mods are required to load game"),
+			missingMods(std::move(_missingMods))
+		{
+			std::ostringstream _ss;
+			_ss << std::logic_error::what() << std::endl;
+			for(auto & m : missingMods)
+				_ss << m.first << ' ' << m.second << std::endl;
+			message = _ss.str();
+		}
+		
+		const char * what() const noexcept override
+		{
+			return message.c_str();
+		}
+		
+	private:
+		//list of mods required to load the game
+		// first: mod name
+		// second: mod version
+		const ModList missingMods;
+		std::string message;
 	};
 
 	CIdentifierStorage identifiers;
@@ -366,7 +389,6 @@ public:
 		{
 			h & activeMods;
 			for(const auto & m : activeMods)
-
 				h & allMods[m].version;
 		}
 		else
@@ -374,22 +396,22 @@ public:
 			loadMods();
 			std::vector<TModID> newActiveMods;
 			h & newActiveMods;
+			
+			Incompatibility::ModList missingMods;
 			for(auto & m : newActiveMods)
 			{
-				if(!allMods.count(m))
-					throw Incompatibility(m + " unkown mod");
-				
 				CModInfo::Version mver;
 				h & mver;
-				if(!allMods[m].version.isNull() && !mver.isNull() && !allMods[m].version.compatible(mver))
-				{
-					std::string err = allMods[m].name +
-					": version needed " + mver.toString() +
-					"but you have installed " + allMods[m].version.toString();
-					throw Incompatibility(err);
-				}
-				allMods[m].enabled = true;
+				
+				if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver)))
+					allMods[m].enabled = true;
+				else
+					missingMods.emplace_back(m, mver.toString());
 			}
+			
+			if(!missingMods.empty())
+				throw Incompatibility(std::move(missingMods));
+			
 			std::swap(activeMods, newActiveMods);
 		}
 				

+ 12 - 0
lib/NetPacksLobby.h

@@ -309,3 +309,15 @@ struct LobbyForceSetPlayer : public CLobbyPackToServer
 		h & targetPlayerColor;
 	}
 };
+
+struct LobbyShowMessage : public CLobbyPackToPropagate
+{
+	std::string message;
+	
+	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & message;
+	}
+};

+ 1 - 0
lib/registerTypes/RegisterTypes.h

@@ -374,6 +374,7 @@ void registerTypesLobbyPacks(Serializer &s)
 	s.template registerType<CLobbyPackToPropagate, LobbyChangeHost>();
 	// Only server send
 	s.template registerType<CLobbyPackToPropagate, LobbyUpdateState>();
+	s.template registerType<CLobbyPackToPropagate, LobbyShowMessage>();
 
 	// For client with permissions
 	s.template registerType<CLobbyPackToServer, LobbyChangePlayerOption>();

+ 10 - 2
server/CGameHandler.cpp

@@ -2967,7 +2967,7 @@ void CGameHandler::save(const std::string & filename)
 	}
 }
 
-void CGameHandler::load(const std::string & filename)
+bool CGameHandler::load(const std::string & filename)
 {
 	logGlobal->info("Loading from %s", filename);
 	const auto stem	= FileInfo::GetPathStem(filename);
@@ -2984,12 +2984,20 @@ void CGameHandler::load(const std::string & filename)
 		}
 		logGlobal->info("Game has been successfully loaded!");
 	}
-	catch(std::exception &e)
+	catch(const CModHandler::Incompatibility & e)
 	{
 		logGlobal->error("Failed to load game: %s", e.what());
+		lobby->announceMessage(e.what());
+		return false;
+	}
+	catch(const std::exception & e)
+	{
+		logGlobal->error("Failed to load game: %s", e.what());
+		return false;
 	}
 	gs->preInit(VLC);
 	gs->updateOnLoad(lobby->si.get());
+	return true;
 }
 
 bool CGameHandler::bulkSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner, si32 howMany)

+ 1 - 1
server/CGameHandler.h

@@ -257,7 +257,7 @@ public:
 	bool bulkMergeStacks(SlotID slotSrc, ObjectInstanceID srcOwner);
 	bool bulkSmartSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner);
 	void save(const std::string &fname);
-	void load(const std::string &fname);
+	bool load(const std::string &fname);
 
 	void handleTimeEvents();
 	void handleTownEvents(CGTownInstance *town, NewTurn &n);

+ 17 - 4
server/CVCMIServer.cpp

@@ -223,7 +223,7 @@ void CVCMIServer::threadAnnounceLobby()
 	}
 }
 
-void CVCMIServer::prepareToStartGame()
+bool CVCMIServer::prepareToStartGame()
 {
 	if(state == EServerState::GAMEPLAY)
 	{
@@ -234,8 +234,9 @@ void CVCMIServer::prepareToStartGame()
 		// FIXME: dirry hack to make sure old CGameHandler::run is finished
 		boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
 	}
-	state = EServerState::GAMEPLAY_STARTING;
-	gh = std::make_shared<CGameHandler>(this);
+
+	if(!gh)
+		gh = std::make_shared<CGameHandler>(this);
 	switch(si->mode)
 	{
 	case StartInfo::CAMPAIGN:
@@ -252,13 +253,17 @@ void CVCMIServer::prepareToStartGame()
 
 	case StartInfo::LOAD_GAME:
 		logNetwork->info("Preparing to start loaded game");
-		gh->load(si->mapname);
+		if(!gh->load(si->mapname))
+			return false;
 		break;
 	default:
 		logNetwork->error("Wrong mode in StartInfo!");
 		assert(0);
 		break;
 	}
+	
+	state = EServerState::GAMEPLAY_STARTING;
+	return true;
 }
 
 void CVCMIServer::startGameImmidiately()
@@ -384,6 +389,14 @@ void CVCMIServer::announcePack(std::unique_ptr<CPackForLobby> pack)
 	applier->getApplier(typeList.getTypeID(pack.get()))->applyOnServerAfter(this, pack.get());
 }
 
+void CVCMIServer::announceMessage(const std::string & txt)
+{
+	logNetwork->info("Show message: %s", txt);
+	auto cm = vstd::make_unique<LobbyShowMessage>();
+	cm->message = txt;
+	addToAnnounceQueue(std::move(cm));
+}
+
 void CVCMIServer::announceTxt(const std::string & txt, const std::string & playerName)
 {
 	logNetwork->info("%s says: %s", playerName, txt);

+ 2 - 1
server/CVCMIServer.h

@@ -63,7 +63,7 @@ public:
 	CVCMIServer(boost::program_options::variables_map & opts);
 	~CVCMIServer();
 	void run();
-	void prepareToStartGame();
+	bool prepareToStartGame();
 	void startGameImmidiately();
 
 	void startAsyncAccept();
@@ -76,6 +76,7 @@ public:
 	bool passHost(int toConnectionId);
 
 	void announceTxt(const std::string & txt, const std::string & playerName = "system");
+	void announceMessage(const std::string & txt);
 	void addToAnnounceQueue(std::unique_ptr<CPackForLobby> pack);
 
 	void setPlayerConnectedId(PlayerSettings & pset, ui8 player) const;

+ 3 - 1
server/NetPacksLobbyServer.cpp

@@ -177,7 +177,9 @@ bool LobbyStartGame::applyOnServer(CVCMIServer * srv)
 		return false;
 	}
 	// Server will prepare gamestate and we announce StartInfo to clients
-	srv->prepareToStartGame();
+	if(!srv->prepareToStartGame())
+		return false;
+	
 	initializedStartInfo = std::make_shared<StartInfo>(*srv->gh->getStartInfo(true));
 	return true;
 }