diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs
index 8b2be30aa7..3f33cfaa03 100644
--- a/OpenRA.Game/Manifest.cs
+++ b/OpenRA.Game/Manifest.cs
@@ -46,6 +46,8 @@ namespace OpenRA
{
public string Title;
public string Version;
+ public string Website;
+ public string WebIcon32;
public bool Hidden;
}
diff --git a/OpenRA.Game/Network/GameServer.cs b/OpenRA.Game/Network/GameServer.cs
index 495ebfb11a..4572efd2cb 100644
--- a/OpenRA.Game/Network/GameServer.cs
+++ b/OpenRA.Game/Network/GameServer.cs
@@ -45,7 +45,18 @@ namespace OpenRA.Network
public class GameServer
{
- static readonly string[] SerializeFields = { "Name", "Address", "Mod", "Version", "Map", "State", "MaxPlayers", "Protected" };
+ static readonly string[] SerializeFields =
+ {
+ // Server information
+ "Name", "Address",
+
+ // Mod information
+ "Mod", "Version", "ModTitle", "ModWebsite", "ModIcon32",
+
+ // Current server state
+ "Map", "State", "MaxPlayers", "Protected"
+ };
+
public const int ProtocolVersion = 2;
/// Online game number or -1 for LAN games
@@ -75,6 +86,15 @@ namespace OpenRA.Network
/// Mod Version
public readonly string Version = "";
+ /// Human-readable mod title
+ public readonly string ModTitle = "";
+
+ /// URL to show in game listings for custom/unknown mods.
+ public readonly string ModWebsite = "";
+
+ /// URL to a 32x32 px icon for the mod.
+ public readonly string ModIcon32 = "";
+
/// Password protected
public readonly bool Protected = false;
@@ -101,13 +121,11 @@ namespace OpenRA.Network
[FieldLoader.Ignore]
public readonly bool IsJoinable = false;
- /// Label to display in the multiplayer browser. Only defined if GameServer is parsed from yaml.
- [FieldLoader.Ignore]
- public readonly string ModLabel = "";
-
[FieldLoader.LoadUsing("LoadClients")]
public readonly GameClient[] Clients;
+ public string ModLabel { get { return "{0} ({1})".F(ModTitle, Version); } }
+
static object LoadClients(MiniYaml yaml)
{
var clients = new List();
@@ -147,33 +165,40 @@ namespace OpenRA.Network
PlayTime = (int)(DateTime.UtcNow - startTime).TotalSeconds;
}
- Manifest mod;
ExternalMod external;
-
var externalKey = ExternalMod.MakeKey(Mod, Version);
if (Game.ExternalMods.TryGetValue(externalKey, out external) && external.Version == Version)
- {
- ModLabel = "{0} ({1})".F(external.Title, external.Version);
IsCompatible = true;
- }
- else if (Game.Mods.TryGetValue(Mod, out mod))
- {
- // Use internal mod data to populate the section header, but
- // on-connect switching must use the external mod plumbing.
- ModLabel = "{0} ({1})".F(mod.Metadata.Title, Version);
- }
- else
- {
- // Some platforms (e.g. macOS) package each mod separately, so the Mods check above won't work.
- // Guess based on the most recent ExternalMod instead.
- var guessMod = Game.ExternalMods.Values
- .OrderByDescending(m => m.Version)
- .FirstOrDefault(m => m.Id == Mod);
- if (guessMod != null)
- ModLabel = "{0} ({1})".F(guessMod.Title, Version);
+ // Games advertised using the old API used local mod metadata
+ if (string.IsNullOrEmpty(ModTitle))
+ {
+ Manifest mod;
+
+ if (external != null && external.Version == Version)
+ {
+ // Use external mod registration to populate the section header
+ ModTitle = external.Title;
+ }
+ else if (Game.Mods.TryGetValue(Mod, out mod))
+ {
+ // Use internal mod data to populate the section header, but
+ // on-connect switching must use the external mod plumbing.
+ ModTitle = mod.Metadata.Title;
+ }
else
- ModLabel = "Unknown mod: {0} ({1})".F(Mod, Version);
+ {
+ // Some platforms (e.g. macOS) package each mod separately, so the Mods check above won't work.
+ // Guess based on the most recent ExternalMod instead.
+ var guessMod = Game.ExternalMods.Values
+ .OrderByDescending(m => m.Version)
+ .FirstOrDefault(m => m.Id == Mod);
+
+ if (guessMod != null)
+ ModTitle = "{0}".F(guessMod.Title);
+ else
+ ModTitle = "Unknown mod: {0}".F(Mod);
+ }
}
var mapAvailable = Game.Settings.Game.AllowDownloading || Game.ModData.MapCache[Map].Status == MapStatus.Available;
@@ -182,6 +207,8 @@ namespace OpenRA.Network
public GameServer(Server.Server server)
{
+ var manifest = server.ModData.Manifest;
+
Name = server.Settings.Name;
// IP address will be replaced with a real value by the master server / receiving LAN client
@@ -189,8 +216,11 @@ namespace OpenRA.Network
State = (int)server.State;
MaxPlayers = server.LobbyInfo.Slots.Count(s => !s.Value.Closed) - server.LobbyInfo.Clients.Count(c1 => c1.Bot != null);
Map = server.Map.Uid;
- Mod = server.ModData.Manifest.Id;
- Version = server.ModData.Manifest.Metadata.Version;
+ Mod = manifest.Id;
+ Version = manifest.Metadata.Version;
+ ModTitle = manifest.Metadata.Title;
+ ModWebsite = manifest.Metadata.Website;
+ ModIcon32 = manifest.Metadata.WebIcon32;
Protected = !string.IsNullOrEmpty(server.Settings.Password);
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray();
}