Rework MapCache API.
This clarifies the ownership of the underlying IReadOnlyPackage objects, and avoids passing around some unnecessary state.
This commit is contained in:
committed by
Gustas Kažukauskas
parent
5c0b82e65b
commit
e186dc428e
@@ -978,7 +978,7 @@ namespace OpenRA
|
||||
Order.Command($"state {Session.ClientState.Ready}")
|
||||
};
|
||||
|
||||
var map = ModData.MapCache.SingleOrDefault(m => m.Uid == launchMap || Path.GetFileName(m.PackageName) == launchMap);
|
||||
var map = ModData.MapCache.SingleOrDefault(m => m.Uid == launchMap || Path.GetFileName(m.Path) == launchMap);
|
||||
if (map == null)
|
||||
throw new ArgumentException($"Could not find map '{launchMap}'.");
|
||||
|
||||
|
||||
@@ -87,8 +87,6 @@ namespace OpenRA
|
||||
if (!modData.Manifest.Contains<MapGrid>())
|
||||
return;
|
||||
|
||||
var mapGrid = modData.Manifest.Get<MapGrid>();
|
||||
|
||||
// Enumerate map directories
|
||||
foreach (var kv in modData.Manifest.MapFolders)
|
||||
{
|
||||
@@ -120,28 +118,29 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
mapLocations.Add(package, classification);
|
||||
mapDirectoryTrackers.Add(new MapDirectoryTracker(mapGrid, package, classification));
|
||||
mapDirectoryTrackers.Add(new MapDirectoryTracker(package, classification));
|
||||
}
|
||||
|
||||
// PERF: Load the mod YAML once outside the loop, and reuse it when resolving each maps custom YAML.
|
||||
var modDataRules = modData.GetRulesYaml();
|
||||
var gridType = modData.Manifest.Get<MapGrid>().Type;
|
||||
foreach (var kv in MapLocations)
|
||||
{
|
||||
foreach (var map in kv.Key.Contents)
|
||||
LoadMapInternal(map, kv.Key, kv.Value, mapGrid, null, modDataRules);
|
||||
LoadMapInternal(map, kv.Key, kv.Value, null, gridType, modDataRules);
|
||||
}
|
||||
|
||||
// We only want to track maps in runtime, not at loadtime
|
||||
LastModifiedMap = null;
|
||||
}
|
||||
|
||||
public void LoadMap(string map, IReadOnlyPackage package, MapClassification classification, MapGrid mapGrid, string oldMap)
|
||||
public void LoadMap(string map, IReadOnlyPackage package, MapClassification classification, string oldMap)
|
||||
{
|
||||
LoadMapInternal(map, package, classification, mapGrid, oldMap, null);
|
||||
LoadMapInternal(map, package, classification, oldMap);
|
||||
}
|
||||
|
||||
void LoadMapInternal(string map, IReadOnlyPackage package, MapClassification classification, MapGrid mapGrid, string oldMap,
|
||||
IEnumerable<List<MiniYamlNode>> modDataRules)
|
||||
void LoadMapInternal(string map, IReadOnlyPackage package, MapClassification classification, string oldMap,
|
||||
MapGridType? gridType = null, IEnumerable<List<MiniYamlNode>> modDataRules = null)
|
||||
{
|
||||
IReadOnlyPackage mapPackage = null;
|
||||
try
|
||||
@@ -152,10 +151,8 @@ namespace OpenRA
|
||||
if (mapPackage != null)
|
||||
{
|
||||
var uid = Map.ComputeUID(mapPackage);
|
||||
previews[uid].UpdateFromMap(mapPackage, package, classification, modData.Manifest.MapCompatibility, mapGrid.Type, modDataRules);
|
||||
|
||||
// Freeing the package to save memory if there is a lot of Maps
|
||||
previews[uid].DisposePackage();
|
||||
previews[uid].UpdateFromMapWithoutOwningPackage(mapPackage, package, classification, gridType, modDataRules);
|
||||
mapPackage.Dispose();
|
||||
|
||||
if (oldMap != uid)
|
||||
{
|
||||
@@ -257,7 +254,7 @@ namespace OpenRA
|
||||
|
||||
var yaml = MiniYaml.FromStream(result, url, stringPool: stringPool);
|
||||
foreach (var kv in yaml)
|
||||
previews[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, modData.Manifest.MapCompatibility, mapDetailsReceived);
|
||||
previews[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, mapDetailsReceived);
|
||||
|
||||
foreach (var uid in batchUids)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace OpenRA
|
||||
public sealed class MapDirectoryTracker : IDisposable
|
||||
{
|
||||
readonly FileSystemWatcher watcher;
|
||||
readonly MapGrid mapGrid;
|
||||
readonly IReadOnlyPackage package;
|
||||
readonly MapClassification classification;
|
||||
|
||||
@@ -29,17 +28,16 @@ namespace OpenRA
|
||||
|
||||
bool dirty = false;
|
||||
|
||||
public MapDirectoryTracker(MapGrid mapGrid, IReadOnlyPackage package, MapClassification classification)
|
||||
public MapDirectoryTracker(IReadOnlyPackage package, MapClassification classification)
|
||||
{
|
||||
this.mapGrid = mapGrid;
|
||||
this.package = package;
|
||||
this.classification = classification;
|
||||
|
||||
watcher = new FileSystemWatcher(package.Name);
|
||||
watcher.Changed += (object sender, FileSystemEventArgs e) => AddMapAction(MapAction.Update, e.FullPath);
|
||||
watcher.Created += (object sender, FileSystemEventArgs e) => AddMapAction(MapAction.Add, e.FullPath);
|
||||
watcher.Deleted += (object sender, FileSystemEventArgs e) => AddMapAction(MapAction.Delete, e.FullPath);
|
||||
watcher.Renamed += (object sender, RenamedEventArgs e) => AddMapAction(MapAction.Add, e.FullPath, e.OldFullPath);
|
||||
watcher.Changed += (_, e) => AddMapAction(MapAction.Update, e.FullPath);
|
||||
watcher.Created += (_, e) => AddMapAction(MapAction.Add, e.FullPath);
|
||||
watcher.Deleted += (_, e) => AddMapAction(MapAction.Delete, e.FullPath);
|
||||
watcher.Renamed += (_, e) => AddMapAction(MapAction.Add, e.FullPath, e.OldFullPath);
|
||||
|
||||
watcher.IncludeSubdirectories = true;
|
||||
watcher.EnableRaisingEvents = true;
|
||||
@@ -86,7 +84,7 @@ namespace OpenRA
|
||||
dirty = false;
|
||||
foreach (var mapAction in mapActionQueue)
|
||||
{
|
||||
var map = mapcache.FirstOrDefault(x => x.PackageName == mapAction.Key && x.Status == MapStatus.Available);
|
||||
var map = mapcache.FirstOrDefault(x => x.Path == mapAction.Key && x.Status == MapStatus.Available);
|
||||
if (map != null)
|
||||
{
|
||||
if (mapAction.Value == MapAction.Delete)
|
||||
@@ -98,7 +96,7 @@ namespace OpenRA
|
||||
{
|
||||
Console.WriteLine(mapAction.Key + " was updated");
|
||||
map.Invalidate();
|
||||
mapcache.LoadMap(mapAction.Key.Replace(package.Name + Path.DirectorySeparatorChar, ""), package, classification, mapGrid, map.Uid);
|
||||
mapcache.LoadMap(mapAction.Key.Replace(package.Name + Path.DirectorySeparatorChar, ""), package, classification, map.Uid);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -106,7 +104,7 @@ namespace OpenRA
|
||||
if (mapAction.Value != MapAction.Delete)
|
||||
{
|
||||
Console.WriteLine(mapAction.Key + " was added");
|
||||
mapcache.LoadMap(mapAction.Key.Replace(package?.Name + Path.DirectorySeparatorChar, ""), package, classification, mapGrid, null);
|
||||
mapcache.LoadMap(mapAction.Key.Replace(package?.Name + Path.DirectorySeparatorChar, ""), package, classification, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,19 +198,23 @@ namespace OpenRA
|
||||
static readonly CPos[] NoSpawns = Array.Empty<CPos>();
|
||||
readonly MapCache cache;
|
||||
readonly ModData modData;
|
||||
IReadOnlyPackage package;
|
||||
|
||||
public readonly string Uid;
|
||||
public string PackageName { get; private set; }
|
||||
IReadOnlyPackage package;
|
||||
public IReadOnlyPackage Package
|
||||
{
|
||||
get
|
||||
{
|
||||
package ??= parentPackage.OpenPackage(PackageName, modData.ModFiles);
|
||||
return package;
|
||||
}
|
||||
|
||||
private set => package = value;
|
||||
public string Path { get; private set; }
|
||||
|
||||
void LoadPackage()
|
||||
{
|
||||
if (package == null && parentPackage != null)
|
||||
package = parentPackage.OpenPackage(Path, modData.ModFiles);
|
||||
}
|
||||
|
||||
public Map ToMap()
|
||||
{
|
||||
LoadPackage();
|
||||
using (new PerfTimer("Map"))
|
||||
return new Map(modData, package);
|
||||
}
|
||||
|
||||
IReadOnlyPackage parentPackage;
|
||||
@@ -317,9 +321,28 @@ namespace OpenRA
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification,
|
||||
string[] mapCompatibility, MapGridType gridType, IEnumerable<List<MiniYamlNode>> modDataRules)
|
||||
/// <summary>
|
||||
/// Updates internal state from a map without taking ownership of its package.
|
||||
/// A new copy of the map package will be opened lazily when needed.
|
||||
/// </summary>
|
||||
public void UpdateFromMapWithoutOwningPackage(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification,
|
||||
MapGridType? gridType = null, IEnumerable<List<MiniYamlNode>> modDataRules = null)
|
||||
{
|
||||
UpdateFromMap(p, classification, gridType, modDataRules);
|
||||
parentPackage = parent;
|
||||
package = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates internal state from a map and takes ownership of its package.
|
||||
/// The package remains in memory and must not be disposed.
|
||||
/// </summary>
|
||||
public void UpdateFromMap(IReadOnlyPackage p, MapClassification classification,
|
||||
MapGridType? gridType = null, IEnumerable<List<MiniYamlNode>> modDataRules = null)
|
||||
{
|
||||
Path = p.Name;
|
||||
package = p;
|
||||
|
||||
Dictionary<string, MiniYaml> yaml;
|
||||
using (var yamlStream = p.GetStream("map.yaml"))
|
||||
{
|
||||
@@ -329,12 +352,9 @@ namespace OpenRA
|
||||
yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, $"{p.Name}:map.yaml", stringPool: cache.StringPool)).ToDictionary();
|
||||
}
|
||||
|
||||
PackageName = p.Name;
|
||||
parentPackage = parent;
|
||||
|
||||
var newData = innerData.Clone();
|
||||
newData.GridType = gridType;
|
||||
newData.Class = classification;
|
||||
newData.GridType = gridType ?? modData.Manifest.Get<MapGrid>().Type;
|
||||
|
||||
if (yaml.TryGetValue("MapFormat", out var temp))
|
||||
{
|
||||
@@ -368,7 +388,7 @@ namespace OpenRA
|
||||
if (yaml.TryGetValue("MapFormat", out temp))
|
||||
newData.MapFormat = FieldLoader.GetValue<int>("MapFormat", temp.Value);
|
||||
|
||||
newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ?
|
||||
newData.Status = modData.Manifest.MapCompatibility.Contains(requiresMod) ?
|
||||
MapStatus.Available : MapStatus.Unavailable;
|
||||
|
||||
try
|
||||
@@ -420,7 +440,7 @@ namespace OpenRA
|
||||
innerData = newData;
|
||||
}
|
||||
|
||||
public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml, string[] mapCompatibility, Action<MapPreview> parseMetadata = null)
|
||||
public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml, Action<MapPreview> parseMetadata = null)
|
||||
{
|
||||
var newData = innerData.Clone();
|
||||
newData.Status = status;
|
||||
@@ -478,7 +498,7 @@ namespace OpenRA
|
||||
// Map is for a different mod: update its information so it can be displayed
|
||||
// in the cross-mod server browser UI, but mark it as unavailable so it can't
|
||||
// be selected in a server for the current mod.
|
||||
if (!mapCompatibility.Contains(r.game_mod))
|
||||
if (!modData.Manifest.MapCompatibility.Contains(r.game_mod))
|
||||
newData.Status = MapStatus.Unavailable;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -553,12 +573,12 @@ namespace OpenRA
|
||||
mapInstallPackage.Update(mapFilename, fileStream.ToArray());
|
||||
Log.Write("debug", $"Downloaded map to '{mapFilename}'");
|
||||
|
||||
var package = mapInstallPackage.OpenPackage(mapFilename, modData.ModFiles);
|
||||
if (package == null)
|
||||
var p = mapInstallPackage.OpenPackage(mapFilename, modData.ModFiles);
|
||||
if (p == null)
|
||||
innerData.Status = MapStatus.DownloadError;
|
||||
else
|
||||
{
|
||||
UpdateFromMap(package, mapInstallPackage, MapClassification.User, null, GridType, null);
|
||||
UpdateFromMapWithoutOwningPackage(p, mapInstallPackage, MapClassification.User, GridType);
|
||||
Game.RunAfterTick(onSuccess);
|
||||
}
|
||||
}
|
||||
@@ -577,11 +597,6 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposePackage();
|
||||
}
|
||||
|
||||
public void DisposePackage()
|
||||
{
|
||||
if (package != null)
|
||||
{
|
||||
@@ -593,14 +608,15 @@ namespace OpenRA
|
||||
public void Delete()
|
||||
{
|
||||
Invalidate();
|
||||
(parentPackage as IReadWritePackage)?.Delete(Package.Name);
|
||||
(parentPackage as IReadWritePackage)?.Delete(Path);
|
||||
}
|
||||
|
||||
Stream IReadOnlyFileSystem.Open(string filename)
|
||||
{
|
||||
// Explicit package paths never refer to a map
|
||||
if (!filename.Contains('|') && Package.Contains(filename))
|
||||
return Package.GetStream(filename);
|
||||
LoadPackage();
|
||||
if (!filename.Contains('|') && package.Contains(filename))
|
||||
return package.GetStream(filename);
|
||||
|
||||
return modData.DefaultFileSystem.Open(filename);
|
||||
}
|
||||
@@ -616,7 +632,8 @@ namespace OpenRA
|
||||
// Explicit package paths never refer to a map
|
||||
if (!filename.Contains('|'))
|
||||
{
|
||||
s = Package.GetStream(filename);
|
||||
LoadPackage();
|
||||
s = package.GetStream(filename);
|
||||
if (s != null)
|
||||
return true;
|
||||
}
|
||||
@@ -627,7 +644,8 @@ namespace OpenRA
|
||||
bool IReadOnlyFileSystem.Exists(string filename)
|
||||
{
|
||||
// Explicit package paths never refer to a map
|
||||
if (!filename.Contains('|') && Package.Contains(filename))
|
||||
LoadPackage();
|
||||
if (!filename.Contains('|') && package.Contains(filename))
|
||||
return true;
|
||||
|
||||
return modData.DefaultFileSystem.Exists(filename);
|
||||
|
||||
@@ -148,11 +148,8 @@ namespace OpenRA
|
||||
if (MapCache[uid].Status != MapStatus.Available)
|
||||
throw new InvalidDataException($"Invalid map uid: {uid}");
|
||||
|
||||
Map map;
|
||||
using (new Support.PerfTimer("Map"))
|
||||
map = new Map(this, MapCache[uid].Package);
|
||||
|
||||
// Reinitialize all our assets
|
||||
var map = MapCache[uid].ToMap();
|
||||
InitializeLoaders(map);
|
||||
map.Sequences.LoadSprites();
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
if (map.Package?.Name != combinedPath)
|
||||
{
|
||||
// When creating a new map or when file paths don't match
|
||||
if (modData.MapCache.Any(m => m.Status == MapStatus.Available && m.PackageName == combinedPath))
|
||||
if (modData.MapCache.Any(m => m.Status == MapStatus.Available && m.Path == combinedPath))
|
||||
{
|
||||
ConfirmationDialogs.ButtonPrompt(modData,
|
||||
title: OverwriteMapFailedTitle,
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
.Select(p => new
|
||||
{
|
||||
Preview = p,
|
||||
Index = missionMapPaths.IndexOf(Path.GetFileName(p.PackageName))
|
||||
Index = missionMapPaths.IndexOf(Path.GetFileName(p.Path))
|
||||
})
|
||||
.Where(x => x.Index != -1)
|
||||
.OrderBy(x => x.Index)
|
||||
|
||||
Reference in New Issue
Block a user