1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-22 03:39:45 +02:00

Implemented join room dialog

This commit is contained in:
Ivan Savenko 2024-04-16 16:30:22 +03:00
parent e5f8cefa7f
commit 1840666327
17 changed files with 372 additions and 96 deletions

View File

@ -103,10 +103,16 @@
"vcmi.lobby.room.load" : "Load Game",
"vcmi.lobby.room.type" : "Room Type",
"vcmi.lobby.room.mode" : "Game Mode",
"vcmi.lobby.room.join" : "Join Game Room",
"vcmi.lobby.room.state.public" : "Public",
"vcmi.lobby.room.state.private" : "Private",
"vcmi.lobby.room.state.busy" : "In Game",
"vcmi.lobby.room.state.invited" : "Invited",
"vcmi.lobby.mod.state.compatible" : "Compatible",
"vcmi.lobby.mod.state.disabled" : "Disabled",
"vcmi.lobby.mod.state.version" : "Version mismatch",
"vcmi.lobby.mod.state.excessive" : "Excessive",
"vcmi.lobby.mod.state.missing" : "Not installed",
"vcmi.client.errors.invalidMap" : "{Invalid map or campaign}\n\nFailed to start game! Selected map or campaign might be invalid or corrupted. Reason:\n%s",
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",

View File

@ -98,6 +98,7 @@ set(client_SRCS
globalLobby/GlobalLobbyClient.cpp
globalLobby/GlobalLobbyInviteWindow.cpp
globalLobby/GlobalLobbyLoginWindow.cpp
globalLobby/GlobalLobbyRoomWindow.cpp
globalLobby/GlobalLobbyServerSetup.cpp
globalLobby/GlobalLobbyWidget.cpp
globalLobby/GlobalLobbyWindow.cpp
@ -288,6 +289,7 @@ set(client_HEADERS
globalLobby/GlobalLobbyDefines.h
globalLobby/GlobalLobbyInviteWindow.h
globalLobby/GlobalLobbyLoginWindow.h
globalLobby/GlobalLobbyRoomWindow.h
globalLobby/GlobalLobbyServerSetup.h
globalLobby/GlobalLobbyWidget.h
globalLobby/GlobalLobbyWindow.h

View File

@ -432,6 +432,17 @@ const std::vector<GlobalLobbyRoom> & GlobalLobbyClient::getMatchesHistory() cons
return matchesHistory;
}
const GlobalLobbyRoom & GlobalLobbyClient::getActiveRoomByName(const std::string & roomUUID) const
{
for (auto const & room : activeRooms)
{
if (room.gameRoomID == roomUUID)
return room;
}
throw std::out_of_range("Failed to find room with UUID of " + roomUUID);
}
const std::vector<GlobalLobbyChannelMessage> & GlobalLobbyClient::getChannelHistory(const std::string & channelType, const std::string & channelName) const
{
static const std::vector<GlobalLobbyChannelMessage> emptyVector;

View File

@ -73,6 +73,9 @@ public:
const std::vector<GlobalLobbyRoom> & getMatchesHistory() const;
const std::vector<GlobalLobbyChannelMessage> & getChannelHistory(const std::string & channelType, const std::string & channelName) const;
/// Returns active room by ID. Throws out-of-range on failure
const GlobalLobbyRoom & getActiveRoomByName(const std::string & roomUUID) const;
const std::string & getAccountID() const;
const std::string & getAccountCookie() const;
const std::string & getAccountDisplayName() const;

View File

@ -0,0 +1,147 @@
/*
* GlobalLobbyRoomWindow.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "GlobalLobbyRoomWindow.h"
#include "GlobalLobbyClient.h"
#include "GlobalLobbyDefines.h"
#include "GlobalLobbyWindow.h"
#include "../CGameInfo.h"
#include "../CServerHandler.h"
#include "../gui/CGuiHandler.h"
#include "../mainmenu/CMainMenu.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../widgets/TextControls.h"
#include "../widgets/GraphicalPrimitiveCanvas.h"
#include "../widgets/ObjectLists.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/MetaString.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/modding/CModHandler.h"
#include "../../lib/modding/CModInfo.h"
GlobalLobbyRoomAccountCard::GlobalLobbyRoomAccountCard(const GlobalLobbyAccount & accountDescription)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos.w = 130;
pos.h = 40;
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, accountDescription.displayName);
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, accountDescription.status);
}
GlobalLobbyRoomModCard::GlobalLobbyRoomModCard(const GlobalLobbyRoomModInfo & modInfo)
{
const std::map<ModVerificationStatus, std::string> statusToString = {
{ ModVerificationStatus::NOT_INSTALLED, "missing" },
{ ModVerificationStatus::DISABLED, "disabled" },
{ ModVerificationStatus::EXCESSIVE, "excessive" },
{ ModVerificationStatus::VERSION_MISMATCH, "version" },
{ ModVerificationStatus::FULL_MATCH, "compatible" }
};
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos.w = 200;
pos.h = 40;
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, modInfo.modName);
labelVersion = std::make_shared<CLabel>(195, 30, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, modInfo.version);
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.mod.state." + statusToString.at(modInfo.status)));
}
GlobalLobbyRoomWindow::GlobalLobbyRoomWindow(GlobalLobbyWindow * window, const std::string & roomUUID)
: CWindowObject(BORDERED)
, roomUUID(roomUUID)
, window(window)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos.w = 400;
pos.h = 400;
GlobalLobbyRoom roomDescription = CSH->getGlobalLobby().getActiveRoomByName(roomUUID);
for (auto const & modEntry : ModVerificationInfo::verifyListAgainstLocalMods(roomDescription.modList))
{
GlobalLobbyRoomModInfo modInfo;
modInfo.status = modEntry.second;
modInfo.version = roomDescription.modList.at(modEntry.first).version.toString();
if (modEntry.second == ModVerificationStatus::NOT_INSTALLED)
modInfo.modName = roomDescription.modList.at(modEntry.first).name;
else
modInfo.modName = CGI->modh->getModInfo(modEntry.first).getVerificationInfo().name;
modVerificationList.push_back(modInfo);
}
bool gameStarted = roomDescription.statusID != "public" && roomDescription.statusID != "private";
bool publicRoom = roomDescription.statusID == "public";
bool hasInvite = CSH->getGlobalLobby().isInvitedToRoom(roomDescription.gameRoomID);
bool canJoinRoom = (publicRoom || hasInvite) && !gameStarted && !CSH->inGame();
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.room.join"));
labelSubtitle = std::make_shared<CLabel>( pos.w / 2, 40, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, "Game room hosted by AccountName");
labelDescription = std::make_shared<CLabel>( 20, 60, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::YELLOW, roomDescription.description);
labelVersion = std::make_shared<CLabel>( 20, 80, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::YELLOW, "Game version: " + roomDescription.gameVersion);
buttonJoin = std::make_shared<CButton>(Point(10, 360), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onJoin(); });
buttonClose = std::make_shared<CButton>(Point(100, 360), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); });
if (canJoinRoom)
labelJoinStatus = std::make_shared<CLabel>( 20, 300, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::YELLOW, "Join Room?");
else
labelJoinStatus = std::make_shared<CLabel>( 20, 300, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::YELLOW, "Unable to join room!");
const auto & createAccountCardCallback = [participants = roomDescription.participants](size_t index) -> std::shared_ptr<CIntObject>
{
if(index < participants.size())
return std::make_shared<GlobalLobbyRoomAccountCard>(participants[index]);
return std::make_shared<CIntObject>();
};
accountListBackground = std::make_shared<TransparentFilledRectangle>(Rect(8, 98, 150, 180), ColorRGBA(0, 0, 0, 64), ColorRGBA(64, 80, 128, 255), 1);
accountList = std::make_shared<CListBox>(createAccountCardCallback, Point(10, 116), Point(0, 40), 4, roomDescription.participants.size(), 0, 1 | 4, Rect(130, 0, 160, 160));
accountList->setRedrawParent(true);
accountListTitle = std::make_shared<CLabel>( 12, 109, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, "Players in room");
const auto & createModCardCallback = [this](size_t index) -> std::shared_ptr<CIntObject>
{
if(index < modVerificationList.size())
return std::make_shared<GlobalLobbyRoomModCard>(modVerificationList[index]);
return std::make_shared<CIntObject>();
};
modListBackground = std::make_shared<TransparentFilledRectangle>(Rect(178, 48, 220, 340), ColorRGBA(0, 0, 0, 64), ColorRGBA(64, 80, 128, 255), 1);
modList = std::make_shared<CListBox>(createModCardCallback, Point(180, 66), Point(0, 40), 8, modVerificationList.size(), 0, 1 | 4, Rect(200, 0, 320, 320));
modList->setRedrawParent(true);
modListTitle = std::make_shared<CLabel>( 182, 59, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, "Used mods");
buttonJoin->block(!canJoinRoom);
filledBackground->playerColored(PlayerColor(1));
center();
}
void GlobalLobbyRoomWindow::onJoin()
{
window->doJoinRoom(roomUUID);
}
void GlobalLobbyRoomWindow::onClose()
{
close();
}

View File

@ -0,0 +1,86 @@
/*
* GlobalLobbyRoomWindow.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../windows/CWindowObject.h"
#include "../../lib/modding/ModVerificationInfo.h"
class CLabel;
class CTextBox;
class FilledTexturePlayerColored;
class CButton;
class CToggleGroup;
class GlobalLobbyWindow;
class TransparentFilledRectangle;
class CListBox;
struct GlobalLobbyAccount;
struct GlobalLobbyRoom;
struct ModVerificationInfo;
struct GlobalLobbyRoomModInfo
{
std::string modName;
std::string version;
ModVerificationStatus status;
};
class GlobalLobbyRoomAccountCard : public CIntObject
{
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
std::shared_ptr<CLabel> labelName;
std::shared_ptr<CLabel> labelStatus;
public:
GlobalLobbyRoomAccountCard(const GlobalLobbyAccount & accountDescription);
};
class GlobalLobbyRoomModCard : public CIntObject
{
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
std::shared_ptr<CLabel> labelName;
std::shared_ptr<CLabel> labelStatus;
std::shared_ptr<CLabel> labelVersion;
public:
GlobalLobbyRoomModCard(const GlobalLobbyRoomModInfo & modInfo);
};
class GlobalLobbyRoomWindow : public CWindowObject
{
std::vector<GlobalLobbyRoomModInfo> modVerificationList;
GlobalLobbyWindow * window;
std::string roomUUID;
std::shared_ptr<FilledTexturePlayerColored> filledBackground;
std::shared_ptr<CLabel> labelTitle;
std::shared_ptr<CLabel> labelSubtitle;
std::shared_ptr<CLabel> labelDescription;
std::shared_ptr<CLabel> labelVersion;
std::shared_ptr<CLabel> labelJoinStatus;
std::shared_ptr<CLabel> accountListTitle;
std::shared_ptr<CLabel> modListTitle;
std::shared_ptr<TransparentFilledRectangle> accountListBackground;
std::shared_ptr<TransparentFilledRectangle> modListBackground;
std::shared_ptr<CListBox> accountList;
std::shared_ptr<CListBox> modList;
std::shared_ptr<CButton> buttonJoin;
std::shared_ptr<CButton> buttonClose;
void onJoin();
void onClose();
public:
GlobalLobbyRoomWindow(GlobalLobbyWindow * window, const std::string & roomUUID);
};

View File

@ -13,6 +13,7 @@
#include "GlobalLobbyClient.h"
#include "GlobalLobbyWindow.h"
#include "GlobalLobbyRoomWindow.h"
#include "../CGameInfo.h"
#include "../CMusicHandler.h"
@ -209,17 +210,13 @@ GlobalLobbyAccountCard::GlobalLobbyAccountCard(GlobalLobbyWindow * window, const
}
GlobalLobbyRoomCard::GlobalLobbyRoomCard(GlobalLobbyWindow * window, const GlobalLobbyRoom & roomDescription)
: roomUUID(roomDescription.gameRoomID)
, window(window)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
addUsedEvents(LCLICK);
const auto & onJoinClicked = [window, roomID = roomDescription.gameRoomID]()
{
window->doJoinRoom(roomID);
};
bool publicRoom = roomDescription.statusID == "public";
bool hasInvite = CSH->getGlobalLobby().isInvitedToRoom(roomDescription.gameRoomID);
bool canJoin = publicRoom || hasInvite;
auto roomSizeText = MetaString::createFromRawString("%d/%d");
roomSizeText.replaceNumber(roomDescription.participants.size());
@ -244,12 +241,11 @@ GlobalLobbyRoomCard::GlobalLobbyRoomCard(GlobalLobbyWindow * window, const Globa
labelRoomSize = std::make_shared<CLabel>(178, 10, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, roomSizeText.toString());
labelRoomStatus = std::make_shared<CLabel>(190, 30, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, roomStatusText.toString());
iconRoomSize = std::make_shared<CPicture>(ImagePath::builtin("lobby/iconPlayer"), Point(180, 5));
}
if(!CSH->inGame() && canJoin)
{
buttonJoin = std::make_shared<CButton>(Point(194, 4), AnimationPath::builtin("lobbyJoinRoom"), CButton::tooltip(), onJoinClicked);
buttonJoin->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("lobby/iconEnter")));
}
void GlobalLobbyRoomCard::clickPressed(const Point & cursorPosition)
{
GH.windows().createAndPushWindow<GlobalLobbyRoomWindow>(window, roomUUID);
}
GlobalLobbyChannelCard::GlobalLobbyChannelCard(GlobalLobbyWindow * window, const std::string & channelName)

View File

@ -69,6 +69,9 @@ public:
class GlobalLobbyRoomCard : public CIntObject
{
GlobalLobbyWindow * window;
std::string roomUUID;
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
std::shared_ptr<CLabel> labelName;
std::shared_ptr<CLabel> labelRoomSize;
@ -77,6 +80,7 @@ class GlobalLobbyRoomCard : public CIntObject
std::shared_ptr<CButton> buttonJoin;
std::shared_ptr<CPicture> iconRoomSize;
void clickPressed(const Point & cursorPosition) override;
public:
GlobalLobbyRoomCard(GlobalLobbyWindow * window, const GlobalLobbyRoom & roomDescription);
};

View File

@ -96,7 +96,7 @@
}
}
}
}
},
"status" :
{
"type" : "string",

View File

@ -109,9 +109,9 @@ void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
throw std::domain_error(VLC->generaltexth->translate("core.genrltxt.529"));
auto missingMods = CMapService::verifyMapHeaderMods(*mi->mapHeader);
ModIncompatibility::ModListWithVersion modList;
ModIncompatibility::ModList modList;
for(const auto & m : missingMods)
modList.push_back({m.second.name, m.second.version.toString()});
modList.push_back(m.second.name);
if(!modList.empty())
throw ModIncompatibility(modList);

View File

@ -32,85 +32,26 @@ const ModVerificationInfo & ActiveModsInSaveList::getVerificationInfo(TModID mod
return VLC->modh->getModInfo(mod).getVerificationInfo();
}
void ActiveModsInSaveList::verifyActiveMods(const std::vector<std::pair<TModID, ModVerificationInfo>> & modList)
void ActiveModsInSaveList::verifyActiveMods(const std::map<TModID, ModVerificationInfo> & modList)
{
auto searchVerificationInfo = [&modList](const TModID & m) -> const ModVerificationInfo*
auto comparison = ModVerificationInfo::verifyListAgainstLocalMods(modList);
std::vector<TModID> missingMods;
std::vector<TModID> excessiveMods;
for (auto const & compared : comparison)
{
for(auto & i : modList)
if(i.first == m)
return &i.second;
return nullptr;
};
if (compared.second == ModVerificationStatus::NOT_INSTALLED)
missingMods.push_back(modList.at(compared.first).name);
std::vector<TModID> missingMods, excessiveMods;
ModIncompatibility::ModListWithVersion missingModsResult;
ModIncompatibility::ModList excessiveModsResult;
if (compared.second == ModVerificationStatus::DISABLED)
missingMods.push_back(VLC->modh->getModInfo(compared.first).getVerificationInfo().name);
for(const auto & m : VLC->modh->getActiveMods())
{
if(searchVerificationInfo(m))
continue;
//TODO: support actual disabling of these mods
if(VLC->modh->getModInfo(m).checkModGameplayAffecting())
excessiveMods.push_back(m);
if (compared.second == ModVerificationStatus::EXCESSIVE)
excessiveMods.push_back(modList.at(compared.first).name);
}
for(const auto & infoPair : modList)
{
auto & remoteModId = infoPair.first;
auto & remoteModInfo = infoPair.second;
bool modAffectsGameplay = remoteModInfo.impactsGameplay;
//parent mod affects gameplay if child affects too
for(const auto & subInfoPair : modList)
modAffectsGameplay |= (subInfoPair.second.impactsGameplay && subInfoPair.second.parent == remoteModId);
if(!vstd::contains(VLC->modh->getAllMods(), remoteModId))
{
if(modAffectsGameplay)
missingMods.push_back(remoteModId); //mod is not installed
continue;
}
auto & localModInfo = VLC->modh->getModInfo(remoteModId).getVerificationInfo();
modAffectsGameplay |= VLC->modh->getModInfo(remoteModId).checkModGameplayAffecting();
bool modVersionCompatible = localModInfo.version.isNull()
|| remoteModInfo.version.isNull()
|| localModInfo.version.compatible(remoteModInfo.version);
bool modLocalyEnabled = vstd::contains(VLC->modh->getActiveMods(), remoteModId);
if(modVersionCompatible && modAffectsGameplay && modLocalyEnabled)
continue;
if(modAffectsGameplay)
missingMods.push_back(remoteModId); //incompatible mod impacts gameplay
}
//filter mods
for(auto & m : missingMods)
{
if(auto * vInfo = searchVerificationInfo(m))
{
assert(vInfo->parent != m);
if(!vInfo->parent.empty() && vstd::contains(missingMods, vInfo->parent))
continue;
missingModsResult.push_back({vInfo->name, vInfo->version.toString()});
}
}
for(auto & m : excessiveMods)
{
auto & vInfo = VLC->modh->getModInfo(m).getVerificationInfo();
assert(vInfo.parent != m);
if(!vInfo.parent.empty() && vstd::contains(excessiveMods, vInfo.parent))
continue;
excessiveModsResult.push_back(vInfo.name);
}
if(!missingModsResult.empty() || !excessiveModsResult.empty())
throw ModIncompatibility(missingModsResult, excessiveModsResult);
//TODO: support actual enabling of required mods
if(!missingMods.empty() || !excessiveMods.empty())
throw ModIncompatibility(missingMods, excessiveMods);
}

View File

@ -20,7 +20,7 @@ class ActiveModsInSaveList
const ModVerificationInfo & getVerificationInfo(TModID mod);
/// Checks whether provided mod list is compatible with current VLC and throws on failure
void verifyActiveMods(const std::vector<std::pair<TModID, ModVerificationInfo>> & modList);
void verifyActiveMods(const std::map<TModID, ModVerificationInfo> & modList);
public:
template <typename Handler> void serialize(Handler &h)
{
@ -36,11 +36,11 @@ public:
std::vector<TModID> saveActiveMods;
h & saveActiveMods;
std::vector<std::pair<TModID, ModVerificationInfo>> saveModInfos(saveActiveMods.size());
std::map<TModID, ModVerificationInfo> saveModInfos;
for(int i = 0; i < saveActiveMods.size(); ++i)
{
saveModInfos[i].first = saveActiveMods[i];
h & saveModInfos[i].second;
ModVerificationInfo data;
h & saveModInfos[saveActiveMods[i]];
}
verifyActiveMods(saveModInfos);

View File

@ -59,6 +59,16 @@ std::string CModVersion::toString() const
return res;
}
bool CModVersion::operator ==(const CModVersion & other) const
{
return major == other.major && minor == other.minor && patch == other.patch;
}
bool CModVersion::operator !=(const CModVersion & other) const
{
return major != other.major || minor != other.minor || patch != other.patch;
}
bool CModVersion::compatible(const CModVersion & other, bool checkMinor, bool checkPatch) const
{
bool doCheckMinor = checkMinor && minor != Any && other.minor != Any;

View File

@ -35,6 +35,8 @@ struct DLL_LINKAGE CModVersion
static CModVersion fromString(std::string from);
std::string toString() const;
bool operator !=(const CModVersion & other) const;
bool operator ==(const CModVersion & other) const;
bool compatible(const CModVersion & other, bool checkMinor = false, bool checkPatch = false) const;
bool isNull() const;

View File

@ -14,18 +14,17 @@ VCMI_LIB_NAMESPACE_BEGIN
class DLL_LINKAGE ModIncompatibility: public std::exception
{
public:
using ModListWithVersion = std::vector<std::pair<const std::string, const std::string>>;
using ModList = std::vector<std::string>;
ModIncompatibility(const ModListWithVersion & _missingMods)
ModIncompatibility(const ModList & _missingMods)
{
std::ostringstream _ss;
for(const auto & m : _missingMods)
_ss << m.first << ' ' << m.second << std::endl;
_ss << m << std::endl;
messageMissingMods = _ss.str();
}
ModIncompatibility(const ModListWithVersion & _missingMods, ModList & _excessiveMods)
ModIncompatibility(const ModList & _missingMods, ModList & _excessiveMods)
: ModIncompatibility(_missingMods)
{
std::ostringstream _ss;

View File

@ -10,7 +10,12 @@
#include "StdInc.h"
#include "ModVerificationInfo.h"
#include "CModInfo.h"
#include "CModHandler.h"
#include "ModIncompatibility.h"
#include "../json/JsonNode.h"
#include "../VCMI_Lib.h"
JsonNode ModVerificationInfo::jsonSerializeList(const ModCompatibilityInfo & input)
{
@ -51,3 +56,55 @@ ModCompatibilityInfo ModVerificationInfo::jsonDeserializeList(const JsonNode & i
return output;
}
ModListVerificationStatus ModVerificationInfo::verifyListAgainstLocalMods(const ModCompatibilityInfo & modList)
{
ModListVerificationStatus result;
for(const auto & m : VLC->modh->getActiveMods())
{
if(modList.count(m))
continue;
if(VLC->modh->getModInfo(m).checkModGameplayAffecting())
result[m] = ModVerificationStatus::EXCESSIVE;
}
for(const auto & infoPair : modList)
{
auto & remoteModId = infoPair.first;
auto & remoteModInfo = infoPair.second;
bool modAffectsGameplay = remoteModInfo.impactsGameplay;
//parent mod affects gameplay if child affects too
for(const auto & subInfoPair : modList)
modAffectsGameplay |= (subInfoPair.second.impactsGameplay && subInfoPair.second.parent == remoteModId);
if(!vstd::contains(VLC->modh->getAllMods(), remoteModId))
{
result[remoteModId] = ModVerificationStatus::NOT_INSTALLED;
continue;
}
auto & localModInfo = VLC->modh->getModInfo(remoteModId).getVerificationInfo();
modAffectsGameplay |= VLC->modh->getModInfo(remoteModId).checkModGameplayAffecting();
assert(modAffectsGameplay); // such mods should not be in the list to begin with
if (!vstd::contains(VLC->modh->getActiveMods(), remoteModId))
{
result[remoteModId] = ModVerificationStatus::DISABLED;
continue;
}
if(remoteModInfo.version != localModInfo.version)
{
result[remoteModId] = ModVerificationStatus::VERSION_MISMATCH;
continue;
}
result[remoteModId] = ModVerificationStatus::FULL_MATCH;
}
return result;
}

View File

@ -17,6 +17,17 @@ class JsonNode;
struct ModVerificationInfo;
using ModCompatibilityInfo = std::map<std::string, ModVerificationInfo>;
enum class ModVerificationStatus
{
NOT_INSTALLED, /// Mod is not installed locally
DISABLED, /// Mod is installed locally but not enabled
EXCESSIVE, /// Mod is enabled locally but must be disabled
VERSION_MISMATCH, /// Mod is present on both sides, but has different version
FULL_MATCH, /// No issues detected, everything matches
};
using ModListVerificationStatus = std::map<std::string, ModVerificationStatus>;
struct DLL_LINKAGE ModVerificationInfo
{
/// human-readable mod name
@ -36,6 +47,7 @@ struct DLL_LINKAGE ModVerificationInfo
static JsonNode jsonSerializeList(const ModCompatibilityInfo & input);
static ModCompatibilityInfo jsonDeserializeList(const JsonNode & input);
static ModListVerificationStatus verifyListAgainstLocalMods(const ModCompatibilityInfo & input);
template <typename Handler>
void serialize(Handler & h)