diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index f2bdf80ec7..61491cc9ef 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -445,7 +445,7 @@ namespace OpenRA static string ChooseShellmap() { var shellmaps = ModData.MapCache - .Where(m => m.Status == MapStatus.Available && m.Map.Visibility.HasFlag(MapVisibility.Shellmap)) + .Where(m => m.Status == MapStatus.Available && m.Visibility.HasFlag(MapVisibility.Shellmap)) .Select(m => m.Uid); if (!shellmaps.Any()) diff --git a/OpenRA.Game/Map/MapCache.cs b/OpenRA.Game/Map/MapCache.cs index da0eace73c..bd6e01b0ff 100644 --- a/OpenRA.Game/Map/MapCache.cs +++ b/OpenRA.Game/Map/MapCache.cs @@ -174,7 +174,8 @@ namespace OpenRA if (bitmap == null) { createdPreview = true; - bitmap = Minimap.RenderMapPreview(modData.DefaultRules.TileSets[p.Map.Tileset], p.Map, modData.DefaultRules, true); + var map = new Map(p.Path); + bitmap = Minimap.RenderMapPreview(modData.DefaultRules.TileSets[map.Tileset], map, modData.DefaultRules, true); } Game.RunAfterTick(() => diff --git a/OpenRA.Game/Map/MapPreview.cs b/OpenRA.Game/Map/MapPreview.cs index f8c35c9b40..11b0908e3a 100644 --- a/OpenRA.Game/Map/MapPreview.cs +++ b/OpenRA.Game/Map/MapPreview.cs @@ -57,6 +57,8 @@ namespace OpenRA MapCache cache; public readonly string Uid; + public string Path { get; private set; } + public string Title { get; private set; } public string Type { get; private set; } public string Author { get; private set; } @@ -65,13 +67,11 @@ namespace OpenRA public MapGridType GridType { get; private set; } public Rectangle Bounds { get; private set; } public Bitmap CustomPreview { get; private set; } - public Map Map { get; private set; } public MapStatus Status { get; private set; } public MapClassification Class { get; private set; } + public MapVisibility Visibility { get; private set; } public bool SuitableForInitialMap { get; private set; } - public MapRuleStatus RuleStatus { get; private set; } - Download download; public long DownloadBytes { get; private set; } public int DownloadPercentage { get; private set; } @@ -111,11 +111,12 @@ namespace OpenRA GridType = gridType; Status = MapStatus.Unavailable; Class = MapClassification.Unknown; + Visibility = MapVisibility.Lobby; } public void UpdateFromMap(Map m, MapClassification classification) { - Map = m; + Path = m.Path; Title = m.Title; Type = m.Type; Type = m.Type; @@ -126,6 +127,7 @@ namespace OpenRA CustomPreview = m.CustomPreview; Status = MapStatus.Available; Class = classification; + Visibility = m.Visibility; var players = new MapPlayers(m.PlayerDefinitions).Players; PlayerCount = players.Count(x => x.Value.Playable); @@ -135,7 +137,7 @@ namespace OpenRA bool EvaluateUserFriendliness(Dictionary players) { - if (Status != MapStatus.Available || !Map.Visibility.HasFlag(MapVisibility.Lobby)) + if (Status != MapStatus.Available || !Visibility.HasFlag(MapVisibility.Lobby)) return false; // Other map types may have confusing settings or gameplay @@ -168,7 +170,6 @@ namespace OpenRA if (!r.downloading) { Status = MapStatus.Unavailable; - RuleStatus = MapRuleStatus.Invalid; return; } @@ -228,7 +229,7 @@ namespace OpenRA return; } - mapPath = Path.Combine(baseMapPath, res.Headers["Content-Disposition"].Replace("attachment; filename = ", "")); + mapPath = System.IO.Path.Combine(baseMapPath, res.Headers["Content-Disposition"].Replace("attachment; filename = ", "")); } Action onDownloadProgress = i => { DownloadBytes = i.BytesReceived; DownloadPercentage = i.ProgressPercentage; }; @@ -246,11 +247,7 @@ namespace OpenRA } Log.Write("debug", "Downloaded map to '{0}'", mapPath); - Game.RunAfterTick(() => - { - UpdateFromMap(new Map(mapPath), MapClassification.User); - CacheRules(); - }); + Game.RunAfterTick(() => UpdateFromMap(new Map(mapPath), MapClassification.User)); }; download = new Download(mapUrl, mapPath, onDownloadProgress, onDownloadComplete); @@ -272,19 +269,9 @@ namespace OpenRA download = null; } - public void CacheRules() - { - if (RuleStatus != MapRuleStatus.Unknown) - return; - - Map.PreloadRules(); - RuleStatus = Map.InvalidCustomRules ? MapRuleStatus.Invalid : MapRuleStatus.Cached; - } - public void Invalidate() { Status = MapStatus.Unavailable; - RuleStatus = MapRuleStatus.Unknown; } } } diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index e6ad5fd73f..3fee14ee7e 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -166,7 +166,7 @@ namespace OpenRA throw new InvalidDataException("Invalid map uid: {0}".F(uid)); // Operate on a copy of the map to avoid gameplay state leaking into the cache - var map = new Map(MapCache[uid].Map.Path); + var map = new Map(MapCache[uid].Path); LoadTranslations(map); diff --git a/OpenRA.Game/Widgets/WidgetUtils.cs b/OpenRA.Game/Widgets/WidgetUtils.cs index c0bc0e250c..a1fb0f0670 100644 --- a/OpenRA.Game/Widgets/WidgetUtils.cs +++ b/OpenRA.Game/Widgets/WidgetUtils.cs @@ -255,7 +255,7 @@ namespace OpenRA.Widgets if (string.IsNullOrEmpty(initialUid) || Game.ModData.MapCache[initialUid].Status != MapStatus.Available) { var selected = Game.ModData.MapCache.Where(x => x.SuitableForInitialMap).RandomOrDefault(Game.CosmeticRandom) ?? - Game.ModData.MapCache.First(m => m.Status == MapStatus.Available && m.Map.Visibility.HasFlag(MapVisibility.Lobby)); + Game.ModData.MapCache.First(m => m.Status == MapStatus.Available && m.Visibility.HasFlag(MapVisibility.Lobby)); return selected.Uid; } diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 12c695b705..21581d2c40 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -926,7 +926,7 @@ namespace OpenRA.Mods.Common.Server static void LoadMap(S server) { - server.Map = server.ModData.MapCache[server.LobbyInfo.GlobalSettings.Map].Map; + server.Map = new Map(server.ModData.MapCache[server.LobbyInfo.GlobalSettings.Map].Path); server.MapPlayers = new MapPlayers(server.Map.PlayerDefinitions); server.LobbyInfo.Slots = server.MapPlayers.Players diff --git a/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs b/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs index 57aa5497a5..e00e712621 100644 --- a/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs +++ b/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs @@ -78,7 +78,7 @@ namespace OpenRA.Mods.Common.UtilityCommands Game.ModData.MapCache.LoadMaps(); maps.AddRange(Game.ModData.MapCache .Where(m => m.Status == MapStatus.Available) - .Select(m => m.Map)); + .Select(m => new Map(m.Path))); } else maps.Add(new Map(args[1])); diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs index 99cab44484..1b35b63570 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs @@ -101,7 +101,7 @@ namespace OpenRA.Mods.Common.UtilityCommands Console.WriteLine("Processing Maps:"); var maps = Game.ModData.MapCache .Where(m => m.Status == MapStatus.Available) - .Select(m => m.Map); + .Select(m => new Map(m.Path)); foreach (var map in maps) { diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index 9f381f2e73..24978d7f22 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -26,7 +26,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic { static readonly Action DoNothing = () => { }; - public MapPreview Map = MapCache.UnknownMap; + public MapPreview MapPreview { get; private set; } + public Map Map { get; private set; } readonly Action onStart; readonly Action onExit; @@ -111,6 +112,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic internal LobbyLogic(Widget widget, WorldRenderer worldRenderer, OrderManager orderManager, Action onExit, Action onStart, bool skirmishMode, Ruleset modRules) { + MapPreview = MapCache.UnknownMap; lobby = widget; this.orderManager = orderManager; this.onStart = onStart; @@ -156,6 +158,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var gameStarting = false; Func configurationDisabled = () => !Game.IsHost || gameStarting || panel == PanelType.Kick || panel == PanelType.ForceStart || + Map == null || Map.InvalidCustomRules || orderManager.LocalClient == null || orderManager.LocalClient.IsReady; var mapButton = lobby.GetOrNull("CHANGEMAP_BUTTON"); @@ -168,7 +171,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var onSelect = new Action(uid => { // Don't select the same map again - if (uid == Map.Uid) + if (uid == MapPreview.Uid) return; orderManager.IssueOrder(Order.Command("map " + uid)); @@ -178,7 +181,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs() { - { "initialMap", Map.Uid }, + { "initialMap", MapPreview.Uid }, { "initialTab", MapClassification.System }, { "onExit", DoNothing }, { "onSelect", Game.IsHost ? onSelect : null }, @@ -190,7 +193,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var slotsButton = lobby.GetOrNull("SLOTS_DROPDOWNBUTTON"); if (slotsButton != null) { - slotsButton.IsDisabled = () => configurationDisabled() || panel != PanelType.Players || Map.RuleStatus != MapRuleStatus.Cached || + slotsButton.IsDisabled = () => configurationDisabled() || panel != PanelType.Players || (orderManager.LobbyInfo.Slots.Values.All(s => !s.AllowBots) && orderManager.LobbyInfo.Slots.Count(s => !s.Value.LockTeam && orderManager.LobbyInfo.ClientInSlot(s.Key) != null) == 0); @@ -294,7 +297,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var optionsTab = lobby.Get("OPTIONS_TAB"); optionsTab.IsHighlighted = () => panel == PanelType.Options; - optionsTab.IsDisabled = () => Map.RuleStatus != MapRuleStatus.Cached || panel == PanelType.Kick || panel == PanelType.ForceStart; + optionsTab.IsDisabled = () => Map == null || Map.InvalidCustomRules || panel == PanelType.Kick || panel == PanelType.ForceStart; optionsTab.OnClick = () => panel = PanelType.Options; var playersTab = lobby.Get("PLAYERS_TAB"); @@ -317,9 +320,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic var startGameButton = lobby.GetOrNull("START_GAME_BUTTON"); if (startGameButton != null) { - startGameButton.IsDisabled = () => configurationDisabled() || Map.RuleStatus != MapRuleStatus.Cached || + startGameButton.IsDisabled = () => configurationDisabled() || orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null) || (orderManager.LobbyInfo.GlobalSettings.DisableSingleplayer && orderManager.LobbyInfo.IsSinglePlayer); + startGameButton.OnClick = () => { // Bots and admins don't count @@ -341,7 +345,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (allowCheats != null) { allowCheats.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowCheats; - allowCheats.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Cheats.HasValue || configurationDisabled(); + allowCheats.IsDisabled = () => configurationDisabled() || Map.Options.Cheats.HasValue; allowCheats.OnClick = () => orderManager.IssueOrder(Order.Command( "allowcheats {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllowCheats))); } @@ -350,7 +354,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (crates != null) { crates.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Crates; - crates.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Crates.HasValue || configurationDisabled(); + crates.IsDisabled = () => configurationDisabled() || Map.Options.Crates.HasValue; crates.OnClick = () => orderManager.IssueOrder(Order.Command( "crates {0}".F(!orderManager.LobbyInfo.GlobalSettings.Crates))); } @@ -359,7 +363,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (creeps != null) { creeps.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Creeps; - creeps.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Creeps.HasValue || configurationDisabled(); + creeps.IsDisabled = () => configurationDisabled() || Map.Options.Creeps.HasValue; creeps.OnClick = () => orderManager.IssueOrder(Order.Command( "creeps {0}".F(!orderManager.LobbyInfo.GlobalSettings.Creeps))); } @@ -368,7 +372,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (allybuildradius != null) { allybuildradius.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius; - allybuildradius.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.AllyBuildRadius.HasValue || configurationDisabled(); + allybuildradius.IsDisabled = () => configurationDisabled() || Map.Options.AllyBuildRadius.HasValue; allybuildradius.OnClick = () => orderManager.IssueOrder(Order.Command( "allybuildradius {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius))); } @@ -377,7 +381,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (shortGame != null) { shortGame.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.ShortGame; - shortGame.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.ShortGame.HasValue || configurationDisabled(); + shortGame.IsDisabled = () => configurationDisabled() || Map.Options.ShortGame.HasValue; shortGame.OnClick = () => orderManager.IssueOrder(Order.Command( "shortgame {0}".F(!orderManager.LobbyInfo.GlobalSettings.ShortGame))); } @@ -385,12 +389,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic var difficulty = optionsBin.GetOrNull("DIFFICULTY_DROPDOWNBUTTON"); if (difficulty != null) { - difficulty.IsVisible = () => Map.Status == MapStatus.Available && Map.Map.Options.Difficulties.Any(); - difficulty.IsDisabled = () => Map.Status != MapStatus.Available || configurationDisabled(); + difficulty.IsVisible = () => Map != null && Map.Options.Difficulties.Any(); + difficulty.IsDisabled = configurationDisabled; difficulty.GetText = () => orderManager.LobbyInfo.GlobalSettings.Difficulty; difficulty.OnMouseDown = _ => { - var options = Map.Map.Options.Difficulties.Select(d => new DropDownOption + var options = Map.Options.Difficulties.Select(d => new DropDownOption { Title = d, IsSelected = () => orderManager.LobbyInfo.GlobalSettings.Difficulty == d, @@ -419,10 +423,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic return selectedClass != null ? selectedClass : c; }; - startingUnits.IsDisabled = () => Map.Status != MapStatus.Available || - !Map.Map.Options.ConfigurableStartingUnits || configurationDisabled(); - startingUnits.GetText = () => Map.Status != MapStatus.Available || - !Map.Map.Options.ConfigurableStartingUnits ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass); + startingUnits.IsDisabled = () => configurationDisabled() || !Map.Options.ConfigurableStartingUnits; + startingUnits.GetText = () => MapPreview.Status != MapStatus.Available || + Map == null || !Map.Options.ConfigurableStartingUnits ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass); startingUnits.OnMouseDown = _ => { var options = classes.Select(c => new DropDownOption @@ -448,10 +451,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic var startingCash = optionsBin.GetOrNull("STARTINGCASH_DROPDOWNBUTTON"); if (startingCash != null) { - startingCash.IsDisabled = () => Map.Status != MapStatus.Available || - Map.Map.Options.StartingCash.HasValue || configurationDisabled(); - startingCash.GetText = () => Map.Status != MapStatus.Available || - Map.Map.Options.StartingCash.HasValue ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash); + startingCash.IsDisabled = () => configurationDisabled() || Map.Options.StartingCash.HasValue; + startingCash.GetText = () => MapPreview.Status != MapStatus.Available || + Map == null || Map.Options.StartingCash.HasValue ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash); startingCash.OnMouseDown = _ => { var options = modRules.Actors["player"].TraitInfo().SelectableCash.Select(c => new DropDownOption @@ -482,10 +484,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (techLevelDescription != null) techLevelDescription.IsVisible = () => techTraits.Count > 0; - techLevel.IsDisabled = () => Map.Status != MapStatus.Available || - Map.Map.Options.TechLevel != null || configurationDisabled() || techTraits.Count <= 1; - techLevel.GetText = () => Map.Status != MapStatus.Available || - Map.Map.Options.TechLevel != null ? "Not Available" : "{0}".F(orderManager.LobbyInfo.GlobalSettings.TechLevel); + techLevel.IsDisabled = () => configurationDisabled() || Map.Options.TechLevel != null || techTraits.Count <= 1; + techLevel.GetText = () => MapPreview.Status != MapStatus.Available || + Map == null || Map.Options.TechLevel != null ? "Not Available" : "{0}".F(orderManager.LobbyInfo.GlobalSettings.TechLevel); techLevel.OnMouseDown = _ => { var options = techTraits.Select(c => new DropDownOption @@ -511,10 +512,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var speeds = Game.ModData.Manifest.Get().Speeds; - gameSpeed.IsDisabled = () => Map.Status != MapStatus.Available || configurationDisabled(); + gameSpeed.IsDisabled = configurationDisabled; gameSpeed.GetText = () => { - if (Map.Status != MapStatus.Available) + if (MapPreview.Status != MapStatus.Available) return "Not Available"; GameSpeed speed; @@ -548,7 +549,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (exploredMap != null) { exploredMap.IsChecked = () => !orderManager.LobbyInfo.GlobalSettings.Shroud; - exploredMap.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Shroud.HasValue || configurationDisabled(); + exploredMap.IsDisabled = () => configurationDisabled() || Map.Options.Shroud.HasValue; exploredMap.OnClick = () => orderManager.IssueOrder(Order.Command( "shroud {0}".F(!orderManager.LobbyInfo.GlobalSettings.Shroud))); } @@ -557,7 +558,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (enableFog != null) { enableFog.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Fog; - enableFog.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Fog.HasValue || configurationDisabled(); + enableFog.IsDisabled = () => configurationDisabled() || Map.Options.Fog.HasValue; enableFog.OnClick = () => orderManager.IssueOrder(Order.Command( "fog {0}".F(!orderManager.LobbyInfo.GlobalSettings.Fog))); } @@ -733,31 +734,32 @@ namespace OpenRA.Mods.Common.Widgets.Logic void UpdateCurrentMap() { var uid = orderManager.LobbyInfo.GlobalSettings.Map; - if (Map.Uid == uid) + if (MapPreview.Uid == uid) return; - Map = Game.ModData.MapCache[uid]; - if (Map.Status == MapStatus.Available) + MapPreview = Game.ModData.MapCache[uid]; + Map = null; + if (MapPreview.Status == MapStatus.Available) { // Maps need to be validated and pre-loaded before they can be accessed new Thread(_ => { - var map = Map; - map.CacheRules(); + var currentMap = Map = new Map(MapPreview.Path); + currentMap.PreloadRules(); Game.RunAfterTick(() => { // Map may have changed in the meantime - if (map != Map) + if (currentMap != Map) return; - if (map.RuleStatus != MapRuleStatus.Invalid) + if (!currentMap.InvalidCustomRules) { // Tell the server that we have the map orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady))); // Restore default starting cash if the last map set it to something invalid var pri = modRules.Actors["player"].TraitInfo(); - if (!Map.Map.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash)) + if (!currentMap.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash)) orderManager.IssueOrder(Order.Command("startingcash {0}".F(pri.DefaultCash))); } }); @@ -816,9 +818,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, shellmapWorld, colorPreview); LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, factions); - LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, Map); - LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, Map); - LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, Map); + LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, MapPreview); + LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, MapPreview); + LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, MapPreview, Map == null || Map.InvalidCustomRules); } else { diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyMapPreviewLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyMapPreviewLogic.cs index 5c0952ef05..74dca76b8e 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyMapPreviewLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyMapPreviewLogic.cs @@ -25,79 +25,80 @@ namespace OpenRA.Mods.Common.Widgets.Logic var available = widget.GetOrNull("MAP_AVAILABLE"); if (available != null) { - available.IsVisible = () => lobby.Map.Status == MapStatus.Available && lobby.Map.RuleStatus == MapRuleStatus.Cached; + available.IsVisible = () => lobby.MapPreview.Status == MapStatus.Available && (lobby.Map == null || !lobby.Map.InvalidCustomRules); var preview = available.Get("MAP_PREVIEW"); - preview.Preview = () => lobby.Map; - preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi); - preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map); + preview.Preview = () => lobby.MapPreview; + preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); + preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); var titleLabel = available.GetOrNull("MAP_TITLE"); if (titleLabel != null) { var font = Game.Renderer.Fonts[titleLabel.Font]; var title = new CachedTransform(m => WidgetUtils.TruncateText(m.Title, titleLabel.Bounds.Width, font)); - titleLabel.GetText = () => title.Update(lobby.Map); + titleLabel.GetText = () => title.Update(lobby.MapPreview); } var typeLabel = available.GetOrNull("MAP_TYPE"); if (typeLabel != null) - typeLabel.GetText = () => lobby.Map.Type; + typeLabel.GetText = () => lobby.MapPreview.Type; var authorLabel = available.GetOrNull("MAP_AUTHOR"); if (authorLabel != null) { var font = Game.Renderer.Fonts[authorLabel.Font]; - var author = new CachedTransform(m => WidgetUtils.TruncateText("Created by {0}".F(lobby.Map.Author), authorLabel.Bounds.Width, font)); - authorLabel.GetText = () => author.Update(lobby.Map); + var author = new CachedTransform( + m => WidgetUtils.TruncateText("Created by {0}".F(lobby.MapPreview.Author), authorLabel.Bounds.Width, font)); + authorLabel.GetText = () => author.Update(lobby.MapPreview); } } var invalid = widget.GetOrNull("MAP_INVALID"); if (invalid != null) { - invalid.IsVisible = () => lobby.Map.Status == MapStatus.Available && lobby.Map.RuleStatus == MapRuleStatus.Invalid; + invalid.IsVisible = () => lobby.MapPreview.Status == MapStatus.Available && lobby.Map != null && lobby.Map.InvalidCustomRules; var preview = invalid.Get("MAP_PREVIEW"); - preview.Preview = () => lobby.Map; - preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi); - preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map); + preview.Preview = () => lobby.MapPreview; + preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); + preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); var title = invalid.GetOrNull("MAP_TITLE"); if (title != null) - title.GetText = () => lobby.Map.Title; + title.GetText = () => lobby.MapPreview.Title; var type = invalid.GetOrNull("MAP_TYPE"); if (type != null) - type.GetText = () => lobby.Map.Type; + type.GetText = () => lobby.MapPreview.Type; } var download = widget.GetOrNull("MAP_DOWNLOADABLE"); if (download != null) { - download.IsVisible = () => lobby.Map.Status == MapStatus.DownloadAvailable; + download.IsVisible = () => lobby.MapPreview.Status == MapStatus.DownloadAvailable; var preview = download.Get("MAP_PREVIEW"); - preview.Preview = () => lobby.Map; - preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi); - preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map); + preview.Preview = () => lobby.MapPreview; + preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); + preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); var title = download.GetOrNull("MAP_TITLE"); if (title != null) - title.GetText = () => lobby.Map.Title; + title.GetText = () => lobby.MapPreview.Title; var type = download.GetOrNull("MAP_TYPE"); if (type != null) - type.GetText = () => lobby.Map.Type; + type.GetText = () => lobby.MapPreview.Type; var author = download.GetOrNull("MAP_AUTHOR"); if (author != null) - author.GetText = () => "Created by {0}".F(lobby.Map.Author); + author.GetText = () => "Created by {0}".F(lobby.MapPreview.Author); var install = download.GetOrNull("MAP_INSTALL"); if (install != null) { - install.OnClick = () => lobby.Map.Install(); + install.OnClick = () => lobby.MapPreview.Install(); install.IsHighlighted = () => installHighlighted; } } @@ -105,72 +106,72 @@ namespace OpenRA.Mods.Common.Widgets.Logic var progress = widget.GetOrNull("MAP_PROGRESS"); if (progress != null) { - progress.IsVisible = () => - (lobby.Map.Status != MapStatus.Available || lobby.Map.RuleStatus == MapRuleStatus.Unknown) && - lobby.Map.Status != MapStatus.DownloadAvailable; + progress.IsVisible = () => lobby.MapPreview.Status != MapStatus.Available && + lobby.MapPreview.Status != MapStatus.DownloadAvailable; var preview = progress.Get("MAP_PREVIEW"); - preview.Preview = () => lobby.Map; - preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi); - preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map); + preview.Preview = () => lobby.MapPreview; + preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); + preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); var title = progress.GetOrNull("MAP_TITLE"); if (title != null) - title.GetText = () => lobby.Map.Title; + title.GetText = () => lobby.MapPreview.Title; var type = progress.GetOrNull("MAP_TYPE"); if (type != null) - type.GetText = () => lobby.Map.Type; + type.GetText = () => lobby.MapPreview.Type; var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING"); if (statusSearching != null) - statusSearching.IsVisible = () => lobby.Map.Status == MapStatus.Searching; + statusSearching.IsVisible = () => lobby.MapPreview.Status == MapStatus.Searching; var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE"); if (statusUnavailable != null) - statusUnavailable.IsVisible = () => lobby.Map.Status == MapStatus.Unavailable; + statusUnavailable.IsVisible = () => lobby.MapPreview.Status == MapStatus.Unavailable; var statusError = progress.GetOrNull("MAP_STATUS_ERROR"); if (statusError != null) - statusError.IsVisible = () => lobby.Map.Status == MapStatus.DownloadError; + statusError.IsVisible = () => lobby.MapPreview.Status == MapStatus.DownloadError; var statusDownloading = progress.GetOrNull("MAP_STATUS_DOWNLOADING"); if (statusDownloading != null) { - statusDownloading.IsVisible = () => lobby.Map.Status == MapStatus.Downloading; + statusDownloading.IsVisible = () => lobby.MapPreview.Status == MapStatus.Downloading; statusDownloading.GetText = () => { - if (lobby.Map.DownloadBytes == 0) + if (lobby.MapPreview.DownloadBytes == 0) return "Connecting..."; // Server does not provide the total file length - if (lobby.Map.DownloadPercentage == 0) - return "Downloading {0} kB".F(lobby.Map.DownloadBytes / 1024); + if (lobby.MapPreview.DownloadPercentage == 0) + return "Downloading {0} kB".F(lobby.MapPreview.DownloadBytes / 1024); - return "Downloading {0} kB ({1}%)".F(lobby.Map.DownloadBytes / 1024, lobby.Map.DownloadPercentage); + return "Downloading {0} kB ({1}%)".F(lobby.MapPreview.DownloadBytes / 1024, lobby.MapPreview.DownloadPercentage); }; } var retry = progress.GetOrNull("MAP_RETRY"); if (retry != null) { - retry.IsVisible = () => (lobby.Map.Status == MapStatus.DownloadError || lobby.Map.Status == MapStatus.Unavailable) && lobby.Map != MapCache.UnknownMap; + retry.IsVisible = () => (lobby.MapPreview.Status == MapStatus.DownloadError || lobby.MapPreview.Status == MapStatus.Unavailable) && + lobby.MapPreview != MapCache.UnknownMap; retry.OnClick = () => { - if (lobby.Map.Status == MapStatus.DownloadError) - lobby.Map.Install(); - else if (lobby.Map.Status == MapStatus.Unavailable) - Game.ModData.MapCache.QueryRemoteMapDetails(new[] { lobby.Map.Uid }); + if (lobby.MapPreview.Status == MapStatus.DownloadError) + lobby.MapPreview.Install(); + else if (lobby.MapPreview.Status == MapStatus.Unavailable) + Game.ModData.MapCache.QueryRemoteMapDetails(new[] { lobby.MapPreview.Uid }); }; - retry.GetText = () => lobby.Map.Status == MapStatus.DownloadError ? "Retry Install" : "Retry Search"; + retry.GetText = () => lobby.MapPreview.Status == MapStatus.DownloadError ? "Retry Install" : "Retry Search"; } var progressbar = progress.GetOrNull("MAP_PROGRESSBAR"); if (progressbar != null) { - progressbar.IsIndeterminate = () => lobby.Map.DownloadPercentage == 0; - progressbar.GetPercentage = () => lobby.Map.DownloadPercentage; + progressbar.IsIndeterminate = () => lobby.MapPreview.DownloadPercentage == 0; + progressbar.GetPercentage = () => lobby.MapPreview.DownloadPercentage; progressbar.IsVisible = () => !retry.IsVisible(); } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs index 27b88dbabf..672f7621bb 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs @@ -455,12 +455,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic parent.Get("SPAWN").GetText = () => (c.SpawnPoint == 0) ? "-" : Convert.ToChar('A' - 1 + c.SpawnPoint).ToString(); } - public static void SetupEditableReadyWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map) + public static void SetupEditableReadyWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map, bool forceDisable) { var status = parent.Get("STATUS_CHECKBOX"); status.IsChecked = () => orderManager.LocalClient.IsReady || c.Bot != null; status.IsVisible = () => true; - status.IsDisabled = () => c.Bot != null || map.Status != MapStatus.Available || map.RuleStatus != MapRuleStatus.Cached; + status.IsDisabled = () => c.Bot != null || map.Status != MapStatus.Available || forceDisable; var state = orderManager.LocalClient.IsReady ? Session.ClientState.NotReady : Session.ClientState.Ready; status.OnClick = () => orderManager.IssueOrder(Order.Command("state {0}".F(state))); diff --git a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs index 7899d345a3..65afcae84c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs @@ -104,7 +104,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var hasCampaign = Game.ModData.Manifest.Missions.Any(); var hasMissions = Game.ModData.MapCache - .Any(p => p.Status == MapStatus.Available && p.Map.Visibility.HasFlag(MapVisibility.MissionSelector)); + .Any(p => p.Status == MapStatus.Available && p.Visibility.HasFlag(MapVisibility.MissionSelector)); missionsButton.Disabled = !hasCampaign && !hasMissions; @@ -167,7 +167,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var onSelect = new Action(uid => { RemoveShellmapUI(); - LoadMapIntoEditor(Game.ModData.MapCache[uid].Map); + LoadMapIntoEditor(Game.ModData.MapCache[uid].Uid); }); var newMapButton = widget.Get("NEW_MAP_BUTTON"); @@ -242,12 +242,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic }); } - void LoadMapIntoEditor(Map map) + void LoadMapIntoEditor(string uid) { ConnectionLogic.Connect(IPAddress.Loopback.ToString(), - Game.CreateLocalServer(map.Uid), + Game.CreateLocalServer(uid), "", - () => { Game.LoadEditor(map.Uid); }, + () => { Game.LoadEditor(uid); }, () => { Game.CloseServer(); SwitchMenu(MenuType.MapEditor); }); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs index fa205533c8..2a8b44e900 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs @@ -143,7 +143,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic void RefreshMaps(MapClassification tab, MapVisibility filter) { tabMaps[tab] = Game.ModData.MapCache.Where(m => m.Status == MapStatus.Available && - m.Class == tab && (m.Map.Visibility & filter) != 0).ToArray(); + m.Class == tab && (m.Visibility & filter) != 0).ToArray(); } void SetupMapTab(MapClassification tab, MapVisibility filter, string tabButtonName, string tabContainerName, ScrollItemWidget itemTemplate) @@ -284,7 +284,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic string DeleteMap(string map) { - var path = Game.ModData.MapCache[map].Map.Path; + var path = Game.ModData.MapCache[map].Path; try { if (File.Exists(path)) diff --git a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs index b87ce9c3d7..a7c3f78db4 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs @@ -14,6 +14,7 @@ using System.IO; using System.Linq; using System.Threading; using OpenRA.Graphics; +using OpenRA.Primitives; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic @@ -38,8 +39,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic readonly ScrollPanelWidget missionList; readonly ScrollItemWidget headerTemplate; readonly ScrollItemWidget template; + readonly Cache mapCache = new Cache(p => new Map(p.Path)); MapPreview selectedMapPreview; + Map selectedMap; PlayingVideo playingVideo; @@ -89,7 +92,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic stopInfoVideoButton.IsVisible = () => playingVideo == PlayingVideo.Info; stopInfoVideoButton.OnClick = () => StopVideo(videoPlayer); - var allMaps = new List(); + var allPreviews = new List(); missionList.RemoveChildren(); // Add a group for each campaign @@ -102,33 +105,38 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var missionMapPaths = kv.Value.Nodes.Select(n => Path.GetFullPath(n.Key)).ToList(); - var maps = modData.MapCache - .Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(Path.GetFullPath(p.Map.Path))) - .Select(p => p.Map) - .OrderBy(m => missionMapPaths.IndexOf(Path.GetFullPath(m.Path))); + var previews = modData.MapCache + .Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(Path.GetFullPath(p.Path))) + .OrderBy(p => missionMapPaths.IndexOf(Path.GetFullPath(p.Path))); - CreateMissionGroup(kv.Key, maps); - allMaps.AddRange(maps); + CreateMissionGroup(kv.Key, previews); + allPreviews.AddRange(previews); } } // Add an additional group for loose missions - var looseMissions = modData.MapCache - .Where(p => p.Status == MapStatus.Available && p.Map.Visibility.HasFlag(MapVisibility.MissionSelector) && !allMaps.Contains(p.Map)) - .Select(p => p.Map); + var loosePreviews = modData.MapCache + .Where(p => p.Status == MapStatus.Available && p.Visibility.HasFlag(MapVisibility.MissionSelector) && !allPreviews.Any(a => a.Uid == p.Uid)); - if (looseMissions.Any()) + if (loosePreviews.Any()) { - CreateMissionGroup("Missions", looseMissions); - allMaps.AddRange(looseMissions); + CreateMissionGroup("Missions", loosePreviews); + allPreviews.AddRange(loosePreviews); } - if (allMaps.Any()) - SelectMap(allMaps.First()); + if (allPreviews.Any()) + SelectMap(allPreviews.First()); + + // Preload map and preview data to reduce jank + new Thread(() => + { + foreach (var p in allPreviews) + modData.MapCache[mapCache[p].Uid].GetMinimap(); + }).Start(); var startButton = widget.Get("STARTGAME_BUTTON"); startButton.OnClick = StartMissionClicked; - startButton.IsDisabled = () => selectedMapPreview == null || selectedMapPreview.RuleStatus != MapRuleStatus.Cached; + startButton.IsDisabled = () => selectedMap == null || selectedMap.InvalidCustomRules; widget.Get("BACK_BUTTON").OnClick = () => { @@ -139,38 +147,39 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; } - void CreateMissionGroup(string title, IEnumerable maps) + void CreateMissionGroup(string title, IEnumerable previews) { var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => { }); header.Get("LABEL").GetText = () => title; missionList.AddChild(header); - foreach (var m in maps) + foreach (var p in previews) { - var map = m; + var preview = p; var item = ScrollItemWidget.Setup(template, - () => selectedMapPreview != null && selectedMapPreview.Uid == map.Uid, - () => SelectMap(map), + () => selectedMapPreview != null && selectedMapPreview.Uid == preview.Uid, + () => SelectMap(preview), StartMissionClicked); - item.Get("TITLE").GetText = () => map.Title; + item.Get("TITLE").GetText = () => preview.Title; missionList.AddChild(item); } } - void SelectMap(Map map) + void SelectMap(MapPreview preview) { - selectedMapPreview = Game.ModData.MapCache[map.Uid]; + selectedMap = mapCache[preview]; + selectedMapPreview = preview; // Cache the rules on a background thread to avoid jank - new Thread(selectedMapPreview.CacheRules).Start(); + new Thread(() => selectedMap.PreloadRules()).Start(); - var briefingVideo = selectedMapPreview.Map.Videos.Briefing; + var briefingVideo = selectedMap.Videos.Briefing; var briefingVideoVisible = briefingVideo != null; var briefingVideoDisabled = !(briefingVideoVisible && Game.ModData.ModFiles.Exists(briefingVideo)); - var infoVideo = selectedMapPreview.Map.Videos.BackgroundInfo; + var infoVideo = selectedMap.Videos.BackgroundInfo; var infoVideoVisible = infoVideo != null; var infoVideoDisabled = !(infoVideoVisible && Game.ModData.ModFiles.Exists(infoVideo)); @@ -182,7 +191,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic startInfoVideoButton.IsDisabled = () => infoVideoDisabled || playingVideo != PlayingVideo.None; startInfoVideoButton.OnClick = () => PlayVideo(videoPlayer, infoVideo, PlayingVideo.Info, () => StopVideo(videoPlayer)); - var text = map.Description != null ? map.Description.Replace("\\n", "\n") : ""; + var text = selectedMap.Description != null ? selectedMap.Description.Replace("\\n", "\n") : ""; text = WidgetUtils.WrapText(text, description.Bounds.Width, descriptionFont); description.Text = text; description.Bounds.Height = descriptionFont.Measure(text).Y; @@ -191,13 +200,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (difficultyButton != null) { - difficultyButton.IsDisabled = () => !map.Options.Difficulties.Any(); + difficultyButton.IsDisabled = () => !selectedMap.Options.Difficulties.Any(); - difficulty = map.Options.Difficulties.FirstOrDefault(); + difficulty = selectedMap.Options.Difficulties.FirstOrDefault(); difficultyButton.GetText = () => difficulty ?? "Normal"; difficultyButton.OnMouseDown = _ => { - var options = map.Options.Difficulties.Select(d => new DropDownOption + var options = selectedMap.Options.Difficulties.Select(d => new DropDownOption { Title = d, IsSelected = () => difficulty == d, @@ -288,10 +297,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic { StopVideo(videoPlayer); - if (selectedMapPreview.RuleStatus != MapRuleStatus.Cached) + if (selectedMap.InvalidCustomRules) return; - var gameStartVideo = selectedMapPreview.Map.Videos.GameStart; + var gameStartVideo = selectedMap.Videos.GameStart; if (gameStartVideo != null && Game.ModData.ModFiles.Exists(gameStartVideo)) { var fsPlayer = fullscreenVideoPlayer.Get("PLAYER");