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(); }