From e74890c4b17ec578a6450416f7854288d595b1ab Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 23 Sep 2022 15:02:19 +0400 Subject: [PATCH] Add system message about mods incompatibility --- client/CServerHandler.cpp | 5 +++ client/CServerHandler.h | 1 + client/NetPacksLobbyClient.cpp | 6 ++++ lib/CModHandler.h | 52 ++++++++++++++++++++++--------- lib/NetPacksLobby.h | 12 +++++++ lib/registerTypes/RegisterTypes.h | 1 + server/CGameHandler.cpp | 12 +++++-- server/CGameHandler.h | 2 +- server/CVCMIServer.cpp | 21 ++++++++++--- server/CVCMIServer.h | 3 +- server/NetPacksLobbyServer.cpp | 4 ++- 11 files changed, 95 insertions(+), 24 deletions(-) diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 46495145c..fb70c11dc 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -546,6 +546,11 @@ void CServerHandler::startCampaignScenario(std::shared_ptr cs) SDL_PushEvent(&event); } +void CServerHandler::showServerError(std::string txt) +{ + CInfoWindow::showInfoDialog(txt, {}); +} + int CServerHandler::howManyPlayerInterfaces() { int playerInts = 0; diff --git a/client/CServerHandler.h b/client/CServerHandler.h index 03ac005ed..3f9568c8c 100644 --- a/client/CServerHandler.h +++ b/client/CServerHandler.h @@ -137,6 +137,7 @@ public: void startGameplay(); void endGameplay(bool closeConnection = true, bool restart = false); void startCampaignScenario(std::shared_ptr 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(); diff --git a/client/NetPacksLobbyClient.cpp b/client/NetPacksLobbyClient.cpp index be8966916..88df3c774 100644 --- a/client/NetPacksLobbyClient.cpp +++ b/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); +} diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 1605f59fd..8b1f5ad02 100644 --- a/lib/CModHandler.h +++ b/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; + using ModList = std::list; + + 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 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); } diff --git a/lib/NetPacksLobby.h b/lib/NetPacksLobby.h index f635d3fa0..18e9a5a5c 100644 --- a/lib/NetPacksLobby.h +++ b/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 void serialize(Handler &h, const int version) + { + h & message; + } +}; diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 17e46c54c..b0cf4aecf 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -374,6 +374,7 @@ void registerTypesLobbyPacks(Serializer &s) s.template registerType(); // Only server send s.template registerType(); + s.template registerType(); // For client with permissions s.template registerType(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 0604c48e8..8e9641135 100644 --- a/server/CGameHandler.cpp +++ b/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) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 279fca93e..086fdeef2 100644 --- a/server/CGameHandler.h +++ b/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); diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 5cef5f553..ce5e0623c 100644 --- a/server/CVCMIServer.cpp +++ b/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(this); + + if(!gh) + gh = std::make_shared(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 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(); + 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); diff --git a/server/CVCMIServer.h b/server/CVCMIServer.h index 111ded751..a441863d9 100644 --- a/server/CVCMIServer.h +++ b/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 pack); void setPlayerConnectedId(PlayerSettings & pset, ui8 player) const; diff --git a/server/NetPacksLobbyServer.cpp b/server/NetPacksLobbyServer.cpp index e6ba261f5..5885630fd 100644 --- a/server/NetPacksLobbyServer.cpp +++ b/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(*srv->gh->getStartInfo(true)); return true; }