Browse Source

Store and send mod list and game version for game rooms

Ivan Savenko 1 year ago
parent
commit
e5f8cefa7f

+ 2 - 0
client/globalLobby/GlobalLobbyClient.cpp

@@ -201,6 +201,8 @@ void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json)
 		room.hostAccountDisplayName = jsonEntry["hostAccountDisplayName"].String();
 		room.description = jsonEntry["description"].String();
 		room.statusID = jsonEntry["status"].String();
+		room.gameVersion = jsonEntry["version"].String();
+		room.modList = ModVerificationInfo::jsonDeserializeList(jsonEntry["mods"]);
 		std::chrono::seconds ageSeconds (jsonEntry["ageSeconds"].Integer());
 		room.startDateFormatted = TextOperations::getCurrentFormattedDateTimeLocal(-ageSeconds);
 

+ 4 - 0
client/globalLobby/GlobalLobbyDefines.h

@@ -9,6 +9,8 @@
  */
 #pragma once
 
+#include "../../lib/modding/ModVerificationInfo.h"
+
 struct GlobalLobbyAccount
 {
 	std::string accountID;
@@ -22,8 +24,10 @@ struct GlobalLobbyRoom
 	std::string hostAccountID;
 	std::string hostAccountDisplayName;
 	std::string description;
+	std::string gameVersion;
 	std::string statusID;
 	std::string startDateFormatted;
+	ModCompatibilityInfo modList;
 	std::vector<GlobalLobbyAccount> participants;
 	int playerLimit;
 };

+ 38 - 1
config/schemas/lobbyProtocol/activeGameRooms.json

@@ -20,7 +20,7 @@
 			{
 				"type" : "object",
 				"additionalProperties" : false,
-				"required" : [ "gameRoomID", "hostAccountID", "hostAccountDisplayName", "description", "participants", "playerLimit", "status", "ageSeconds" ],
+				"required" : [ "gameRoomID", "hostAccountID", "hostAccountDisplayName", "description", "participants", "playerLimit", "status", "ageSeconds", "mods", "version" ],
 				"properties" : {
 					"gameRoomID" :
 					{
@@ -65,6 +65,38 @@
 							}
 						}
 					},
+					"mods" : 
+					{
+						"type" : "array",
+						"description" : "List of gameplay-affecting mods active on server",
+						"items" : {
+							"type" : "object",
+							"additionalProperties" : false,
+							"required" : [ "modId", "name", "version" ],
+							"properties" : {
+								"modId" :
+								{
+									"type" : "string",
+									"description" : "Unique identifier of the mod"
+								},
+								"name" :
+								{
+									"type" : "string",
+									"description" : "Human-readable name of the mod"
+								},
+								"parent" :
+								{
+									"type" : "string",
+									"description" : "Unique ID of parent mod, only for submods"
+								},
+								"version" :
+								{
+									"type" : "string",
+									"description" : "Version of mod, as specified in mod config"
+								}
+							}
+						}
+					}
 					"status" :
 					{
 						"type" : "string",
@@ -78,6 +110,11 @@
 						"maximum" : 8,
 						"description" : "Maximum number of players that can join this room, including host"
 					},
+					"version" :
+					{
+						"type" : "string",
+						"description" : "Version of match server, e.g. 1.5.0"
+					},
 					"ageSeconds" :
 					{
 						"type" : "number",

+ 32 - 0
config/schemas/lobbyProtocol/serverLogin.json

@@ -32,6 +32,38 @@
 		{
 			"type" : "string",
 			"description" : "Version of match server, e.g. 1.5.0"
+		},
+		"mods" : 
+		{
+			"type" : "array",
+			"description" : "List of gameplay-affecting mods active on server",
+			"items" : {
+				"type" : "object",
+				"additionalProperties" : false,
+				"required" : [ "modId", "name", "version" ],
+				"properties" : {
+					"modId" :
+					{
+						"type" : "string",
+						"description" : "Unique identifier of the mod"
+					},
+					"name" :
+					{
+						"type" : "string",
+						"description" : "Human-readable name of the mod"
+					},
+					"parent" :
+					{
+						"type" : "string",
+						"description" : "Unique ID of parent mod, only for submods"
+					},
+					"version" :
+					{
+						"type" : "string",
+						"description" : "Version of mod, as specified in mod config"
+					}
+				}
+			}
 		}
 	}
 }

+ 2 - 1
lib/modding/ModVerificationInfo.cpp

@@ -21,7 +21,8 @@ JsonNode ModVerificationInfo::jsonSerializeList(const ModCompatibilityInfo & inp
 		JsonNode modWriter;
 		modWriter["modId"].String() = mod.first;
 		modWriter["name"].String() = mod.second.name;
-		modWriter["parent"].String() = mod.second.parent;
+		if (!mod.second.parent.empty())
+			modWriter["parent"].String() = mod.second.parent;
 		modWriter["version"].String() = mod.second.version.toString();
 		output.Vector().push_back(modWriter);
 	}

+ 1 - 1
lib/modding/ModVerificationInfo.h

@@ -17,7 +17,7 @@ class JsonNode;
 struct ModVerificationInfo;
 using ModCompatibilityInfo = std::map<std::string, ModVerificationInfo>;
 
-struct ModVerificationInfo
+struct DLL_LINKAGE ModVerificationInfo
 {
 	/// human-readable mod name
 	std::string name;

+ 34 - 6
lobby/LobbyDatabase.cpp

@@ -33,7 +33,8 @@ void LobbyDatabase::createTables()
 			description TEXT NOT NULL DEFAULT '',
 			status INTEGER NOT NULL DEFAULT 0,
 			playerLimit INTEGER NOT NULL,
-			creationTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
+			creationTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+			mods TEXT NOT NULL DEFAULT '[]'
 		);
 	)";
 
@@ -81,6 +82,32 @@ void LobbyDatabase::createTables()
 	database->prepare(createTableGameRoomInvites)->execute();
 }
 
+void LobbyDatabase::upgradeDatabase()
+{
+	auto getDatabaseVersionStatement = database->prepare(R"(
+		PRAGMA schema.user_version
+	)");
+
+	auto setDatabaseVersionStatement = database->prepare(R"(
+		PRAGMA schema.user_version = ?
+	)");
+
+	int databaseVersion;
+	getDatabaseVersionStatement->execute();
+	getDatabaseVersionStatement->getColumns(databaseVersion);
+
+	if (databaseVersion < 10501)
+	{
+		database->prepare(R"(
+			ALTER TABLE gameRooms
+			ADD COLUMN version TEXT NOT NULL DEFAULT ''
+			ADD COLUMN mods TEXT NOT NULL DEFAULT '{}'
+		)")->execute();
+
+		setDatabaseVersionStatement->executeOnce(10501);
+	}
+}
+
 void LobbyDatabase::clearOldData()
 {
 	static const std::string removeActiveAccounts = R"(
@@ -122,7 +149,7 @@ void LobbyDatabase::prepareStatements()
 	)");
 
 	insertGameRoomStatement = database->prepare(R"(
-		INSERT INTO gameRooms(roomID, hostAccountID, status, playerLimit) VALUES(?, ?, 0, 8);
+		INSERT INTO gameRooms(roomID, hostAccountID, status, playerLimit, version, mods) VALUES(?, ?, 0, 8, ?, ?);
 	)");
 
 	insertGameRoomPlayersStatement = database->prepare(R"(
@@ -233,7 +260,7 @@ void LobbyDatabase::prepareStatements()
 	)");
 
 	getActiveGameRoomsStatement = database->prepare(R"(
-		SELECT roomID, hostAccountID, displayName, description, status, playerLimit, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',gr.creationTime)  AS secondsElapsed
+		SELECT roomID, hostAccountID, displayName, description, status, playerLimit, mods, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',gr.creationTime)  AS secondsElapsed
 		FROM gameRooms gr
 		LEFT JOIN accounts a ON gr.hostAccountID = a.accountID
 		WHERE status IN (1, 2, 3)
@@ -298,6 +325,7 @@ LobbyDatabase::LobbyDatabase(const boost::filesystem::path & databasePath)
 {
 	database = SQLiteInstance::open(databasePath, true);
 	createTables();
+	upgradeDatabase();
 	clearOldData();
 	prepareStatements();
 }
@@ -393,9 +421,9 @@ void LobbyDatabase::insertGameRoomInvite(const std::string & targetAccountID, co
 	insertGameRoomInvitesStatement->executeOnce(roomID, targetAccountID);
 }
 
-void LobbyDatabase::insertGameRoom(const std::string & roomID, const std::string & hostAccountID)
+void LobbyDatabase::insertGameRoom(const std::string & roomID, const std::string & hostAccountID, const std::string & serverVersion, const std::string & modListJson)
 {
-	insertGameRoomStatement->executeOnce(roomID, hostAccountID);
+	insertGameRoomStatement->executeOnce(roomID, hostAccountID, serverVersion, modListJson);
 }
 
 void LobbyDatabase::insertAccount(const std::string & accountID, const std::string & displayName)
@@ -526,7 +554,7 @@ std::vector<LobbyGameRoom> LobbyDatabase::getActiveGameRooms()
 	while(getActiveGameRoomsStatement->execute())
 	{
 		LobbyGameRoom entry;
-		getActiveGameRoomsStatement->getColumns(entry.roomID, entry.hostAccountID, entry.hostAccountDisplayName, entry.description, entry.roomState, entry.playerLimit, entry.age);
+		getActiveGameRoomsStatement->getColumns(entry.roomID, entry.hostAccountID, entry.hostAccountDisplayName, entry.description, entry.roomState, entry.playerLimit, entry.version, entry.modsJson, entry.age);
 		result.push_back(entry);
 	}
 	getActiveGameRoomsStatement->reset();

+ 2 - 1
lobby/LobbyDatabase.h

@@ -59,6 +59,7 @@ class LobbyDatabase
 
 	void prepareStatements();
 	void createTables();
+	void upgradeDatabase();
 	void clearOldData();
 
 public:
@@ -74,7 +75,7 @@ public:
 	void deleteGameRoomInvite(const std::string & targetAccountID, const std::string & roomID);
 	void insertGameRoomInvite(const std::string & targetAccountID, const std::string & roomID);
 
-	void insertGameRoom(const std::string & roomID, const std::string & hostAccountID);
+	void insertGameRoom(const std::string & roomID, const std::string & hostAccountID, const std::string & serverVersion, const std::string & modListJson);
 	void insertAccount(const std::string & accountID, const std::string & displayName);
 	void insertAccessCookie(const std::string & accountID, const std::string & accessCookieUUID);
 	void insertChatMessage(const std::string & sender, const std::string & channelType, const std::string & roomID, const std::string & messageText);

+ 2 - 0
lobby/LobbyDefines.h

@@ -44,6 +44,8 @@ struct LobbyGameRoom
 	std::string hostAccountID;
 	std::string hostAccountDisplayName;
 	std::string description;
+	std::string version;
+	std::string modsJson;
 	std::vector<LobbyAccount> participants;
 	LobbyRoomState roomState;
 	uint32_t playerLimit;

+ 4 - 1
lobby/LobbyServer.cpp

@@ -208,9 +208,11 @@ static JsonNode loadLobbyGameRoomToJson(const LobbyGameRoom & gameRoom)
 	jsonEntry["hostAccountID"].String() = gameRoom.hostAccountID;
 	jsonEntry["hostAccountDisplayName"].String() = gameRoom.hostAccountDisplayName;
 	jsonEntry["description"].String() = gameRoom.description;
+	jsonEntry["version"].String() = gameRoom.version;
 	jsonEntry["status"].String() = LOBBY_ROOM_STATE_NAMES[vstd::to_underlying(gameRoom.roomState)];
 	jsonEntry["playerLimit"].Integer() = gameRoom.playerLimit;
 	jsonEntry["ageSeconds"].Integer() = gameRoom.age.count();
+	jsonEntry["mods"] = JsonNode(reinterpret_cast<const std::byte *>(gameRoom.modsJson.data()), gameRoom.modsJson.size());
 
 	for(const auto & account : gameRoom.participants)
 		jsonEntry["participants"].Vector().push_back(loadLobbyAccountToJson(account));
@@ -625,7 +627,8 @@ void LobbyServer::receiveServerLogin(const NetworkConnectionPtr & connection, co
 	}
 	else
 	{
-		database->insertGameRoom(gameRoomID, accountID);
+		std::string modListString = json["mods"].isNull() ? "[]" : json["mods"].toCompactString();
+		database->insertGameRoom(gameRoomID, accountID, version, modListString);
 		activeGameRooms[connection] = gameRoomID;
 		sendServerLoginSuccess(connection, accountCookie);
 		broadcastActiveGameRooms();

+ 17 - 0
server/GlobalLobbyProcessor.cpp

@@ -13,6 +13,9 @@
 #include "CVCMIServer.h"
 #include "../lib/CConfigHandler.h"
 #include "../lib/json/JsonUtils.h"
+#include "../lib/VCMI_Lib.h"
+#include "../lib/modding/CModHandler.h"
+#include "../lib/modding/CModInfo.h"
 
 GlobalLobbyProcessor::GlobalLobbyProcessor(CVCMIServer & owner)
 	: owner(owner)
@@ -125,6 +128,7 @@ void GlobalLobbyProcessor::onConnectionEstablished(const std::shared_ptr<INetwor
 		toSend["accountID"].String() = getHostAccountID();
 		toSend["accountCookie"].String() = getHostAccountCookie();
 		toSend["version"].String() = VCMI_VERSION_STRING;
+		toSend["mods"] = getHostModList();
 
 		sendMessage(connection, toSend);
 	}
@@ -149,6 +153,19 @@ void GlobalLobbyProcessor::onConnectionEstablished(const std::shared_ptr<INetwor
 	}
 }
 
+JsonNode GlobalLobbyProcessor::getHostModList() const
+{
+	ModCompatibilityInfo info;
+
+	for (auto const & modName : VLC->modh->getActiveMods())
+	{
+		if(VLC->modh->getModInfo(modName).checkModGameplayAffecting())
+			info[modName] = VLC->modh->getModInfo(modName).getVerificationInfo();
+	}
+
+	return ModVerificationInfo::jsonSerializeList(info);
+}
+
 void GlobalLobbyProcessor::sendGameStarted()
 {
 	JsonNode toSend;

+ 1 - 0
server/GlobalLobbyProcessor.h

@@ -36,6 +36,7 @@ class GlobalLobbyProcessor : public INetworkClientListener
 	void establishNewConnection();
 	void sendMessage(const NetworkConnectionPtr & targetConnection, const JsonNode & payload);
 
+	JsonNode getHostModList() const;
 	const std::string & getHostAccountID() const;
 	const std::string & getHostAccountCookie() const;
 	const std::string & getHostAccountDisplayName() const;