diff --git a/OpenRA.Game/Map/MapCache.cs b/OpenRA.Game/Map/MapCache.cs index de966d3252..a1ac688605 100644 --- a/OpenRA.Game/Map/MapCache.cs +++ b/OpenRA.Game/Map/MapCache.cs @@ -112,7 +112,7 @@ namespace OpenRA } } - public void QueryRemoteMapDetails(IEnumerable uids, Action mapDetailsReceived = null) + public void QueryRemoteMapDetails(IEnumerable uids, Action mapDetailsReceived = null, Action queryFailed = null) { var maps = uids.Distinct() .Select(uid => previews[uid]) @@ -136,6 +136,9 @@ namespace OpenRA foreach (var p in maps.Values) p.UpdateRemoteSearch(MapStatus.Unavailable, null); + if (queryFailed != null) + queryFailed(); + return; } @@ -149,6 +152,8 @@ namespace OpenRA catch { Log.Write("debug", "Can't parse remote map search data:\n{0}", data); + if (queryFailed != null) + queryFailed(); } }; diff --git a/OpenRA.Game/Settings.cs b/OpenRA.Game/Settings.cs index ce24c64b20..6454c6f220 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -74,6 +74,9 @@ namespace OpenRA [Desc("Disallow games where only one player plays with bots.")] public bool DisableSinglePlayer = false; + [Desc("Query map information from the Resource Center if they are not available locally.")] + public bool QueryMapRepository = true; + public string TimestampFormat = "s"; public ServerSettings Clone() diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 671cb96374..330abbefc8 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Threading; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Network; @@ -338,64 +339,87 @@ namespace OpenRA.Mods.Common.Server return true; } - if (server.ModData.MapCache[s].Status != MapStatus.Available) + var lastMap = server.LobbyInfo.GlobalSettings.Map; + Action selectMap = map => { - server.SendOrderTo(conn, "Message", "Map was not found on server."); - return true; - } + // Make sure the map hasn't changed in the meantime + if (server.LobbyInfo.GlobalSettings.Map != lastMap) + return; - server.LobbyInfo.GlobalSettings.Map = s; + server.LobbyInfo.GlobalSettings.Map = map.Uid; - var oldSlots = server.LobbyInfo.Slots.Keys.ToArray(); - LoadMap(server); + var oldSlots = server.LobbyInfo.Slots.Keys.ToArray(); + server.Map = server.ModData.MapCache[server.LobbyInfo.GlobalSettings.Map]; - // Reset client states - foreach (var c in server.LobbyInfo.Clients) - c.State = Session.ClientState.Invalid; + server.LobbyInfo.Slots = server.Map.Players.Players + .Select(p => MakeSlotFromPlayerReference(p.Value)) + .Where(ss => ss != null) + .ToDictionary(ss => ss.PlayerReference, ss => ss); - // Reassign players into new slots based on their old slots: - // - Observers remain as observers - // - Players who now lack a slot are made observers - // - Bots who now lack a slot are dropped - // - Bots who are not defined in the map rules are dropped - var botNames = server.Map.Rules.Actors["player"].TraitInfos().Select(t => t.Name); - var slots = server.LobbyInfo.Slots.Keys.ToArray(); - var i = 0; - foreach (var os in oldSlots) - { - var c = server.LobbyInfo.ClientInSlot(os); - if (c == null) - continue; + LoadMapSettings(server.LobbyInfo.GlobalSettings, server.Map.Rules); - c.SpawnPoint = 0; - c.Slot = i < slots.Length ? slots[i++] : null; - if (c.Slot != null) + // Reset client states + foreach (var c in server.LobbyInfo.Clients) + c.State = Session.ClientState.Invalid; + + // Reassign players into new slots based on their old slots: + // - Observers remain as observers + // - Players who now lack a slot are made observers + // - Bots who now lack a slot are dropped + // - Bots who are not defined in the map rules are dropped + var botNames = server.Map.Rules.Actors["player"].TraitInfos().Select(t => t.Name); + var slots = server.LobbyInfo.Slots.Keys.ToArray(); + var i = 0; + foreach (var os in oldSlots) { - // Remove Bot from slot if slot forbids bots - if (c.Bot != null && (!server.Map.Players.Players[c.Slot].AllowBots || !botNames.Contains(c.Bot))) + var c = server.LobbyInfo.ClientInSlot(os); + if (c == null) + continue; + + c.SpawnPoint = 0; + c.Slot = i < slots.Length ? slots[i++] : null; + if (c.Slot != null) + { + // Remove Bot from slot if slot forbids bots + if (c.Bot != null && (!server.Map.Players.Players[c.Slot].AllowBots || !botNames.Contains(c.Bot))) + server.LobbyInfo.Clients.Remove(c); + S.SyncClientToPlayerReference(c, server.Map.Players.Players[c.Slot]); + } + else if (c.Bot != null) server.LobbyInfo.Clients.Remove(c); - S.SyncClientToPlayerReference(c, server.Map.Players.Players[c.Slot]); } - else if (c.Bot != null) - server.LobbyInfo.Clients.Remove(c); + + // Validate if color is allowed and get an alternative it isn't + foreach (var c in server.LobbyInfo.Clients) + if (c.Slot == null || (c.Slot != null && !server.LobbyInfo.Slots[c.Slot].LockColor)) + c.Color = c.PreferredColor = SanitizePlayerColor(server, c.Color, c.Index, conn); + + server.SyncLobbyInfo(); + + server.SendMessage("{0} changed the map to {1}.".F(client.Name, server.Map.Title)); + + if (server.Map.Rules.Actors != server.ModData.DefaultRules.Actors) + server.SendMessage("This map contains custom rules. Game experience may change."); + + if (server.Settings.DisableSinglePlayer) + server.SendMessage("Singleplayer games have been disabled on this server."); + else if (server.Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots)) + server.SendMessage("Bots have been disabled on this map."); + }; + + Action queryFailed = () => + server.SendOrderTo(conn, "Message", "Map was not found on server."); + + var m = server.ModData.MapCache[s]; + if (m.Status == MapStatus.Available || m.Status == MapStatus.DownloadAvailable) + selectMap(m); + else if (server.Settings.QueryMapRepository) + { + server.SendOrderTo(conn, "Message", "Searching for map on the Resource Center..."); + server.ModData.MapCache.QueryRemoteMapDetails(new[] { s }, selectMap, queryFailed); } - - // Validate if color is allowed and get an alternative it isn't - foreach (var c in server.LobbyInfo.Clients) - if (c.Slot == null || (c.Slot != null && !server.LobbyInfo.Slots[c.Slot].LockColor)) - c.Color = c.PreferredColor = SanitizePlayerColor(server, c.Color, c.Index, conn); - - server.SyncLobbyInfo(); - - server.SendMessage("{0} changed the map to {1}.".F(client.Name, server.Map.Title)); - - if (server.Map.Rules.Actors != server.ModData.DefaultRules.Actors) - server.SendMessage("This map contains custom rules. Game experience may change."); - - if (server.Settings.DisableSinglePlayer) - server.SendMessage("Singleplayer games have been disabled on this server."); - else if (server.Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots)) - server.SendMessage("Bots have been disabled on this map."); + else + queryFailed(); return true; } @@ -996,7 +1020,18 @@ namespace OpenRA.Mods.Common.Server public void ServerStarted(S server) { - LoadMap(server); + // Remote maps are not supported for the initial map + var uid = server.LobbyInfo.GlobalSettings.Map; + server.Map = server.ModData.MapCache[uid]; + if (server.Map.Status != MapStatus.Available) + throw new Exception("Map {0} not found".F(uid)); + + server.LobbyInfo.Slots = server.Map.Players.Players + .Select(p => MakeSlotFromPlayerReference(p.Value)) + .Where(s => s != null) + .ToDictionary(s => s.PlayerReference, s => s); + + LoadMapSettings(server.LobbyInfo.GlobalSettings, server.Map.Rules); } static Session.Slot MakeSlotFromPlayerReference(PlayerReference pr) @@ -1042,18 +1077,6 @@ namespace OpenRA.Mods.Common.Server gs.Difficulty = mapOptions.Difficulty ?? mapOptions.Difficulties.FirstOrDefault(); } - static void LoadMap(S server) - { - server.Map = server.ModData.MapCache[server.LobbyInfo.GlobalSettings.Map]; - - server.LobbyInfo.Slots = server.Map.Players.Players - .Select(p => MakeSlotFromPlayerReference(p.Value)) - .Where(s => s != null) - .ToDictionary(s => s.PlayerReference, s => s); - - LoadMapSettings(server.LobbyInfo.GlobalSettings, server.Map.Rules); - } - static HSLColor SanitizePlayerColor(S server, HSLColor askedColor, int playerIndex, Connection connectionToEcho = null) { var validator = server.ModData.Manifest.Get();