From 02ab54c9bce656a6c149a5e12a0cd362c50e2a43 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 6 Feb 2016 12:37:19 +0000 Subject: [PATCH] Load MapPreview data without initialising a full Map. --- OpenRA.Game/Map/MapCache.cs | 13 ++- OpenRA.Game/Map/MapPreview.cs | 104 +++++++++++++++--- .../Widgets/Logic/Editor/SaveMapLogic.cs | 2 +- 3 files changed, 98 insertions(+), 21 deletions(-) diff --git a/OpenRA.Game/Map/MapCache.cs b/OpenRA.Game/Map/MapCache.cs index 4cf460c0aa..d936a89282 100644 --- a/OpenRA.Game/Map/MapCache.cs +++ b/OpenRA.Game/Map/MapCache.cs @@ -44,20 +44,27 @@ namespace OpenRA public void LoadMaps() { + // Utility mod that does not support maps + if (!modData.Manifest.Contains()) + return; + // Expand the dictionary (dir path, dir type) to a dictionary of (map path, dir type) var mapPaths = modData.Manifest.MapFolders.SelectMany(kv => FindMapsIn(modData.ModFiles, kv.Key).ToDictionary(p => p, p => string.IsNullOrEmpty(kv.Value) ? MapClassification.Unknown : Enum.Parse(kv.Value))); + var mapGrid = modData.Manifest.Get(); foreach (var path in mapPaths) { try { using (new Support.PerfTimer(path.Key)) { - var map = new Map(path.Key); - if (modData.Manifest.MapCompatibility.Contains(map.RequiresMod)) - previews[map.Uid].UpdateFromMap(map, path.Value); + using (var package = modData.ModFiles.OpenPackage(path.Key)) + { + var uid = Map.ComputeUID(package); + previews[uid].UpdateFromMap(package, path.Value, modData.Manifest.MapCompatibility, mapGrid.Type); + } } } catch (Exception e) diff --git a/OpenRA.Game/Map/MapPreview.cs b/OpenRA.Game/Map/MapPreview.cs index 11b0908e3a..e2fbd42831 100644 --- a/OpenRA.Game/Map/MapPreview.cs +++ b/OpenRA.Game/Map/MapPreview.cs @@ -17,6 +17,7 @@ using System.IO; using System.Linq; using System.Net; using System.Threading; +using OpenRA.FileSystem; using OpenRA.Graphics; namespace OpenRA @@ -92,7 +93,7 @@ namespace OpenRA return null; } - public void SetMinimap(Sprite minimap) + internal void SetMinimap(Sprite minimap) { this.minimap = minimap; generatingMinimap = false; @@ -114,25 +115,89 @@ namespace OpenRA Visibility = MapVisibility.Lobby; } - public void UpdateFromMap(Map m, MapClassification classification) + public void UpdateFromMap(IReadOnlyPackage p, MapClassification classification, string[] mapCompatibility, MapGridType gridType) { - Path = m.Path; - Title = m.Title; - Type = m.Type; - Type = m.Type; - Author = m.Author; - Bounds = m.Bounds; - SpawnPoints = m.SpawnPoints.Value; - GridType = m.Grid.Type; - CustomPreview = m.CustomPreview; - Status = MapStatus.Available; + Dictionary yaml; + using (var yamlStream = p.GetStream("map.yaml")) + { + if (yamlStream == null) + throw new FileNotFoundException("Required file map.yaml not present in this map"); + + yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, "map.yaml")).ToDictionary(); + } + + Path = p.Name; + GridType = gridType; Class = classification; - Visibility = m.Visibility; - var players = new MapPlayers(m.PlayerDefinitions).Players; - PlayerCount = players.Count(x => x.Value.Playable); + MiniYaml temp; + if (yaml.TryGetValue("MapFormat", out temp)) + { + var format = FieldLoader.GetValue("MapFormat", temp.Value); + if (format != Map.SupportedMapFormat) + throw new InvalidDataException("Map format {0} is not supported.".F(format)); + } - SuitableForInitialMap = EvaluateUserFriendliness(players); + if (yaml.TryGetValue("Title", out temp)) + Title = temp.Value; + if (yaml.TryGetValue("Type", out temp)) + Type = temp.Value; + if (yaml.TryGetValue("Author", out temp)) + Author = temp.Value; + if (yaml.TryGetValue("Bounds", out temp)) + Bounds = FieldLoader.GetValue("Bounds", temp.Value); + if (yaml.TryGetValue("Visibility", out temp)) + Visibility = FieldLoader.GetValue("Visibility", temp.Value); + + string requiresMod = string.Empty; + if (yaml.TryGetValue("RequiresMod", out temp)) + requiresMod = temp.Value; + + Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ? MapStatus.Available : MapStatus.Unavailable; + + try + { + // Actor definitions may change if the map format changes + MiniYaml actorDefinitions; + if (yaml.TryGetValue("Actors", out actorDefinitions)) + { + var spawns = new List(); + foreach (var kv in actorDefinitions.Nodes.Where(d => d.Value.Value == "mpspawn")) + { + var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary()); + spawns.Add(s.InitDict.Get().Value(null)); + } + + SpawnPoints = spawns.ToArray(); + } + else + SpawnPoints = new CPos[0]; + } + catch (Exception) + { + SpawnPoints = new CPos[0]; + Status = MapStatus.Unavailable; + } + + try + { + // Player definitions may change if the map format changes + MiniYaml playerDefinitions; + if (yaml.TryGetValue("Players", out playerDefinitions)) + { + var players = new MapPlayers(playerDefinitions.Nodes).Players; + PlayerCount = players.Count(x => x.Value.Playable); + SuitableForInitialMap = EvaluateUserFriendliness(players); + } + } + catch (Exception) + { + Status = MapStatus.Unavailable; + } + + if (p.Contains("map.png")) + using (var dataStream = p.GetStream("map.png")) + CustomPreview = new Bitmap(dataStream); } bool EvaluateUserFriendliness(Dictionary players) @@ -210,6 +275,7 @@ namespace OpenRA if (!Directory.Exists(baseMapPath)) Directory.CreateDirectory(baseMapPath); + var modData = Game.ModData; new Thread(() => { // Request the filename from the server @@ -247,7 +313,11 @@ namespace OpenRA } Log.Write("debug", "Downloaded map to '{0}'", mapPath); - Game.RunAfterTick(() => UpdateFromMap(new Map(mapPath), MapClassification.User)); + Game.RunAfterTick(() => + { + using (var package = modData.ModFiles.OpenPackage(mapPath)) + UpdateFromMap(package, MapClassification.User, null, GridType); + }); }; download = new Download(mapUrl, mapPath, onDownloadProgress, onDownloadComplete); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs index f9046f9592..d9bfda3d8a 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs @@ -168,7 +168,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic // Update the map cache so it can be loaded without restarting the game var classification = mapDirectories[directoryDropdown.Text]; - modData.MapCache[map.Uid].UpdateFromMap(map, classification); + modData.MapCache[map.Uid].UpdateFromMap(map.Container, classification, null, map.Grid.Type); Console.WriteLine("Saved current map at {0}", combinedPath); Ui.CloseWindow();