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.hostAccountDisplayName = jsonEntry["hostAccountDisplayName"].String();
 		room.description = jsonEntry["description"].String();
 		room.description = jsonEntry["description"].String();
 		room.statusID = jsonEntry["status"].String();
 		room.statusID = jsonEntry["status"].String();
+		room.gameVersion = jsonEntry["version"].String();
+		room.modList = ModVerificationInfo::jsonDeserializeList(jsonEntry["mods"]);
 		std::chrono::seconds ageSeconds (jsonEntry["ageSeconds"].Integer());
 		std::chrono::seconds ageSeconds (jsonEntry["ageSeconds"].Integer());
 		room.startDateFormatted = TextOperations::getCurrentFormattedDateTimeLocal(-ageSeconds);
 		room.startDateFormatted = TextOperations::getCurrentFormattedDateTimeLocal(-ageSeconds);
 
 

+ 4 - 0
client/globalLobby/GlobalLobbyDefines.h

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

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

@@ -20,7 +20,7 @@
 			{
 			{
 				"type" : "object",
 				"type" : "object",
 				"additionalProperties" : false,
 				"additionalProperties" : false,
-				"required" : [ "gameRoomID", "hostAccountID", "hostAccountDisplayName", "description", "participants", "playerLimit", "status", "ageSeconds" ],
+				"required" : [ "gameRoomID", "hostAccountID", "hostAccountDisplayName", "description", "participants", "playerLimit", "status", "ageSeconds", "mods", "version" ],
 				"properties" : {
 				"properties" : {
 					"gameRoomID" :
 					"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" :
 					"status" :
 					{
 					{
 						"type" : "string",
 						"type" : "string",
@@ -78,6 +110,11 @@
 						"maximum" : 8,
 						"maximum" : 8,
 						"description" : "Maximum number of players that can join this room, including host"
 						"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" :
 					"ageSeconds" :
 					{
 					{
 						"type" : "number",
 						"type" : "number",

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

@@ -32,6 +32,38 @@
 		{
 		{
 			"type" : "string",
 			"type" : "string",
 			"description" : "Version of match server, e.g. 1.5.0"
 			"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;
 		JsonNode modWriter;
 		modWriter["modId"].String() = mod.first;
 		modWriter["modId"].String() = mod.first;
 		modWriter["name"].String() = mod.second.name;
 		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();
 		modWriter["version"].String() = mod.second.version.toString();
 		output.Vector().push_back(modWriter);
 		output.Vector().push_back(modWriter);
 	}
 	}

+ 1 - 1
lib/modding/ModVerificationInfo.h

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

+ 34 - 6
lobby/LobbyDatabase.cpp

@@ -33,7 +33,8 @@ void LobbyDatabase::createTables()
 			description TEXT NOT NULL DEFAULT '',
 			description TEXT NOT NULL DEFAULT '',
 			status INTEGER NOT NULL DEFAULT 0,
 			status INTEGER NOT NULL DEFAULT 0,
 			playerLimit INTEGER NOT NULL,
 			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();
 	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()
 void LobbyDatabase::clearOldData()
 {
 {
 	static const std::string removeActiveAccounts = R"(
 	static const std::string removeActiveAccounts = R"(
@@ -122,7 +149,7 @@ void LobbyDatabase::prepareStatements()
 	)");
 	)");
 
 
 	insertGameRoomStatement = database->prepare(R"(
 	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"(
 	insertGameRoomPlayersStatement = database->prepare(R"(
@@ -233,7 +260,7 @@ void LobbyDatabase::prepareStatements()
 	)");
 	)");
 
 
 	getActiveGameRoomsStatement = database->prepare(R"(
 	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
 		FROM gameRooms gr
 		LEFT JOIN accounts a ON gr.hostAccountID = a.accountID
 		LEFT JOIN accounts a ON gr.hostAccountID = a.accountID
 		WHERE status IN (1, 2, 3)
 		WHERE status IN (1, 2, 3)
@@ -298,6 +325,7 @@ LobbyDatabase::LobbyDatabase(const boost::filesystem::path & databasePath)
 {
 {
 	database = SQLiteInstance::open(databasePath, true);
 	database = SQLiteInstance::open(databasePath, true);
 	createTables();
 	createTables();
+	upgradeDatabase();
 	clearOldData();
 	clearOldData();
 	prepareStatements();
 	prepareStatements();
 }
 }
@@ -393,9 +421,9 @@ void LobbyDatabase::insertGameRoomInvite(const std::string & targetAccountID, co
 	insertGameRoomInvitesStatement->executeOnce(roomID, targetAccountID);
 	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)
 void LobbyDatabase::insertAccount(const std::string & accountID, const std::string & displayName)
@@ -526,7 +554,7 @@ std::vector<LobbyGameRoom> LobbyDatabase::getActiveGameRooms()
 	while(getActiveGameRoomsStatement->execute())
 	while(getActiveGameRoomsStatement->execute())
 	{
 	{
 		LobbyGameRoom entry;
 		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);
 		result.push_back(entry);
 	}
 	}
 	getActiveGameRoomsStatement->reset();
 	getActiveGameRoomsStatement->reset();

+ 2 - 1
lobby/LobbyDatabase.h

@@ -59,6 +59,7 @@ class LobbyDatabase
 
 
 	void prepareStatements();
 	void prepareStatements();
 	void createTables();
 	void createTables();
+	void upgradeDatabase();
 	void clearOldData();
 	void clearOldData();
 
 
 public:
 public:
@@ -74,7 +75,7 @@ public:
 	void deleteGameRoomInvite(const std::string & targetAccountID, const std::string & roomID);
 	void deleteGameRoomInvite(const std::string & targetAccountID, const std::string & roomID);
 	void insertGameRoomInvite(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 insertAccount(const std::string & accountID, const std::string & displayName);
 	void insertAccessCookie(const std::string & accountID, const std::string & accessCookieUUID);
 	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);
 	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 hostAccountID;
 	std::string hostAccountDisplayName;
 	std::string hostAccountDisplayName;
 	std::string description;
 	std::string description;
+	std::string version;
+	std::string modsJson;
 	std::vector<LobbyAccount> participants;
 	std::vector<LobbyAccount> participants;
 	LobbyRoomState roomState;
 	LobbyRoomState roomState;
 	uint32_t playerLimit;
 	uint32_t playerLimit;

+ 4 - 1
lobby/LobbyServer.cpp

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

+ 17 - 0
server/GlobalLobbyProcessor.cpp

@@ -13,6 +13,9 @@
 #include "CVCMIServer.h"
 #include "CVCMIServer.h"
 #include "../lib/CConfigHandler.h"
 #include "../lib/CConfigHandler.h"
 #include "../lib/json/JsonUtils.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)
 GlobalLobbyProcessor::GlobalLobbyProcessor(CVCMIServer & owner)
 	: owner(owner)
 	: owner(owner)
@@ -125,6 +128,7 @@ void GlobalLobbyProcessor::onConnectionEstablished(const std::shared_ptr<INetwor
 		toSend["accountID"].String() = getHostAccountID();
 		toSend["accountID"].String() = getHostAccountID();
 		toSend["accountCookie"].String() = getHostAccountCookie();
 		toSend["accountCookie"].String() = getHostAccountCookie();
 		toSend["version"].String() = VCMI_VERSION_STRING;
 		toSend["version"].String() = VCMI_VERSION_STRING;
+		toSend["mods"] = getHostModList();
 
 
 		sendMessage(connection, toSend);
 		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()
 void GlobalLobbyProcessor::sendGameStarted()
 {
 {
 	JsonNode toSend;
 	JsonNode toSend;

+ 1 - 0
server/GlobalLobbyProcessor.h

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