1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Add system message about mods incompatibility

This commit is contained in:
nordsoft 2022-09-23 15:02:19 +04:00
parent 2b0f02c832
commit e74890c4b1
11 changed files with 95 additions and 24 deletions

View File

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

View File

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

View File

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

View File

@ -276,11 +276,34 @@ class DLL_LINKAGE CModHandler
void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods); void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods);
public: public:
class Incompatibility: public std::logic_error class DLL_LINKAGE Incompatibility: public std::logic_error
{ {
public: 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; CIdentifierStorage identifiers;
@ -366,7 +389,6 @@ public:
{ {
h & activeMods; h & activeMods;
for(const auto & m : activeMods) for(const auto & m : activeMods)
h & allMods[m].version; h & allMods[m].version;
} }
else else
@ -374,22 +396,22 @@ public:
loadMods(); loadMods();
std::vector<TModID> newActiveMods; std::vector<TModID> newActiveMods;
h & newActiveMods; h & newActiveMods;
Incompatibility::ModList missingMods;
for(auto & m : newActiveMods) for(auto & m : newActiveMods)
{ {
if(!allMods.count(m))
throw Incompatibility(m + " unkown mod");
CModInfo::Version mver; CModInfo::Version mver;
h & mver; h & mver;
if(!allMods[m].version.isNull() && !mver.isNull() && !allMods[m].version.compatible(mver))
{ if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver)))
std::string err = allMods[m].name + allMods[m].enabled = true;
": version needed " + mver.toString() + else
"but you have installed " + allMods[m].version.toString(); missingMods.emplace_back(m, mver.toString());
throw Incompatibility(err);
}
allMods[m].enabled = true;
} }
if(!missingMods.empty())
throw Incompatibility(std::move(missingMods));
std::swap(activeMods, newActiveMods); std::swap(activeMods, newActiveMods);
} }

View File

@ -309,3 +309,15 @@ struct LobbyForceSetPlayer : public CLobbyPackToServer
h & targetPlayerColor; 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;
}
};

View File

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

View File

@ -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); logGlobal->info("Loading from %s", filename);
const auto stem = FileInfo::GetPathStem(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!"); 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()); 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->preInit(VLC);
gs->updateOnLoad(lobby->si.get()); gs->updateOnLoad(lobby->si.get());
return true;
} }
bool CGameHandler::bulkSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner, si32 howMany) bool CGameHandler::bulkSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner, si32 howMany)

View File

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

View File

@ -223,7 +223,7 @@ void CVCMIServer::threadAnnounceLobby()
} }
} }
void CVCMIServer::prepareToStartGame() bool CVCMIServer::prepareToStartGame()
{ {
if(state == EServerState::GAMEPLAY) if(state == EServerState::GAMEPLAY)
{ {
@ -234,8 +234,9 @@ void CVCMIServer::prepareToStartGame()
// FIXME: dirry hack to make sure old CGameHandler::run is finished // FIXME: dirry hack to make sure old CGameHandler::run is finished
boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); 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) switch(si->mode)
{ {
case StartInfo::CAMPAIGN: case StartInfo::CAMPAIGN:
@ -252,13 +253,17 @@ void CVCMIServer::prepareToStartGame()
case StartInfo::LOAD_GAME: case StartInfo::LOAD_GAME:
logNetwork->info("Preparing to start loaded game"); logNetwork->info("Preparing to start loaded game");
gh->load(si->mapname); if(!gh->load(si->mapname))
return false;
break; break;
default: default:
logNetwork->error("Wrong mode in StartInfo!"); logNetwork->error("Wrong mode in StartInfo!");
assert(0); assert(0);
break; break;
} }
state = EServerState::GAMEPLAY_STARTING;
return true;
} }
void CVCMIServer::startGameImmidiately() 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()); 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) void CVCMIServer::announceTxt(const std::string & txt, const std::string & playerName)
{ {
logNetwork->info("%s says: %s", playerName, txt); logNetwork->info("%s says: %s", playerName, txt);

View File

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

View File

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